| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- import { existsSync } from 'node:fs'
- import { readdir, readFile } from 'node:fs/promises'
- import { dirname, extname, join, resolve } from 'node:path'
- import { fileURLToPath } from 'node:url'
- const scriptDir = dirname(fileURLToPath(import.meta.url))
- const sourceRoot = resolve(scriptDir, '..')
- const coreDir = join(sourceRoot, 'packages', 'core')
- const coreSrcDir = join(coreDir, 'src')
- const requiredValueExports = [
- 'createRendererRegistry',
- 'coreBrowserRendererHandlers',
- 'createFileViewerCoreRendererRegistry',
- 'fileViewerCoreRendererDispatcher',
- 'fileViewerCoreRendererRegistry',
- 'fileViewerCoreRendererRegistryBridge',
- 'missingFileViewerCoreRendererHandlers',
- 'renderFileViewerArchive',
- 'renderFileViewerAudio',
- 'renderFileViewerCad',
- 'renderFileViewerCode',
- 'renderFileViewerDataAsset',
- 'renderFileViewerDrawing',
- 'renderFileViewerEda',
- 'renderFileViewerEmail',
- 'renderFileViewerEpub',
- 'renderFileViewerGeo',
- 'renderFileViewerImage',
- 'renderFileViewerMarkdown',
- 'renderFileViewerModel',
- 'renderFileViewerOfd',
- 'renderFileViewerOpenDocument',
- 'renderFileViewerPdf',
- 'parseEdaFile',
- 'renderFileViewerPptx',
- 'renderFileViewerSpreadsheet',
- 'renderFileViewerTypst',
- 'renderFileViewerUmd',
- 'renderFileViewerVideo',
- 'renderFileViewerWordDoc',
- 'renderFileViewerWordDocx',
- 'createFileViewerRendererDispatcher',
- 'createFileRenderHandlerRegistry',
- 'normalizeSource',
- 'resolveFileViewerRemoteSourcePlan',
- 'resolveFileViewerSourceFilename',
- 'readFileViewerBuffer',
- 'readFileViewerDataUrl',
- 'readFileViewerText',
- 'sanitizeFileViewerOptions',
- 'serializeFileViewerOptions',
- 'parseFileViewerOptions',
- 'normalizeFileViewerTheme',
- 'resolveFileViewerPresentationState',
- 'cancelFileViewerPreviewRequest',
- 'DEFAULT_FILE_VIEWER_SOURCE_FILENAME',
- 'DEFAULT_FILE_VIEWER_STREAMING_PDF_FILENAME',
- 'createFileViewerEmptyPreviewState',
- 'createFileViewerPreviewRequestResetState',
- 'createFileViewerPreviewStateTarget',
- 'applyFileViewerPreviewRequestResetState',
- 'applyFileViewerEmptyPreviewState',
- 'resolveFileViewerFileRefSourcePlan',
- 'applyFileViewerPreviewFilenameState',
- 'createFileViewerReadPreviewState',
- 'applyFileViewerReadPreviewState',
- 'applyFileViewerPreviewSourceUrlState',
- 'applyFileViewerRenderReadinessState',
- 'createFileViewerRenderCompleteState',
- 'commitFileViewerEmptyPreviewResetState',
- 'commitFileViewerLoadStartState',
- 'commitFileViewerPreviewRequestStartState',
- 'commitFileViewerRenderCompleteState',
- 'commitFileViewerRemoteDownloadState',
- 'createFileViewerLoadStartState',
- 'createFileViewerRequestScope',
- 'createFileViewerSourceLoadingActionHandlers',
- 'createFileViewerStreamingPdfPlaceholderFile',
- 'finalizeFileViewerPreviewLoadState',
- 'hasFileViewerPreviewSource',
- 'normalizeFileViewerSourceUrl',
- 'resolveFileViewerLoadStartMessage',
- 'DEFAULT_FILE_VIEWER_PREVIEW_LOAD_ERROR_LOGGER',
- 'FILE_VIEWER_PREVIEW_LOAD_ERROR_PREFIXES',
- 'FILE_VIEWER_REMOTE_MISSING_DATA_ERROR_MESSAGE',
- 'resolveFileViewerMissingRemoteDataErrorMessage',
- 'resolveFileViewerPreviewLoadErrorMessage',
- 'resolveFileViewerPageHref',
- 'resolveFileViewerPreviewRequestReason',
- 'reportFileViewerMissingRemoteData',
- 'reportFileViewerPreviewLoadError',
- 'runFileViewerLocalFilePreview',
- 'runFileViewerPreviewComponentUnmount',
- 'runFileViewerPreviewRequest',
- 'runFileViewerPreviewSourceChange',
- 'runFileViewerRemoteFilePreview',
- 'runFileViewerReadAndRenderFile',
- 'runFileViewerStreamingPdfPreview',
- 'DEFAULT_FILE_VIEWER_DOWNLOAD_FILENAME',
- 'DEFAULT_FILE_VIEWER_EXPORT_FILENAME',
- 'DEFAULT_FILE_VIEWER_PREVIEW_TITLE',
- 'DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL',
- 'DEFAULT_FILE_VIEWER_DOCX_WORKER_JSZIP_PATH',
- 'DEFAULT_FILE_VIEWER_DOCX_WORKER_PATH',
- 'DEFAULT_FILE_VIEWER_SPREADSHEET_WORKER_PATH',
- 'resolveFileViewerDataSqlWasmUrl',
- 'resolveFileViewerDocxWorkerJsZipUrl',
- 'resolveFileViewerDocxWorkerUrl',
- 'resolveFileViewerSpreadsheetWorkerUrl',
- 'createFileViewerOriginalSourceState',
- 'createFileViewerOriginalSourceStateFromNormalizedSource',
- 'resolveFileViewerDisplayFilename',
- 'resolveFileViewerOperationFilename',
- 'resolveFileViewerOperationAvailability',
- 'cloneFileViewerOperationAvailability',
- 'resolveVisibleFileViewerToolbar',
- 'hasVisibleFileViewerToolbarActions',
- 'isFileViewerZoomButtonDisabled',
- 'resolveFileViewerToolbarState',
- 'createFileViewerPublicApi',
- 'buildFileViewerLifecycleContextFromNormalizedSource',
- 'buildFileViewerOperationContextFromLifecycleState',
- 'createFileViewerLifecycleActions',
- 'createFileViewerLifecycleStateController',
- 'createFileViewerToolbarActions',
- 'createFileViewerToolbarControllerActionHandlers',
- 'createFileViewerToolbarZoomSyncSnapshot',
- 'dispatchFileViewerLifecycleEvent',
- 'dispatchFileViewerOperationContextEvent',
- 'dispatchFileViewerOperationAvailabilityChange',
- 'dispatchFileViewerZoomChange',
- 'emitFileViewerComponentLifecycleEvent',
- 'FILE_VIEWER_BEFORE_OPERATION_ERROR_PREFIX',
- 'FILE_VIEWER_LIFECYCLE_HOOK_ERROR_MESSAGE_PREFIX',
- 'DEFAULT_FILE_VIEWER_LIFECYCLE_HOOK_ERROR_LOGGER',
- 'DEFAULT_FILE_VIEWER_OPERATION_ERROR_LOGGER',
- 'resolveFileViewerBeforeOperationErrorMessage',
- 'resolveFileViewerLifecycleHookErrorMessage',
- 'reportFileViewerLifecycleHookError',
- 'reportFileViewerOperationError',
- 'runFileViewerActiveUnloadComplete',
- 'runFileViewerActiveUnloadStart',
- 'runFileViewerBeforeOperation',
- 'runFileViewerLifecycleHook',
- 'createFileViewerLifecycleFacade',
- 'runFileViewerToolbarAvailabilitySync',
- 'runFileViewerToolbarZoomSync',
- 'resolveFileViewerLifecycleFallbackSource',
- 'applyFileViewerLoadingState',
- 'syncFileViewerLoadingControllerState',
- 'runFileViewerLoadingControllerAction',
- 'runFileViewerLoadingExtensionSync',
- 'createFileViewerLoadingControllerActionHandlers',
- 'FILE_VIEWER_OPERATION_ACTION_ERROR_PREFIXES',
- 'resolveFileViewerOperationActionErrorMessage',
- 'createFileViewerRenderReadinessTarget',
- 'createFileViewerRenderSurfaceState',
- 'createFileViewerRenderSurfaceStateTarget',
- 'disposeActiveFileViewerRendererSession',
- 'disposeFileViewerRendererSession',
- 'DEFAULT_FILE_VIEWER_RENDER_SESSION_DISPOSE_ERROR_LOGGER',
- 'FILE_VIEWER_RENDER_SESSION_DISPOSE_ERROR_MESSAGE',
- 'reportFileViewerRenderSessionDisposeError',
- 'resolveFileViewerRenderSessionDisposeErrorMessage',
- 'applyFileViewerSearchState',
- 'cloneFileViewerSearchState',
- 'createFileViewerSearchChangeState',
- 'dispatchFileViewerSearchChange',
- 'dispatchFileViewerLocationChange',
- 'applyFileViewerZoomState',
- 'createFileViewerZoomChangeState',
- 'createFileViewerZoomChangeEmitter',
- 'buildFileViewerWatermarkStyle',
- 'resolveFileViewerWatermarkPresentationState',
- 'createFileViewerWorkerController',
- 'resolveFileViewerRendererAssets',
- 'DEFAULT_RENDERER_DEFINITIONS',
- 'DEFAULT_SUPPORTED_EXTENSIONS'
- ]
- const requiredTypeExports = [
- 'CancelFileViewerPreviewRequestInput',
- 'FileViewerOptions',
- 'FileViewerReadResult',
- 'FileViewerSource',
- 'FileViewerLocationLike',
- 'FileViewerFileRefSourcePlan',
- 'FileViewerFileOperationType',
- 'FileViewerOperationActionErrorContext',
- 'FileViewerOperationActionErrorFormatter',
- 'FileViewerOperationActionErrorPrefixes',
- 'FileViewerLoadingControllerActionHandlers',
- 'FileViewerLifecycleFacade',
- 'FileViewerLocalFilePreviewState',
- 'ResolveFileViewerFileRefSourcePlanInput',
- 'FileViewerOriginalSourceState',
- 'FileViewerInstance',
- 'FileViewerLifecycleContext',
- 'FileViewerLifecycleHooks',
- 'FileViewerLifecycleActions',
- 'FileViewerLifecycleComponentEmit',
- 'FileViewerLifecycleHookErrorLogger',
- 'FileViewerOperationErrorLogger',
- 'FileViewerErrorMessageFormatter',
- 'FileViewerToolbarActions',
- 'FileViewerToolbarControllerActionHandlers',
- 'FileViewerToolbarZoomSyncSnapshot',
- 'FileViewerZoomButtonAction',
- 'FileViewerOperationContext',
- 'FileViewerActiveUnloadState',
- 'FileViewerBeforeOperation',
- 'FileViewerPreviewLoadErrorKind',
- 'FileViewerPreviewLoadErrorPrefixes',
- 'FileViewerPreviewLoadErrorLogger',
- 'FileViewerPreviewRequestResetState',
- 'FileViewerPreviewRequestRunState',
- 'FileViewerRequestController',
- 'FileViewerRequestScope',
- 'FileViewerSourceLoadingActionHandlers',
- 'FileViewerReadAndRenderFileState',
- 'FileViewerReadPreviewState',
- 'FileViewerRenderReadinessState',
- 'FileViewerRenderCompleteState',
- 'FileViewerRemoteDownloadState',
- 'FileViewerRemoteFileDownloadInput',
- 'FileViewerRemoteFilePreviewErrorKind',
- 'FileViewerRemoteFilePreviewState',
- 'FileViewerStreamingPdfPreviewState',
- 'FileViewerLoadStartState',
- 'CreateFileViewerLifecycleActionsInput',
- 'CreateFileViewerPublicApiInput',
- 'CreateFileViewerToolbarActionsInput',
- 'CreateFileViewerToolbarControllerActionHandlersInput',
- 'ResolveFileViewerBeforeOperationErrorMessageInput',
- 'ResolveFileViewerLifecycleHookErrorMessageInput',
- 'ReportFileViewerLifecycleHookErrorInput',
- 'ReportFileViewerOperationErrorInput',
- 'ResolveFileViewerMissingRemoteDataErrorMessageInput',
- 'ResolveFileViewerPreviewLoadErrorMessageInput',
- 'ReportFileViewerMissingRemoteDataInput',
- 'ReportFileViewerPreviewLoadErrorInput',
- 'FileViewerDocumentFeatureActionOptions',
- 'CreateFileViewerDocumentFeatureActionsInput',
- 'CreateFileViewerDocumentFeatureControllerActionHandlersInput',
- 'FileViewerDocumentFeatureControllerActionHandlers',
- 'FileViewerPreviewComponentUnmountState',
- 'FileViewerRenderSurfaceActionHandlers',
- 'FileViewerRenderSurfaceState',
- 'FileViewerRenderSurfaceMountContext',
- 'FileViewerRenderSurfaceClearState',
- 'CreateFileViewerRenderSurfaceActionHandlersInput',
- 'CreateFileViewerRenderReadinessTargetInput',
- 'CreateFileViewerRenderSurfaceStateTargetInput',
- 'FinalizeFileViewerPreviewLoadStateInput',
- 'RunFileViewerActiveUnloadCompleteInput',
- 'RunFileViewerActiveUnloadStartInput',
- 'RunFileViewerToolbarAvailabilitySyncInput',
- 'RunFileViewerToolbarZoomSyncInput',
- 'RunFileViewerRenderSurfaceClearInput',
- 'RunFileViewerRenderSurfaceMountInput',
- 'RunFileViewerLocalFilePreviewInput',
- 'RunFileViewerPreviewComponentUnmountInput',
- 'RunFileViewerPreviewRequestInput',
- 'RunFileViewerPreviewSourceChangeInput',
- 'RunFileViewerRemoteFilePreviewInput',
- 'RunFileViewerReadAndRenderFileInput',
- 'RunFileViewerStreamingPdfPreviewInput',
- 'CreateFileViewerSourceLoadingActionHandlersInput',
- 'CreateFileViewerReadPreviewStateInput',
- 'CreateFileViewerRenderCompleteStateInput',
- 'CommitFileViewerEmptyPreviewResetStateInput',
- 'CommitFileViewerLoadStartStateInput',
- 'CommitFileViewerPreviewRequestStartStateInput',
- 'CommitFileViewerRenderCompleteStateInput',
- 'CommitFileViewerRemoteDownloadStateInput',
- 'CreateFileViewerLoadStartStateInput',
- 'CreateFileViewerPreviewStateTargetInput',
- 'FileViewerMutableAccessor',
- 'MutableFileViewerPreviewRequestState',
- 'MutableFileViewerPreviewState',
- 'MutableFileViewerPreviewFilenameState',
- 'MutableFileViewerRenderReadinessState',
- 'MutableFileViewerRenderSurfaceState',
- 'ResetFileViewerRenderSurfaceInput',
- 'ReportFileViewerRenderSessionDisposeErrorInput',
- 'FileViewerRenderSessionDisposeErrorLogger',
- 'ResolveFileViewerRenderSessionDisposeErrorMessageInput',
- 'MutableFileViewerReadPreviewState',
- 'MutableFileViewerPreviewSourceUrlState',
- 'FileViewerLifecycleStateController',
- 'FileViewerOperationAvailability',
- 'FileViewerToolbarState',
- 'ResolveFileViewerToolbarStateInput',
- 'FileViewerSearchState',
- 'FileViewerZoomState',
- 'FileViewerDocumentAnchor',
- 'FileViewerDocumentChunk',
- 'ResolveFileViewerOperationFilenameInput',
- 'ResolveFileViewerOperationActionErrorMessageInput',
- 'DisposeFileViewerRendererSessionOptions',
- 'RendererPlugin',
- 'RendererSession',
- 'RendererRegistry',
- 'RendererLoadContext',
- 'ViewerCapabilityState',
- 'ViewerLifecycleContext',
- 'ViewerOperationContext',
- 'FileRenderContext',
- 'FileRenderExportAdapter',
- 'FileViewerSerializableOptions',
- 'FileViewerRenderedInstance',
- 'FileViewerComponentProps',
- 'FileViewerComponentEmits',
- 'FileViewerComponentEventMap',
- 'FileViewerPublicApi',
- 'BuildFileViewerLifecycleContextFromNormalizedSourceInput',
- 'BuildFileViewerOperationContextFromLifecycleStateInput',
- 'BuildFileViewerLifecycleFacadeLoadStartStateInput',
- 'BuildFileViewerLifecycleFacadeRenderCompleteStateInput',
- 'CreateFileViewerLifecycleFacadeInput',
- 'DispatchFileViewerLifecycleEventInput',
- 'DispatchFileViewerOperationContextEventInput',
- 'DispatchFileViewerOperationAvailabilityChangeInput',
- 'DispatchFileViewerZoomChangeInput',
- 'DispatchFileViewerSearchChangeInput',
- 'DispatchFileViewerLocationChangeInput',
- 'FileViewerPresentationState',
- 'ResolveFileViewerPresentationStateInput',
- 'FileViewerWorkerController',
- 'FileViewerLoadingState',
- 'MutableFileViewerLoadingState',
- 'RunFileViewerLoadingExtensionSyncInput',
- 'MutableFileViewerSearchState',
- 'MutableFileViewerZoomState',
- 'FileViewerWatermarkPresentationState',
- 'FileViewerWatermarkStyle',
- ]
- const requiredInstanceMethods = [
- 'load',
- 'destroy',
- 'updateOptions',
- 'getCapabilities',
- 'getRenderer',
- 'getSource',
- 'download',
- 'exportHtml',
- 'print',
- 'zoomIn',
- 'zoomOut',
- 'resetZoom',
- 'getZoomState',
- 'search',
- 'nextSearchResult',
- 'previousSearchResult',
- 'clearSearch',
- 'getSearchState',
- 'collectDocumentAnchors',
- 'getCurrentDocumentAnchor',
- 'scrollToDocumentAnchor',
- 'scrollToLine',
- 'getDocumentTextChunks'
- ]
- const requiredHeadlessValueExports = [
- 'DEFAULT_RENDERER_DEFINITIONS',
- 'DEFAULT_SUPPORTED_EXTENSIONS',
- 'normalizeSource',
- 'readFileViewerBuffer',
- 'sanitizeFileViewerOptions',
- 'serializeFileViewerOptions',
- 'parseFileViewerOptions',
- 'resolveFileViewerOperationAvailability',
- 'buildFileViewerDocumentTextChunks',
- 'createRendererRegistry',
- 'createFileViewerRendererDispatcher',
- 'createFileRenderHandlerRegistry'
- ]
- const requiredHeadlessTypeExports = [
- 'FileViewerOptions',
- 'FileViewerSource',
- 'RendererDefinition',
- 'RendererRegistry',
- 'RendererSession',
- 'FileViewerOperationAvailability',
- 'FileViewerLifecycleContext',
- 'FileViewerSearchState',
- 'FileViewerZoomState'
- ]
- const forbiddenHeadlessApiTokens = [
- 'createViewer',
- 'coreBrowserRendererHandlers',
- 'fileViewerCoreRendererDispatcher',
- 'renderFileViewer',
- 'createFileViewerDomSearchController',
- 'createFileViewerZoomController',
- 'collectFileViewerDocumentAnchors',
- 'buildFileViewerRenderedHtmlDocument',
- 'triggerFileViewerBlobDownload',
- 'waitForFileViewerPrintWindowReady'
- ]
- const requiredBrowserApiTokens = [
- 'createViewer',
- 'coreBrowserRendererHandlers',
- 'renderFileViewerPdf',
- 'renderFileViewerWordDocx',
- 'renderFileViewerSpreadsheet',
- 'createFileViewerDomSearchController',
- 'createFileViewerZoomController',
- 'buildFileViewerRenderedHtmlDocument'
- ]
- const forbiddenCoreSourceExtensions = new Set(['.jsx', '.tsx', '.vue', '.svelte'])
- const allowedCoreDevDependencies = new Set([
- 'typescript',
- '@types/three',
- '@types/pako'
- ])
- const allowedCoreRendererDependencies = new Set([])
- const forbiddenCoreDependencyPrefixes = [
- 'vue',
- '@vue/',
- '@vitejs/plugin-vue',
- 'vue-tsc',
- '@lucide/vue',
- 'react',
- 'react-dom',
- 'svelte',
- '@file-viewer/',
- '@flyfish-group/',
- 'file-viewer3'
- ]
- const forbiddenCoreApiTokens = [
- 'mountViewer',
- 'ViewerMountOptions',
- 'ViewerController',
- 'ViewerControllerHandle',
- 'createViewerControllerHandle',
- 'createFileViewerNativeController',
- 'resolveFileViewerNativeLoadSource',
- 'FileViewerNativeController',
- 'FileViewerNativeFetchFile',
- 'FileViewerNativeFetchInput',
- 'FileViewerNativeSource'
- ]
- function isForbiddenCoreDependency(name) {
- if (allowedCoreRendererDependencies.has(name)) {
- return false
- }
- return forbiddenCoreDependencyPrefixes.some(prefix =>
- prefix.endsWith('/')
- ? name.startsWith(prefix)
- : name === prefix
- )
- }
- const forbiddenCoreFilenames = new Set([
- 'nativeController.ts',
- 'frame.ts'
- ])
- const forbiddenCoreBrowserGlobalPatterns = [
- /document\.baseURI/,
- /location\.href/,
- /typeof\s+location/,
- /typeof\s+document/,
- ]
- function assert(condition, message) {
- if (!condition) {
- throw new Error(message)
- }
- }
- async function readJson(path) {
- return JSON.parse(await readFile(path, 'utf8'))
- }
- function hasToken(source, token) {
- return new RegExp(`\\b${token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(source)
- }
- async function readAllSourceFiles(dir) {
- const entries = await readdir(dir, { withFileTypes: true })
- const files = []
- for (const entry of entries) {
- const path = join(dir, entry.name)
- if (entry.isDirectory()) {
- files.push(...await readAllSourceFiles(path))
- continue
- }
- if (entry.isFile()) {
- files.push(path)
- }
- }
- return files
- }
- function assertCorePackageMetadata(packageJson) {
- assert(packageJson.name === '@file-viewer/core', 'Core package name must be @file-viewer/core')
- assert(packageJson.private !== true, 'Core package must be publishable')
- assert(packageJson.type === 'module', 'Core package must publish as ESM')
- assert(packageJson.main === './dist/index.js', 'Core package main entry drifted')
- assert(packageJson.module === './dist/index.js', 'Core package module entry drifted')
- assert(packageJson.types === './dist/index.d.ts', 'Core package types entry drifted')
- assert(packageJson.exports?.['.']?.import === './dist/index.js', 'Core package exports["."].import drifted')
- assert(packageJson.exports?.['.']?.types === './dist/index.d.ts', 'Core package exports["."].types drifted')
- assert(packageJson.exports?.['./assets']?.import === './dist/assets.js', 'Core package exports["./assets"].import drifted')
- assert(packageJson.exports?.['./headless']?.import === './dist/headless.js', 'Core package exports["./headless"].import drifted')
- assert(packageJson.exports?.['./headless']?.types === './dist/headless.d.ts', 'Core package exports["./headless"].types drifted')
- assert(packageJson.exports?.['./browser']?.import === './dist/browser.js', 'Core package exports["./browser"].import drifted')
- assert(packageJson.exports?.['./browser']?.types === './dist/browser.d.ts', 'Core package exports["./browser"].types drifted')
- assert(!packageJson.peerDependencies, 'Core package must not have peer dependencies')
- assert(!packageJson.optionalDependencies, 'Core package must not have optional dependencies')
- for (const dependencyName of Object.keys(packageJson.dependencies || {})) {
- assert(
- !isForbiddenCoreDependency(dependencyName),
- `Core package dependencies must stay framework-neutral; unexpected ${dependencyName}`
- )
- }
- for (const dependencyName of Object.keys(packageJson.devDependencies || {})) {
- assert(
- allowedCoreDevDependencies.has(dependencyName),
- `Core package devDependencies must stay tooling-only; unexpected ${dependencyName}`
- )
- }
- }
- function assertCoreTsConfig(tsconfig) {
- assert(tsconfig.compilerOptions?.declaration === true, 'Core tsconfig must emit declarations')
- assert(tsconfig.compilerOptions?.strict === true, 'Core tsconfig must stay strict')
- assert(tsconfig.compilerOptions?.rootDir === 'src', 'Core tsconfig rootDir must be src')
- assert(tsconfig.compilerOptions?.outDir === 'dist', 'Core tsconfig outDir must be dist')
- assert(
- (tsconfig.include || []).includes('src/**/*.ts'),
- 'Core tsconfig must include src/**/*.ts'
- )
- assert(
- (tsconfig.include || []).includes('src/**/*.d.ts'),
- 'Core tsconfig must include src/**/*.d.ts for renderer ambient module declarations'
- )
- }
- function assertCoreEntrypoint(indexSource) {
- for (const exportName of requiredValueExports) {
- assert(hasToken(indexSource, exportName), `Core entrypoint must export ${exportName}`)
- }
- for (const typeName of requiredTypeExports) {
- assert(hasToken(indexSource, typeName), `Core entrypoint must export type ${typeName}`)
- }
- }
- function assertCoreHeadlessEntrypoint(headlessSource) {
- for (const exportName of requiredHeadlessValueExports) {
- assert(hasToken(headlessSource, exportName), `Core headless entrypoint must export ${exportName}`)
- }
- for (const typeName of requiredHeadlessTypeExports) {
- assert(hasToken(headlessSource, typeName), `Core headless entrypoint must export type ${typeName}`)
- }
- for (const token of forbiddenHeadlessApiTokens) {
- assert(
- !hasToken(headlessSource, token),
- `Core headless entrypoint must not expose browser renderer token ${token}`
- )
- }
- }
- function assertCoreBrowserEntrypoint(browserSource, indexSource) {
- assert(
- /export\s+\*\s+from\s+['"]\.\/index['"]/.test(browserSource),
- 'Core browser entrypoint must explicitly re-export the browser-capable root API'
- )
- for (const token of requiredBrowserApiTokens) {
- assert(hasToken(indexSource, token), `Core browser entrypoint must expose ${token} through the root API`)
- }
- }
- function assertCoreInstanceContract(typesSource) {
- const match = /export\s+interface\s+FileViewerInstance\s*{([\s\S]*?)\n}/.exec(typesSource)
- assert(match, 'Core types must declare FileViewerInstance')
- const instanceContract = match[1]
- for (const methodName of requiredInstanceMethods) {
- assert(
- new RegExp(`\\b${methodName}\\s*\\(`).test(instanceContract),
- `FileViewerInstance must expose ${methodName}()`
- )
- }
- }
- function collectBareImportSpecifiers(source) {
- const imports = new Set()
- const patterns = [
- /\bimport\s+(?:type\s+)?(?:[^'"]+\s+from\s+)?['"]([^'"]+)['"]/g,
- /\bexport\s+(?:type\s+)?(?:\*|\{[^}]*})\s+from\s+['"]([^'"]+)['"]/g,
- /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g
- ]
- for (const pattern of patterns) {
- let match
- while ((match = pattern.exec(source))) {
- const specifier = match[1]
- if (
- !specifier.startsWith('.') &&
- !specifier.startsWith('/') &&
- !specifier.startsWith('node:')
- ) {
- imports.add(specifier)
- }
- }
- }
- return [...imports]
- }
- function packageNameFromSpecifier(specifier) {
- if (specifier.startsWith('@')) {
- return specifier.split('/').slice(0, 2).join('/')
- }
- return specifier.split('/')[0]
- }
- async function assertCoreSourceBoundary(files) {
- for (const file of files) {
- const extension = extname(file)
- const relativePath = file.slice(coreDir.length + 1)
- assert(
- !forbiddenCoreFilenames.has(relativePath),
- `Core source must not include wrapper or iframe orchestration file ${relativePath}`
- )
- assert(
- !forbiddenCoreSourceExtensions.has(extension),
- `Core source must remain pure TypeScript; unexpected ${relativePath}`
- )
- if (extension !== '.ts') {
- continue
- }
- const source = await readFile(file, 'utf8')
- for (const token of forbiddenCoreApiTokens) {
- assert(
- !hasToken(source, token),
- `Core source must not expose wrapper/browser mount API token ${token}: ${relativePath}`
- )
- }
- for (const pattern of forbiddenCoreBrowserGlobalPatterns) {
- assert(
- !pattern.test(source),
- `Core source must not read browser document/location globals: ${relativePath}`
- )
- }
- const declaredDependencies = new Set(Object.keys(packageJson.dependencies || {}))
- for (const specifier of collectBareImportSpecifiers(source)) {
- const packageName = packageNameFromSpecifier(specifier)
- assert(
- !isForbiddenCoreDependency(packageName),
- `Core source must not import framework or wrapper package ${specifier}: ${relativePath}`
- )
- assert(
- declaredDependencies.has(packageName),
- `Core source imports ${specifier} but ${packageName} is not declared in core dependencies: ${relativePath}`
- )
- }
- }
- }
- assert(existsSync(coreDir), 'Missing packages/core')
- assert(existsSync(coreSrcDir), 'Missing packages/core/src')
- const packageJson = await readJson(join(coreDir, 'package.json'))
- const tsconfig = await readJson(join(coreDir, 'tsconfig.json'))
- const indexSource = await readFile(join(coreSrcDir, 'index.ts'), 'utf8')
- const headlessSource = await readFile(join(coreSrcDir, 'headless.ts'), 'utf8')
- const browserSource = await readFile(join(coreSrcDir, 'browser.ts'), 'utf8')
- const typesSource = await readFile(join(coreSrcDir, 'contracts', 'types.ts'), 'utf8')
- const sourceFiles = await readAllSourceFiles(coreSrcDir)
- assertCorePackageMetadata(packageJson)
- assertCoreTsConfig(tsconfig)
- assertCoreEntrypoint(indexSource)
- assertCoreHeadlessEntrypoint(headlessSource)
- assertCoreBrowserEntrypoint(browserSource, indexSource)
- assertCoreInstanceContract(typesSource)
- await assertCoreSourceBoundary(sourceFiles)
- console.log(
- `[core-api] Verified ${requiredValueExports.length} root value exports, ${requiredTypeExports.length} root type exports, ${requiredHeadlessValueExports.length} headless value exports, ${requiredBrowserApiTokens.length} browser API tokens, ${requiredInstanceMethods.length} instance methods, and ${sourceFiles.length} pure TS core files.`
- )
|