Files
self/scripts/check-license-headers.mjs
Seshanth.S 6dc79ea8c0 webviewsdk: standardize config params (#1946)
* Standardize config param handling across Android & iOS

- Add shared SdkConstants (loopback host, debug port, didit host, tour path, default URLs)
- Add shared QueryParamsBuilder replacing duplicated platform-specific builders
- Android: deserialize config/request via kotlinx.serialization instead of org.json
- Android: add belt-and-suspenders debug guard (isDebugMode && isDebuggable)
- Android: remove redundant EXTRA_DEBUG_MODE and EXTRA_DEV_SERVER_URL intent extras
- iOS: replace local buildQueryParams/encodeParam with shared QueryParamsBuilder
- All default URLs now reference SdkConstants instead of hardcoded strings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Import KMP SdkConstants in Swift WebViewProviderImpl

- Replace hardcoded constants with SdkConstants.shared.* from KMP framework
- Replace "/tunnel/tour/1" with SdkConstants.shared.BUNDLED_TOUR_PATH
- Add SelfSdk as local package dependency in self-sdk-swift Package.swift

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Internalize CryptoProvider — remove from public SDK interface

CryptoProvider is never called at runtime (WebView uses Web Crypto API
directly). Make the interface, its Android implementation, and the
registry field internal so consumers no longer need to provide or
register a crypto implementation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix ci

* fix: temporarily use constants from self-sdk-swift

* lint

* fix: improve license header handling in check-license-headers script

* lint

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 15:22:56 +05:30

308 lines
8.2 KiB
JavaScript

#!/usr/bin/env node
// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc.
// SPDX-License-Identifier: BUSL-1.1
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
/**
* Script to check and fix license header formatting
* Ensures there's a newline after license headers
*/
import {
existsSync,
readdirSync,
statSync,
readFileSync,
writeFileSync,
} from 'fs';
import path from 'path';
// Legacy composite format (being phased out)
const LEGACY_HEADER =
'// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11';
// Canonical multi-line format (preferred)
const CANONICAL_HEADER_LINES = [
'// SPDX-FileCopyrightText: 2025-2026 Social Connect Labs, Inc.',
'// SPDX-License-Identifier: BUSL-1.1',
'// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.',
];
function findFiles(
dir,
extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.kt', '.swift'],
) {
const files = [];
function isNestedGitRepo(currentDir) {
return existsSync(path.join(currentDir, '.git'));
}
function traverse(currentDir) {
const items = readdirSync(currentDir);
for (const item of items) {
const fullPath = path.join(currentDir, item);
const stat = statSync(fullPath);
if (stat.isDirectory()) {
// Skip nested git repos/submodules. The parent repo should not rewrite or
// lint files owned by another repository.
if (isNestedGitRepo(fullPath)) {
continue;
}
// Skip node_modules, .git, and other common directories
if (
![
'node_modules',
'.git',
'dist',
'build',
'coverage',
'ios',
'android',
'.next',
'.turbo',
'.tamagui',
'DerivedData',
'Pods',
'.gradle',
'vendor',
'assets',
].includes(item)
) {
traverse(fullPath);
}
} else if (extensions.some(ext => item.endsWith(ext))) {
files.push(fullPath);
}
}
}
traverse(dir);
return files;
}
function findLicenseHeaderIndex(lines) {
let i = 0;
// Skip shebang if present
if (lines[i]?.startsWith('#!')) i++;
// Skip swift-tools-version (must be first line in Package.swift)
if (lines[i]?.startsWith('// swift-tools-version')) i++;
// Skip leading blank lines
while (i < lines.length && lines[i].trim() === '') i++;
const currentLine = lines[i];
// Check for legacy composite format
if (currentLine === LEGACY_HEADER) {
return { index: i, type: 'legacy', valid: true, endIndex: i };
}
// Check for canonical multi-line format (current or previous year)
const isCurrentHeader = currentLine === CANONICAL_HEADER_LINES[0];
const isPreviousYearHeader =
currentLine === '// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.';
if (isCurrentHeader || isPreviousYearHeader) {
const hasAllLines =
lines[i + 1] === CANONICAL_HEADER_LINES[1] &&
lines[i + 2] === CANONICAL_HEADER_LINES[2];
return {
index: i,
type: 'canonical',
valid: hasAllLines,
needsYearUpdate: isPreviousYearHeader && hasAllLines,
endIndex: hasAllLines ? i + 2 : i,
};
}
return { index: -1, type: 'none', valid: false };
}
function shouldRequireHeader(filePath, projectRoot) {
const relativePath = path.relative(projectRoot, filePath);
return (
relativePath.startsWith('app/') ||
relativePath.startsWith('packages/mobile-sdk-alpha/') ||
relativePath.startsWith('packages/kmp-sdk-test-app/') ||
relativePath.startsWith('packages/kmp-sdk/')
);
}
function checkLicenseHeader(
filePath,
{ requireHeader = false, projectRoot = process.cwd() } = {},
) {
const content = readFileSync(filePath, 'utf8');
const lines = content.split('\n');
const headerInfo = findLicenseHeaderIndex(lines);
const shouldHaveHeader =
requireHeader || shouldRequireHeader(filePath, projectRoot);
if (headerInfo.index === -1) {
if (shouldHaveHeader) {
return {
file: filePath,
issue: 'Missing or incorrect license header',
fixed: false,
};
}
return null;
}
if (!headerInfo.valid) {
return {
file: filePath,
issue: 'Incomplete or malformed license header',
fixed: false,
};
}
if (headerInfo.needsYearUpdate) {
return {
file: filePath,
issue: 'Copyright year needs updating to 2025-2026',
fixed: false,
};
}
// Check if there's a newline after the license header
const headerEndIndex = headerInfo.endIndex;
if (lines[headerEndIndex + 1] !== '') {
return {
file: filePath,
issue: 'Missing newline after license header',
fixed: false,
};
}
return null;
}
function fixLicenseHeader(filePath) {
const content = readFileSync(filePath, 'utf8');
const lines = content.split('\n');
const headerInfo = findLicenseHeaderIndex(lines);
if (headerInfo.index === -1) {
// No header exists - add the canonical header
// Preserve shebang and swift-tools-version prefixes
let insertIndex = 0;
if (lines[insertIndex]?.startsWith('#!')) {
insertIndex += 1;
}
if (lines[insertIndex]?.startsWith('// swift-tools-version')) {
insertIndex += 1;
// Ensure blank line between tools-version and license header
if (lines[insertIndex]?.trim() !== '') {
lines.splice(insertIndex, 0, '');
}
insertIndex += 1;
}
const newLines = [
...lines.slice(0, insertIndex),
...CANONICAL_HEADER_LINES,
'', // Add newline after header
...lines.slice(insertIndex),
];
const fixedContent = newLines.join('\n');
writeFileSync(filePath, fixedContent, 'utf8');
return true;
}
if (headerInfo.valid) {
// Update copyright year if needed
if (headerInfo.needsYearUpdate) {
lines[headerInfo.index] = CANONICAL_HEADER_LINES[0];
const fixedContent = lines.join('\n');
writeFileSync(filePath, fixedContent, 'utf8');
return true;
}
const headerEndIndex = headerInfo.endIndex;
if (lines[headerEndIndex + 1] !== '') {
// Insert empty line after license header
lines.splice(headerEndIndex + 1, 0, '');
const fixedContent = lines.join('\n');
writeFileSync(filePath, fixedContent, 'utf8');
return true;
}
}
return false;
}
function main() {
const args = process.argv.slice(2);
const isFix = args.includes('--fix');
const isCheck = args.includes('--check') || !isFix;
const requireHeader = args.includes('--require');
// Get all directory arguments (non-flag arguments)
const dirArgs = args.filter(arg => !arg.startsWith('--'));
const projectRoots =
dirArgs.length > 0
? dirArgs.map(dir => path.resolve(dir))
: [process.cwd()];
// Collect files from all directories
const files = [];
for (const projectRoot of projectRoots) {
files.push(...findFiles(projectRoot));
}
const issues = [];
for (const file of files) {
const issue = checkLicenseHeader(file, {
requireHeader,
projectRoot: process.cwd(),
});
if (issue) {
issues.push(issue);
if (isFix) {
const fixed = fixLicenseHeader(file);
if (fixed) {
issue.fixed = true;
console.log(`✅ Fixed: ${file}`);
}
}
}
}
if (isCheck) {
// Show which directories require headers
const requiredDirs = [
'app/',
'packages/mobile-sdk-alpha/',
'packages/kmp-sdk-test-app/',
'packages/kmp-sdk/',
];
console.log(`📋 License headers required in: ${requiredDirs.join(', ')}`);
if (issues.length === 0) {
console.log('✅ All license headers are properly formatted');
} else {
console.log(
`❌ Found ${issues.length} files with license header issues:`,
);
for (const issue of issues) {
console.log(` - ${issue.file}: ${issue.issue}`);
}
console.log('\nRun with --fix to automatically fix these issues');
process.exit(1);
}
} else if (isFix) {
const fixedCount = issues.filter(issue => issue.fixed).length;
console.log(`\n✅ Fixed ${fixedCount} files`);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
main();
}