@intlayer/engine 9.5.11 → 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/installSkills/skills/bundle-optimization.md +123 -0
- package/dist/assets/installSkills/skills/cli.md +28 -1
- package/dist/assets/installSkills/skills/compat.md +1 -5
- package/dist/assets/installSkills/skills/content.md +8 -2
- package/dist/assets/installSkills/skills/dev-tools.md +8 -6
- package/dist/assets/installSkills/skills/next-js.md +2 -2
- package/dist/assets/installSkills/skills/usage.md +1 -1
- package/dist/cjs/build.cjs +5 -0
- package/dist/cjs/cli.cjs +3 -0
- package/dist/cjs/init/index.cjs +14 -14
- package/dist/cjs/init/index.cjs.map +1 -1
- package/dist/cjs/init/utils/backendPackages.cjs +15 -0
- package/dist/cjs/init/utils/backendPackages.cjs.map +1 -0
- package/dist/cjs/init/utils/index.cjs +2 -3
- package/dist/cjs/init/utils/packageManager.cjs +1 -1
- package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
- package/dist/cjs/installMCP/installMCP.cjs +35 -25
- package/dist/cjs/installMCP/installMCP.cjs.map +1 -1
- package/dist/cjs/installSkills/index.cjs +28 -1
- package/dist/cjs/installSkills/index.cjs.map +1 -1
- package/dist/cjs/listGitFiles.cjs +3 -5
- package/dist/cjs/listGitFiles.cjs.map +1 -1
- package/dist/cjs/listProjects.cjs +1 -2
- package/dist/cjs/listProjects.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadDictionaries.cjs +9 -8
- package/dist/cjs/loadDictionaries/loadDictionaries.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs +3 -5
- package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs.map +1 -1
- package/dist/cjs/loadDictionaries/remoteDictionaryClient.cjs +18 -0
- package/dist/cjs/loadDictionaries/remoteDictionaryClient.cjs.map +1 -0
- package/dist/cjs/prepareIntlayerServer.cjs +132 -0
- package/dist/cjs/prepareIntlayerServer.cjs.map +1 -0
- package/dist/cjs/utils/getProcessChainCommands.cjs +117 -0
- package/dist/cjs/utils/getProcessChainCommands.cjs.map +1 -0
- package/dist/cjs/utils/index.cjs +2 -0
- package/dist/cjs/utils/startContentWatcher.cjs +99 -0
- package/dist/cjs/utils/startContentWatcher.cjs.map +1 -0
- package/dist/cjs/watcher.cjs +2 -2
- package/dist/cjs/watcher.cjs.map +1 -1
- package/dist/cjs/writeJsonIfChanged.cjs.map +1 -1
- package/dist/esm/build.mjs +2 -1
- package/dist/esm/cli.mjs +4 -4
- package/dist/esm/init/index.mjs +13 -13
- package/dist/esm/init/index.mjs.map +1 -1
- package/dist/esm/init/utils/backendPackages.mjs +14 -0
- package/dist/esm/init/utils/backendPackages.mjs.map +1 -0
- package/dist/esm/init/utils/index.mjs +2 -2
- package/dist/esm/init/utils/packageManager.mjs +1 -1
- package/dist/esm/init/utils/packageManager.mjs.map +1 -1
- package/dist/esm/installMCP/installMCP.mjs +35 -25
- package/dist/esm/installMCP/installMCP.mjs.map +1 -1
- package/dist/esm/installSkills/index.mjs +29 -3
- package/dist/esm/installSkills/index.mjs.map +1 -1
- package/dist/esm/listGitFiles.mjs +1 -1
- package/dist/esm/listGitFiles.mjs.map +1 -1
- package/dist/esm/listProjects.mjs +1 -1
- package/dist/esm/listProjects.mjs.map +1 -1
- package/dist/esm/loadDictionaries/loadDictionaries.mjs +9 -8
- package/dist/esm/loadDictionaries/loadDictionaries.mjs.map +1 -1
- package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs +2 -4
- package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs.map +1 -1
- package/dist/esm/loadDictionaries/remoteDictionaryClient.mjs +16 -0
- package/dist/esm/loadDictionaries/remoteDictionaryClient.mjs.map +1 -0
- package/dist/esm/prepareIntlayerServer.mjs +128 -0
- package/dist/esm/prepareIntlayerServer.mjs.map +1 -0
- package/dist/esm/utils/getProcessChainCommands.mjs +115 -0
- package/dist/esm/utils/getProcessChainCommands.mjs.map +1 -0
- package/dist/esm/utils/index.mjs +2 -1
- package/dist/esm/utils/runParallel/pidTree.mjs +3 -3
- package/dist/esm/utils/runParallel/pidTree.mjs.map +1 -1
- package/dist/esm/utils/runParallel/ps.mjs +2 -2
- package/dist/esm/utils/runParallel/ps.mjs.map +1 -1
- package/dist/esm/utils/runParallel/wmic.mjs +2 -2
- package/dist/esm/utils/runParallel/wmic.mjs.map +1 -1
- package/dist/esm/utils/startContentWatcher.mjs +98 -0
- package/dist/esm/utils/startContentWatcher.mjs.map +1 -0
- package/dist/esm/watcher.mjs +1 -1
- package/dist/esm/watcher.mjs.map +1 -1
- package/dist/esm/writeJsonIfChanged.mjs +1 -1
- package/dist/esm/writeJsonIfChanged.mjs.map +1 -1
- package/dist/types/build.d.ts +2 -1
- package/dist/types/cli.d.ts +4 -4
- package/dist/types/init/index.d.ts +1 -1
- package/dist/types/init/index.d.ts.map +1 -1
- package/dist/types/init/utils/backendPackages.d.ts +5 -0
- package/dist/types/init/utils/backendPackages.d.ts.map +1 -0
- package/dist/types/init/utils/index.d.ts +2 -2
- package/dist/types/init/utils/packageManager.d.ts +5 -0
- package/dist/types/init/utils/packageManager.d.ts.map +1 -1
- package/dist/types/installMCP/installMCP.d.ts +4 -0
- package/dist/types/installMCP/installMCP.d.ts.map +1 -1
- package/dist/types/installSkills/index.d.ts +7 -0
- package/dist/types/installSkills/index.d.ts.map +1 -1
- package/dist/types/loadDictionaries/loadRemoteDictionaries.d.ts.map +1 -1
- package/dist/types/loadDictionaries/remoteDictionaryClient.d.ts +14 -0
- package/dist/types/loadDictionaries/remoteDictionaryClient.d.ts.map +1 -0
- package/dist/types/prepareIntlayerServer.d.ts +67 -0
- package/dist/types/prepareIntlayerServer.d.ts.map +1 -0
- package/dist/types/utils/getProcessChainCommands.d.ts +35 -0
- package/dist/types/utils/getProcessChainCommands.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +2 -1
- package/dist/types/utils/startContentWatcher.d.ts +40 -0
- package/dist/types/utils/startContentWatcher.d.ts.map +1 -0
- package/dist/types/watcher.d.ts.map +1 -1
- package/package.json +9 -9
- package/dist/cjs/init/utils/devScript.cjs +0 -57
- package/dist/cjs/init/utils/devScript.cjs.map +0 -1
- package/dist/esm/init/utils/devScript.mjs +0 -55
- package/dist/esm/init/utils/devScript.mjs.map +0 -1
- package/dist/types/init/utils/devScript.d.ts +0 -48
- package/dist/types/init/utils/devScript.d.ts.map +0 -1
|
@@ -78,6 +78,14 @@ const loadDictionaries = async (contentDeclarationsPaths, configuration) => {
|
|
|
78
78
|
const { plugins } = configuration;
|
|
79
79
|
const loadDictionariesStartTime = Date.now();
|
|
80
80
|
getAppLogger(configuration)("Dictionaries:", { isVerbose: true });
|
|
81
|
+
const hasRemoteDictionaries = Boolean(configuration.editor.clientId && configuration.editor.clientSecret);
|
|
82
|
+
if (hasRemoteDictionaries) logger$1.setExpectRemote(true);
|
|
83
|
+
const remoteDictionariesPromise = hasRemoteDictionaries ? loadRemoteDictionaries(configuration, setLoadDictionariesStatus, {
|
|
84
|
+
onStartRemoteCheck: () => logger$1.startRemoteCheck(),
|
|
85
|
+
onStopRemoteCheck: () => logger$1.stopRemoteCheck(),
|
|
86
|
+
onError: (error) => logger$1.setRemoteError(error)
|
|
87
|
+
}).then((dictionaries) => filterInvalidDictionaries(dictionaries, configuration)).then((dictionaries) => formatDictionaries(dictionaries)) : Promise.resolve([]);
|
|
88
|
+
remoteDictionariesPromise.catch(() => void 0);
|
|
81
89
|
const pluginsWithLoadDictionaries = (await Promise.all(plugins ?? [])).filter((plugin) => plugin.loadDictionaries);
|
|
82
90
|
logger$1.setPluginNames(pluginsWithLoadDictionaries.map(({ name }) => name));
|
|
83
91
|
const loadPluginDictionariesPromise = pluginsWithLoadDictionaries.map(async (plugin) => {
|
|
@@ -100,14 +108,7 @@ const loadDictionaries = async (contentDeclarationsPaths, configuration) => {
|
|
|
100
108
|
status: "built"
|
|
101
109
|
}));
|
|
102
110
|
setLoadDictionariesStatus(localDictionariesStatus);
|
|
103
|
-
const
|
|
104
|
-
if (hasRemoteDictionaries) logger$1.setExpectRemote(true);
|
|
105
|
-
let remoteDictionaries = [];
|
|
106
|
-
if (hasRemoteDictionaries) remoteDictionaries = await loadRemoteDictionaries(configuration, setLoadDictionariesStatus, {
|
|
107
|
-
onStartRemoteCheck: () => logger$1.startRemoteCheck(),
|
|
108
|
-
onStopRemoteCheck: () => logger$1.stopRemoteCheck(),
|
|
109
|
-
onError: (e) => logger$1.setRemoteError(e)
|
|
110
|
-
}).then((dictionaries) => filterInvalidDictionaries(dictionaries, configuration)).then((dictionaries) => formatDictionaries(dictionaries));
|
|
111
|
+
const remoteDictionaries = await remoteDictionariesPromise;
|
|
111
112
|
const remoteDictionariesTime = Date.now();
|
|
112
113
|
logger$1.finish();
|
|
113
114
|
printSummary(configuration);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadDictionaries.mjs","names":["logger"],"sources":["../../../src/loadDictionaries/loadDictionaries.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeKey,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { filterInvalidDictionaries } from '../filterInvalidDictionaries';\nimport { formatDictionaries } from '../formatDictionary';\nimport { loadContentDeclarations } from './loadContentDeclaration';\nimport { loadRemoteDictionaries } from './loadRemoteDictionaries';\nimport { DictionariesLogger } from './log';\n\nexport type DictionariesStatus = {\n dictionaryKey: string;\n type: 'local' | 'remote';\n status:\n | 'pending' // Key found but not fetched yet\n | 'fetching' // If dictionary fetch is in progress\n | 'fetched' // If dictionary fetch succeeded\n | 'error' // If dictionary fetch failed\n | 'imported' // If dictionary already fetched and still up to date\n | 'found' // If dictionary key is found but promise is not resolved yet (ex: fetching distant content)\n | 'building' // If dictionary is being built\n | 'built'; // If dictionary is built;\n error?: string;\n};\n\nlet loadDictionariesStatus: DictionariesStatus[] = [];\nconst logger = new DictionariesLogger();\n\nconst setLoadDictionariesStatus = (statuses: DictionariesStatus[]) => {\n const updated: DictionariesStatus[] = [...loadDictionariesStatus];\n\n for (const incoming of statuses) {\n const index = updated.findIndex(\n (s) =>\n s.dictionaryKey === incoming.dictionaryKey && s.type === incoming.type\n );\n if (index >= 0) {\n updated[index] = incoming;\n } else {\n updated.push(incoming);\n }\n }\n\n loadDictionariesStatus = updated;\n logger.update(statuses);\n\n return updated;\n};\n\ntype StatusRecord = {\n local?: DictionariesStatus['status'];\n remote?: DictionariesStatus['status'];\n};\n\nconst iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'built':\n case 'imported':\n case 'fetched':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n};\n\nconst colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'built':\n case 'imported':\n case 'fetched':\n return ANSIColors.GREEN;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n};\n\nconst printSummary = (configuration: IntlayerConfig) => {\n if (configuration.log.mode !== 'verbose') return;\n\n const appLogger = getAppLogger(configuration);\n\n // Aggregate by dictionary key\n const byKey = new Map<string, StatusRecord>();\n for (const status of loadDictionariesStatus) {\n const rec = byKey.get(status.dictionaryKey) ?? {};\n if (status.type === 'local') rec.local = status.status;\n if (status.type === 'remote') rec.remote = status.status;\n byKey.set(status.dictionaryKey, rec);\n }\n\n const keys = Array.from(byKey.keys()).sort((a, b) => a.localeCompare(b));\n\n // Compute the max visible length of the local label to align distant labels\n let maxLocalLabelLen = 0;\n for (const key of keys) {\n const rec = byKey.get(key)!;\n if (rec.local) {\n const visibleLocal = `[local: ${iconFor(rec.local)} ${rec.local}]`;\n if (visibleLocal.length > maxLocalLabelLen) {\n maxLocalLabelLen = visibleLocal.length;\n }\n }\n }\n\n for (const key of keys) {\n const rec = byKey.get(key)!;\n const labels: string[] = [];\n\n if (rec.local) {\n const inner = colorize(\n `${iconFor(rec.local)} ${rec.local}`,\n colorFor(rec.local)\n );\n const coloredLocal =\n `${ANSIColors.GREY}[` +\n colorize('local: ', ANSIColors.GREY) +\n inner +\n `${ANSIColors.GREY}]${ANSIColors.RESET}`;\n\n // Pad to align distant label across rows\n const visibleLocal = `[local: ${iconFor(rec.local)} ${rec.local}]`;\n const pad = Math.max(0, maxLocalLabelLen - visibleLocal.length);\n labels.push(coloredLocal + ' '.repeat(pad));\n } else {\n // If no local label, insert spaces to keep distant aligned\n labels.push(' '.repeat(maxLocalLabelLen));\n }\n\n if (rec.remote) {\n const inner = colorize(\n `${iconFor(rec.remote)} ${rec.remote}`,\n colorFor(rec.remote)\n );\n labels.push(\n `${ANSIColors.GREY}[` +\n colorize('distant: ', ANSIColors.GREY) +\n inner +\n `${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n appLogger(\n ` - ${colon(colorizeKey(key), { colSize: keys })} ${labels.join(' ')}`\n );\n }\n};\n\nexport const loadDictionaries = async (\n contentDeclarationsPaths: string[] | string,\n configuration: IntlayerConfig\n): Promise<{\n localDictionaries: Dictionary[];\n remoteDictionaries: Dictionary[];\n pluginDictionaries: Dictionary[];\n time: {\n localDictionaries: number;\n remoteDictionaries: number;\n pluginDictionaries: number;\n };\n}> => {\n const { plugins } = configuration;\n const loadDictionariesStartTime = Date.now();\n const appLogger = getAppLogger(configuration);\n\n appLogger('Dictionaries:', { isVerbose: true });\n\n // Load additional dictionaries via plugins (e.g., ICU JSON ingestion)\n const resolvedPlugins = await Promise.all(plugins ?? []);\n\n const pluginsWithLoadDictionaries = resolvedPlugins.filter(\n (plugin) => plugin.loadDictionaries\n );\n\n logger.setPluginNames(pluginsWithLoadDictionaries.map(({ name }) => name));\n\n const loadPluginDictionariesPromise = pluginsWithLoadDictionaries.map(\n async (plugin) => {\n try {\n const res = await plugin.loadDictionaries?.({\n configuration,\n });\n\n return (res as Dictionary[] | undefined) ?? [];\n } catch (error) {\n logger.setPluginError(error as Error, plugin.name);\n\n return [];\n }\n }\n );\n\n const pluginDictionaries: Dictionary[] = await Promise.all(\n loadPluginDictionariesPromise as Promise<Dictionary[]>[]\n )\n .then((dictionaries) => dictionaries.flat())\n .then((dictionaries) =>\n filterInvalidDictionaries(dictionaries, configuration)\n )\n .then((dictionaries) => formatDictionaries(dictionaries));\n\n logger.setPluginTotal(pluginDictionaries.length);\n logger.setPluginDone(pluginDictionaries.length);\n\n const pluginDictionariesTime = Date.now();\n\n const files = Array.isArray(contentDeclarationsPaths)\n ? contentDeclarationsPaths\n : [contentDeclarationsPaths];\n\n const localDictionaries: Dictionary[] = await loadContentDeclarations(\n files,\n configuration,\n setLoadDictionariesStatus\n )\n .then((dictionaries) =>\n filterInvalidDictionaries(dictionaries, configuration)\n )\n .then((dictionaries) => formatDictionaries(dictionaries));\n\n const localDictionariesTime = Date.now();\n\n const localDictionariesStatus = localDictionaries.map(\n (dictionary) =>\n ({\n dictionaryKey: dictionary.key,\n type: 'local',\n status: 'built',\n }) as const\n );\n\n setLoadDictionariesStatus(localDictionariesStatus);\n\n const hasRemoteDictionaries = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n\n if (hasRemoteDictionaries) {\n // We expect to fetch remote dictionaries soon; suppress a transient local-only render\n logger.setExpectRemote(true);\n }\n\n let remoteDictionaries: Dictionary[] = [];\n\n if (hasRemoteDictionaries) {\n remoteDictionaries = await loadRemoteDictionaries(\n configuration,\n setLoadDictionariesStatus,\n {\n onStartRemoteCheck: () => logger.startRemoteCheck(),\n onStopRemoteCheck: () => logger.stopRemoteCheck(),\n onError: (e) => logger.setRemoteError(e),\n }\n )\n .then((dictionaries) =>\n filterInvalidDictionaries(dictionaries, configuration)\n )\n .then((dictionaries) => formatDictionaries(dictionaries));\n }\n\n const remoteDictionariesTime = Date.now();\n\n // Stop spinner and show final progress line(s)\n logger.finish();\n\n printSummary(configuration);\n\n return {\n localDictionaries,\n remoteDictionaries,\n pluginDictionaries,\n time: {\n localDictionaries: localDictionariesTime - pluginDictionariesTime,\n remoteDictionaries: remoteDictionariesTime - localDictionariesTime,\n pluginDictionaries: pluginDictionariesTime - loadDictionariesStartTime,\n },\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loadDictionaries.mjs","names":["logger"],"sources":["../../../src/loadDictionaries/loadDictionaries.ts"],"sourcesContent":["import * as ANSIColors from '@intlayer/config/colors';\nimport {\n colon,\n colorize,\n colorizeKey,\n getAppLogger,\n} from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { filterInvalidDictionaries } from '../filterInvalidDictionaries';\nimport { formatDictionaries } from '../formatDictionary';\nimport { loadContentDeclarations } from './loadContentDeclaration';\nimport { loadRemoteDictionaries } from './loadRemoteDictionaries';\nimport { DictionariesLogger } from './log';\n\nexport type DictionariesStatus = {\n dictionaryKey: string;\n type: 'local' | 'remote';\n status:\n | 'pending' // Key found but not fetched yet\n | 'fetching' // If dictionary fetch is in progress\n | 'fetched' // If dictionary fetch succeeded\n | 'error' // If dictionary fetch failed\n | 'imported' // If dictionary already fetched and still up to date\n | 'found' // If dictionary key is found but promise is not resolved yet (ex: fetching distant content)\n | 'building' // If dictionary is being built\n | 'built'; // If dictionary is built;\n error?: string;\n};\n\nlet loadDictionariesStatus: DictionariesStatus[] = [];\nconst logger = new DictionariesLogger();\n\nconst setLoadDictionariesStatus = (statuses: DictionariesStatus[]) => {\n const updated: DictionariesStatus[] = [...loadDictionariesStatus];\n\n for (const incoming of statuses) {\n const index = updated.findIndex(\n (s) =>\n s.dictionaryKey === incoming.dictionaryKey && s.type === incoming.type\n );\n if (index >= 0) {\n updated[index] = incoming;\n } else {\n updated.push(incoming);\n }\n }\n\n loadDictionariesStatus = updated;\n logger.update(statuses);\n\n return updated;\n};\n\ntype StatusRecord = {\n local?: DictionariesStatus['status'];\n remote?: DictionariesStatus['status'];\n};\n\nconst iconFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'built':\n case 'imported':\n case 'fetched':\n return '✔';\n case 'error':\n return '✖';\n default:\n return '⏲';\n }\n};\n\nconst colorFor = (status: DictionariesStatus['status']) => {\n switch (status) {\n case 'built':\n case 'imported':\n case 'fetched':\n return ANSIColors.GREEN;\n case 'error':\n return ANSIColors.RED;\n default:\n return ANSIColors.BLUE;\n }\n};\n\nconst printSummary = (configuration: IntlayerConfig) => {\n if (configuration.log.mode !== 'verbose') return;\n\n const appLogger = getAppLogger(configuration);\n\n // Aggregate by dictionary key\n const byKey = new Map<string, StatusRecord>();\n for (const status of loadDictionariesStatus) {\n const rec = byKey.get(status.dictionaryKey) ?? {};\n if (status.type === 'local') rec.local = status.status;\n if (status.type === 'remote') rec.remote = status.status;\n byKey.set(status.dictionaryKey, rec);\n }\n\n const keys = Array.from(byKey.keys()).sort((a, b) => a.localeCompare(b));\n\n // Compute the max visible length of the local label to align distant labels\n let maxLocalLabelLen = 0;\n for (const key of keys) {\n const rec = byKey.get(key)!;\n if (rec.local) {\n const visibleLocal = `[local: ${iconFor(rec.local)} ${rec.local}]`;\n if (visibleLocal.length > maxLocalLabelLen) {\n maxLocalLabelLen = visibleLocal.length;\n }\n }\n }\n\n for (const key of keys) {\n const rec = byKey.get(key)!;\n const labels: string[] = [];\n\n if (rec.local) {\n const inner = colorize(\n `${iconFor(rec.local)} ${rec.local}`,\n colorFor(rec.local)\n );\n const coloredLocal =\n `${ANSIColors.GREY}[` +\n colorize('local: ', ANSIColors.GREY) +\n inner +\n `${ANSIColors.GREY}]${ANSIColors.RESET}`;\n\n // Pad to align distant label across rows\n const visibleLocal = `[local: ${iconFor(rec.local)} ${rec.local}]`;\n const pad = Math.max(0, maxLocalLabelLen - visibleLocal.length);\n labels.push(coloredLocal + ' '.repeat(pad));\n } else {\n // If no local label, insert spaces to keep distant aligned\n labels.push(' '.repeat(maxLocalLabelLen));\n }\n\n if (rec.remote) {\n const inner = colorize(\n `${iconFor(rec.remote)} ${rec.remote}`,\n colorFor(rec.remote)\n );\n labels.push(\n `${ANSIColors.GREY}[` +\n colorize('distant: ', ANSIColors.GREY) +\n inner +\n `${ANSIColors.GREY}]${ANSIColors.RESET}`\n );\n }\n\n appLogger(\n ` - ${colon(colorizeKey(key), { colSize: keys })} ${labels.join(' ')}`\n );\n }\n};\n\nexport const loadDictionaries = async (\n contentDeclarationsPaths: string[] | string,\n configuration: IntlayerConfig\n): Promise<{\n localDictionaries: Dictionary[];\n remoteDictionaries: Dictionary[];\n pluginDictionaries: Dictionary[];\n time: {\n localDictionaries: number;\n remoteDictionaries: number;\n pluginDictionaries: number;\n };\n}> => {\n const { plugins } = configuration;\n const loadDictionariesStartTime = Date.now();\n const appLogger = getAppLogger(configuration);\n\n appLogger('Dictionaries:', { isVerbose: true });\n\n const hasRemoteDictionaries = Boolean(\n configuration.editor.clientId && configuration.editor.clientSecret\n );\n\n if (hasRemoteDictionaries) {\n // We expect to fetch remote dictionaries soon; suppress a transient local-only render\n logger.setExpectRemote(true);\n }\n\n // Started first so the CMS round trips (OAuth2 token, update timestamps)\n // overlap plugin and local loading instead of running after them.\n const remoteDictionariesPromise: Promise<Dictionary[]> = hasRemoteDictionaries\n ? loadRemoteDictionaries(configuration, setLoadDictionariesStatus, {\n onStartRemoteCheck: () => logger.startRemoteCheck(),\n onStopRemoteCheck: () => logger.stopRemoteCheck(),\n onError: (error) => logger.setRemoteError(error),\n })\n .then((dictionaries) =>\n filterInvalidDictionaries(dictionaries, configuration)\n )\n .then((dictionaries) => formatDictionaries(dictionaries))\n : Promise.resolve([]);\n\n // Awaited later; this keeps an early rejection from being reported as unhandled\n remoteDictionariesPromise.catch(() => undefined);\n\n // Load additional dictionaries via plugins (e.g., ICU JSON ingestion)\n const resolvedPlugins = await Promise.all(plugins ?? []);\n\n const pluginsWithLoadDictionaries = resolvedPlugins.filter(\n (plugin) => plugin.loadDictionaries\n );\n\n logger.setPluginNames(pluginsWithLoadDictionaries.map(({ name }) => name));\n\n const loadPluginDictionariesPromise = pluginsWithLoadDictionaries.map(\n async (plugin) => {\n try {\n const res = await plugin.loadDictionaries?.({\n configuration,\n });\n\n return (res as Dictionary[] | undefined) ?? [];\n } catch (error) {\n logger.setPluginError(error as Error, plugin.name);\n\n return [];\n }\n }\n );\n\n const pluginDictionaries: Dictionary[] = await Promise.all(\n loadPluginDictionariesPromise as Promise<Dictionary[]>[]\n )\n .then((dictionaries) => dictionaries.flat())\n .then((dictionaries) =>\n filterInvalidDictionaries(dictionaries, configuration)\n )\n .then((dictionaries) => formatDictionaries(dictionaries));\n\n logger.setPluginTotal(pluginDictionaries.length);\n logger.setPluginDone(pluginDictionaries.length);\n\n const pluginDictionariesTime = Date.now();\n\n const files = Array.isArray(contentDeclarationsPaths)\n ? contentDeclarationsPaths\n : [contentDeclarationsPaths];\n\n const localDictionaries: Dictionary[] = await loadContentDeclarations(\n files,\n configuration,\n setLoadDictionariesStatus\n )\n .then((dictionaries) =>\n filterInvalidDictionaries(dictionaries, configuration)\n )\n .then((dictionaries) => formatDictionaries(dictionaries));\n\n const localDictionariesTime = Date.now();\n\n const localDictionariesStatus = localDictionaries.map(\n (dictionary) =>\n ({\n dictionaryKey: dictionary.key,\n type: 'local',\n status: 'built',\n }) as const\n );\n\n setLoadDictionariesStatus(localDictionariesStatus);\n\n const remoteDictionaries: Dictionary[] = await remoteDictionariesPromise;\n\n const remoteDictionariesTime = Date.now();\n\n // Stop spinner and show final progress line(s)\n logger.finish();\n\n printSummary(configuration);\n\n return {\n localDictionaries,\n remoteDictionaries,\n pluginDictionaries,\n time: {\n localDictionaries: localDictionariesTime - pluginDictionariesTime,\n remoteDictionaries: remoteDictionariesTime - localDictionariesTime,\n pluginDictionaries: pluginDictionariesTime - loadDictionariesStartTime,\n },\n };\n};\n"],"mappings":"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"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { sortAlphabetically } from "../utils/sortAlphabetically.mjs";
|
|
2
|
-
import { fetchDistantDictionaries } from "../fetchDistantDictionaries.mjs";
|
|
3
2
|
import { getConfiguration } from "@intlayer/config/node";
|
|
4
|
-
import { createIntlayerCMS } from "@intlayer/api";
|
|
5
|
-
import { dictionaryEndpoint } from "@intlayer/api/dictionary";
|
|
6
3
|
import { getRemoteDictionaries } from "@intlayer/dictionaries-entry/remote";
|
|
7
4
|
|
|
8
5
|
//#region src/loadDictionaries/loadRemoteDictionaries.ts
|
|
@@ -17,7 +14,8 @@ const loadRemoteDictionaries = async (configuration = getConfiguration(), onStat
|
|
|
17
14
|
if (!Boolean(editor.clientId && editor.clientSecret)) return [];
|
|
18
15
|
try {
|
|
19
16
|
options?.onStartRemoteCheck?.();
|
|
20
|
-
const
|
|
17
|
+
const { createRemoteDictionaryClient, fetchDistantDictionaries } = await import("./remoteDictionaryClient.mjs");
|
|
18
|
+
const distantDictionaryUpdateTimeStamp = (await createRemoteDictionaryClient(configuration).getDictionariesUpdateTimestamp()).data;
|
|
21
19
|
if (!distantDictionaryUpdateTimeStamp) throw new Error("No distant dictionaries found");
|
|
22
20
|
const dictionariesIdToFetch = Object.entries(distantDictionaryUpdateTimeStamp).filter(([dictionaryId, data]) => {
|
|
23
21
|
if (!data.updatedAt) return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadRemoteDictionaries.mjs","names":[],"sources":["../../../src/loadDictionaries/loadRemoteDictionaries.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"loadRemoteDictionaries.mjs","names":[],"sources":["../../../src/loadDictionaries/loadRemoteDictionaries.ts"],"sourcesContent":["// @ts-ignore @intlayer/backend is not build yet\nimport type { DictionaryAPI } from '@intlayer/backend';\nimport { getConfiguration } from '@intlayer/config/node';\nimport { getRemoteDictionaries } from '@intlayer/dictionaries-entry/remote';\nimport type {\n Dictionary,\n DictionaryId,\n DictionaryKey,\n} from '@intlayer/types/dictionary';\nimport { sortAlphabetically } from '../utils/sortAlphabetically';\nimport type { DictionariesStatus } from './loadDictionaries';\n\nexport const formatDistantDictionaries = (\n dictionaries: (DictionaryAPI | Dictionary)[]\n): Dictionary[] =>\n dictionaries.map((dict) => ({\n ...dict,\n localId: `${dict.key}::remote::${dict.id}`,\n location: 'remote' as const,\n }));\n\nexport const loadRemoteDictionaries = async (\n configuration = getConfiguration(),\n onStatusUpdate?: (status: DictionariesStatus[]) => void,\n options?: {\n onStartRemoteCheck?: () => void;\n onStopRemoteCheck?: () => void;\n onError?: (error: Error) => void;\n }\n): Promise<Dictionary[]> => {\n const { editor } = configuration;\n const remoteDictionariesRecord = getRemoteDictionaries(configuration);\n\n const hasRemoteDictionaries = Boolean(editor.clientId && editor.clientSecret);\n\n if (!hasRemoteDictionaries) return [];\n\n try {\n options?.onStartRemoteCheck?.();\n\n // Lazy: the CMS client is only needed when remote credentials are set\n const { createRemoteDictionaryClient, fetchDistantDictionaries } =\n await import('./remoteDictionaryClient');\n\n const dictionary = createRemoteDictionaryClient(configuration);\n\n // Get the list of dictionary keys\n const getDictionariesKeysResult =\n await dictionary.getDictionariesUpdateTimestamp();\n\n const distantDictionaryUpdateTimeStamp: Record<\n DictionaryId,\n { key: DictionaryKey; updatedAt: number }\n > | null = getDictionariesKeysResult.data;\n\n if (!distantDictionaryUpdateTimeStamp) {\n throw new Error('No distant dictionaries found');\n }\n\n const dictionariesIdToFetch = Object.entries(\n distantDictionaryUpdateTimeStamp\n ).filter(([dictionaryId, data]) => {\n // If remote doesn't provide updatedAt, fetch to be safe\n if (!data.updatedAt) return true;\n\n // If no local cache exists, fetch\n const local: Dictionary | undefined = remoteDictionariesRecord[\n data.key\n ]?.find((dictionary) => dictionary.id === dictionaryId);\n if (!local) return true;\n\n const localUpdatedAtRaw = (local as any)?.updatedAt as\n | number\n | string\n | undefined;\n\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n\n // If local timestamp missing or older than remote, fetch\n if (typeof localUpdatedAt !== 'number') return true;\n\n return data.updatedAt > localUpdatedAt;\n });\n\n const flatRemoteDictionariesRecord: DictionaryAPI[] = Object.values(\n remoteDictionariesRecord\n ).flat();\n\n const cachedDictionaries: Dictionary[] =\n flatRemoteDictionariesRecord.filter((dictionary) => {\n const remoteUpdatedAt = dictionary.id\n ? distantDictionaryUpdateTimeStamp[dictionary.id]?.updatedAt\n : undefined;\n\n const localUpdatedAtRaw = dictionary.updatedAt;\n\n const localUpdatedAt =\n typeof localUpdatedAtRaw === 'number'\n ? localUpdatedAtRaw\n : localUpdatedAtRaw\n ? new Date(localUpdatedAtRaw).getTime()\n : undefined;\n\n // Consider as cached/imported when local exists and is up-to-date or newer\n return (\n typeof localUpdatedAt === 'number' &&\n typeof remoteUpdatedAt === 'number' &&\n localUpdatedAt >= remoteUpdatedAt\n );\n });\n\n // Report cached as already imported\n if (cachedDictionaries.length > 0) {\n onStatusUpdate?.(\n cachedDictionaries.map((dictionary) => ({\n dictionaryKey: dictionary.key,\n type: 'remote',\n status: 'imported',\n }))\n );\n }\n\n // Several qualified dictionaries can share a key, so keys are deduplicated:\n // one request per key already returns all of its siblings.\n const orderedDistantDictionaryKeys = [\n ...new Set(dictionariesIdToFetch.map(([, data]) => data.key)),\n ].sort(sortAlphabetically);\n\n // Report pending for keys to be fetched so totals are visible immediately\n if (orderedDistantDictionaryKeys.length > 0) {\n onStatusUpdate?.(\n orderedDistantDictionaryKeys.map((key) => ({\n dictionaryKey: key,\n type: 'remote',\n status: 'pending',\n }))\n );\n }\n\n const distantDictionariesData = await fetchDistantDictionaries(\n {\n dictionaryKeys: orderedDistantDictionaryKeys,\n },\n onStatusUpdate\n );\n\n const distantDictionaries: Dictionary[] = formatDistantDictionaries(\n distantDictionariesData\n );\n\n // Fetching by key also returns the up-to-date siblings of a stale\n // dictionary, so drop the cached copies the remote just sent back.\n const fetchedDictionaryIds = new Set(\n distantDictionaries.map((dictionary) => dictionary.id)\n );\n\n const notRefetchedCachedDictionaries = cachedDictionaries.filter(\n (dictionary) => !fetchedDictionaryIds.has(dictionary.id)\n );\n\n return [...notRefetchedCachedDictionaries, ...distantDictionaries];\n } catch (error) {\n options?.onError?.(error as Error);\n return [];\n } finally {\n options?.onStopRemoteCheck?.();\n }\n};\n"],"mappings":";;;;;AAYA,MAAa,6BACX,iBAEA,aAAa,KAAK,UAAU;CAC1B,GAAG;CACH,SAAS,GAAG,KAAK,IAAI,YAAY,KAAK;CACtC,UAAU;AACZ,EAAE;AAEJ,MAAa,yBAAyB,OACpC,gBAAgB,iBAAiB,GACjC,gBACA,YAK0B;CAC1B,MAAM,EAAE,WAAW;CACnB,MAAM,2BAA2B,sBAAsB,aAAa;CAIpE,IAAI,CAF0B,QAAQ,OAAO,YAAY,OAAO,YAEvC,GAAG,OAAO,CAAC;CAEpC,IAAI;EACF,SAAS,qBAAqB;EAG9B,MAAM,EAAE,8BAA8B,6BACpC,MAAM,OAAO;EAQf,MAAM,oCAGK,MATQ,6BAA6B,aAI/B,CAAC,CAAC,+BAA+B,EAKd,CAAC;EAErC,IAAI,CAAC,kCACH,MAAM,IAAI,MAAM,+BAA+B;EAGjD,MAAM,wBAAwB,OAAO,QACnC,gCACF,CAAC,CAAC,QAAQ,CAAC,cAAc,UAAU;GAEjC,IAAI,CAAC,KAAK,WAAW,OAAO;GAG5B,MAAM,QAAgC,yBACpC,KAAK,IACN,EAAE,MAAM,eAAe,WAAW,OAAO,YAAY;GACtD,IAAI,CAAC,OAAO,OAAO;GAEnB,MAAM,oBAAqB,OAAe;GAK1C,MAAM,iBACJ,OAAO,sBAAsB,WACzB,oBACA,oBACE,IAAI,KAAK,iBAAiB,CAAC,CAAC,QAAQ,IACpC;GAGR,IAAI,OAAO,mBAAmB,UAAU,OAAO;GAE/C,OAAO,KAAK,YAAY;EAC1B,CAAC;EAMD,MAAM,qBAJgD,OAAO,OAC3D,wBACF,CAAC,CAAC,KAG2B,CAAC,CAAC,QAAQ,eAAe;GAClD,MAAM,kBAAkB,WAAW,KAC/B,iCAAiC,WAAW,GAAG,EAAE,YACjD;GAEJ,MAAM,oBAAoB,WAAW;GAErC,MAAM,iBACJ,OAAO,sBAAsB,WACzB,oBACA,oBACE,IAAI,KAAK,iBAAiB,CAAC,CAAC,QAAQ,IACpC;GAGR,OACE,OAAO,mBAAmB,YAC1B,OAAO,oBAAoB,YAC3B,kBAAkB;EAEtB,CAAC;EAGH,IAAI,mBAAmB,SAAS,GAC9B,iBACE,mBAAmB,KAAK,gBAAgB;GACtC,eAAe,WAAW;GAC1B,MAAM;GACN,QAAQ;EACV,EAAE,CACJ;EAKF,MAAM,+BAA+B,CACnC,GAAG,IAAI,IAAI,sBAAsB,KAAK,GAAG,UAAU,KAAK,GAAG,CAAC,CAC9D,CAAC,CAAC,KAAK,kBAAkB;EAGzB,IAAI,6BAA6B,SAAS,GACxC,iBACE,6BAA6B,KAAK,SAAS;GACzC,eAAe;GACf,MAAM;GACN,QAAQ;EACV,EAAE,CACJ;EAGF,MAAM,0BAA0B,MAAM,yBACpC,EACE,gBAAgB,6BAClB,GACA,cACF;EAEA,MAAM,sBAAoC,0BACxC,uBACF;EAIA,MAAM,uBAAuB,IAAI,IAC/B,oBAAoB,KAAK,eAAe,WAAW,EAAE,CACvD;EAMA,OAAO,CAAC,GAJ+B,mBAAmB,QACvD,eAAe,CAAC,qBAAqB,IAAI,WAAW,EAAE,CAGjB,GAAG,GAAG,mBAAmB;CACnE,SAAS,OAAO;EACd,SAAS,UAAU,KAAc;EACjC,OAAO,CAAC;CACV,UAAU;EACR,SAAS,oBAAoB;CAC/B;AACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { fetchDistantDictionaries } from "../fetchDistantDictionaries.mjs";
|
|
2
|
+
import { createIntlayerCMS } from "@intlayer/api";
|
|
3
|
+
import { dictionaryEndpoint } from "@intlayer/api/dictionary";
|
|
4
|
+
|
|
5
|
+
//#region src/loadDictionaries/remoteDictionaryClient.ts
|
|
6
|
+
/**
|
|
7
|
+
* Creates the CMS dictionary client.
|
|
8
|
+
*
|
|
9
|
+
* Kept in its own module so `loadRemoteDictionaries` can import it lazily:
|
|
10
|
+
* `@intlayer/api` is only loaded when remote credentials are configured.
|
|
11
|
+
*/
|
|
12
|
+
const createRemoteDictionaryClient = (configuration) => dictionaryEndpoint(createIntlayerCMS(configuration));
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { createRemoteDictionaryClient, fetchDistantDictionaries };
|
|
16
|
+
//# sourceMappingURL=remoteDictionaryClient.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteDictionaryClient.mjs","names":[],"sources":["../../../src/loadDictionaries/remoteDictionaryClient.ts"],"sourcesContent":["import { createIntlayerCMS } from '@intlayer/api';\nimport { dictionaryEndpoint } from '@intlayer/api/dictionary';\nimport type { IntlayerConfig } from '@intlayer/types/config';\n\nexport { fetchDistantDictionaries } from '../fetchDistantDictionaries';\n\n/**\n * Creates the CMS dictionary client.\n *\n * Kept in its own module so `loadRemoteDictionaries` can import it lazily:\n * `@intlayer/api` is only loaded when remote credentials are configured.\n */\nexport const createRemoteDictionaryClient = (\n configuration: IntlayerConfig\n): ReturnType<typeof dictionaryEndpoint> =>\n dictionaryEndpoint(createIntlayerCMS(configuration));\n"],"mappings":";;;;;;;;;;;AAYA,MAAa,gCACX,kBAEA,mBAAmB,kBAAkB,aAAa,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { startContentWatcher } from "./utils/startContentWatcher.mjs";
|
|
2
|
+
import { prepareIntlayer } from "./prepareIntlayer.mjs";
|
|
3
|
+
import { getProcessChainCommands } from "./utils/getProcessChainCommands.mjs";
|
|
4
|
+
import { getAppLogger } from "@intlayer/config/logger";
|
|
5
|
+
|
|
6
|
+
//#region src/prepareIntlayerServer.ts
|
|
7
|
+
/**
|
|
8
|
+
* Explicit override, for any setup the detection below does not recognise:
|
|
9
|
+
* `INTLAYER_WATCH=true` forces the content watcher on, `INTLAYER_WATCH=false`
|
|
10
|
+
* forces it off.
|
|
11
|
+
*/
|
|
12
|
+
const INTLAYER_WATCH_ENV_VAR = "INTLAYER_WATCH";
|
|
13
|
+
/** Runtime flags that restart or reload the process on file changes. */
|
|
14
|
+
const WATCH_MODE_EXEC_ARGUMENTS = [
|
|
15
|
+
"--watch",
|
|
16
|
+
"--watch-path",
|
|
17
|
+
"--hot"
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Environment variables set on the child process by a restarting watcher:
|
|
21
|
+
* `node --watch` and `ts-node-dev`.
|
|
22
|
+
*/
|
|
23
|
+
const WATCH_MODE_ENV_VARS = ["WATCH_REPORT_DEPENDENCIES", "TS_NODE_DEV"];
|
|
24
|
+
/**
|
|
25
|
+
* Package script names that start a dev server (`dev`, `start:dev`,
|
|
26
|
+
* `dev:api`, `watch`…), matched on the `npm_lifecycle_event` that npm, pnpm,
|
|
27
|
+
* yarn and bun set for `<pm> run <script>`.
|
|
28
|
+
*/
|
|
29
|
+
const DEV_SCRIPT_NAME_PATTERN = /\b(dev|develop|development|watch)\b/;
|
|
30
|
+
/**
|
|
31
|
+
* Command lines of the tools that restart a server on file changes, matched on
|
|
32
|
+
* this process and the ones above it. Needed because most of them leave no
|
|
33
|
+
* trace in the child itself.
|
|
34
|
+
*
|
|
35
|
+
* - Dedicated restarters, matched on their executable name (`nodemon`,
|
|
36
|
+
* `watchexec`, `entr`…).
|
|
37
|
+
* - `tsx watch`, whose watch mode is a subcommand.
|
|
38
|
+
* - Any runtime or CLI given `--watch` / `--hot`: `deno run --watch` (which
|
|
39
|
+
* restarts in-process), `nest start --watch`, `tsup --watch --onSuccess`…
|
|
40
|
+
* - The `-w` shorthand of build tools that run the server on each rebuild
|
|
41
|
+
* (`tsdown -w --on-success "node dist/index.mjs"`).
|
|
42
|
+
*/
|
|
43
|
+
const DEV_WATCHER_COMMAND_PATTERNS = [
|
|
44
|
+
/(^|[\s/])(nodemon|ts-node-dev|tsnd|node-dev|tsc-watch|supervisor|watchexec|entr|chokidar|onchange)(\.c?m?js)?(\s|$)/,
|
|
45
|
+
/(^|[\s/])tsx(\/dist\/cli\.m?js)?\s+watch(\s|$)/,
|
|
46
|
+
/\s--(watch|watch-path|hot)(=|\s|$)/,
|
|
47
|
+
/(^|[\s/])(tsdown|tsup|tsc|rollup|rolldown)(\/\S*)?(\s+\S+)*\s+-w(\s|$)/
|
|
48
|
+
];
|
|
49
|
+
/**
|
|
50
|
+
* Whether a command line is a watcher that restarts its child on changes.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* getIsDevWatcherCommand('node /app/node_modules/.bin/nodemon src/index.ts'); // → true
|
|
55
|
+
* getIsDevWatcherCommand('node dist/index.js'); // → false
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
const getIsDevWatcherCommand = (command) => DEV_WATCHER_COMMAND_PATTERNS.some((pattern) => pattern.test(command));
|
|
59
|
+
/**
|
|
60
|
+
* Whether this process runs under a watcher that restarts or reloads it
|
|
61
|
+
* (`bun --watch`/`--hot`, `node --watch`, `ts-node-dev`).
|
|
62
|
+
*/
|
|
63
|
+
const getIsWatchModeProcess = () => process.execArgv.some((argument) => WATCH_MODE_EXEC_ARGUMENTS.some((flag) => argument === flag || argument.startsWith(`${flag}=`))) || WATCH_MODE_ENV_VARS.some((envVar) => Boolean(process.env[envVar]));
|
|
64
|
+
/**
|
|
65
|
+
* Whether a server process runs in development, and so should watch the
|
|
66
|
+
* content declarations.
|
|
67
|
+
*
|
|
68
|
+
* `INTLAYER_WATCH=true|false` decides first, then `NODE_ENV` when set. An unset
|
|
69
|
+
* `NODE_ENV` is not a sign of development (`bun dist/index.mjs` rarely sets
|
|
70
|
+
* it), so the process must then show a positive signal, cheapest first:
|
|
71
|
+
* 1. a runtime watch flag or marker (`bun --watch`/`--hot`, `node --watch`,
|
|
72
|
+
* `ts-node-dev`);
|
|
73
|
+
* 2. a `dev`-like package script;
|
|
74
|
+
* 3. a restarting watcher in its own command line or its ancestors'
|
|
75
|
+
* (`deno run --watch`, `nodemon`, `tsx watch`…), for those started
|
|
76
|
+
* directly rather than through a script.
|
|
77
|
+
*/
|
|
78
|
+
const getIsServerDevelopment = async () => {
|
|
79
|
+
const watchOverride = process.env[INTLAYER_WATCH_ENV_VAR]?.toLowerCase();
|
|
80
|
+
if (watchOverride === "true" || watchOverride === "1") return true;
|
|
81
|
+
if (watchOverride === "false" || watchOverride === "0") return false;
|
|
82
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
83
|
+
if (nodeEnv) return nodeEnv === "development";
|
|
84
|
+
if (getIsWatchModeProcess()) return true;
|
|
85
|
+
if (DEV_SCRIPT_NAME_PATTERN.test(process.env["npm_lifecycle_event"] ?? "")) return true;
|
|
86
|
+
return (await getProcessChainCommands()).some(getIsDevWatcherCommand);
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* The dev check of this process, computed once: its answer cannot change for
|
|
90
|
+
* the life of the process, and Fastify, Elysia or Remix may register the
|
|
91
|
+
* integration more than once.
|
|
92
|
+
*/
|
|
93
|
+
let serverDevelopmentPromise;
|
|
94
|
+
const getIsServerDevelopmentOnce = () => {
|
|
95
|
+
serverDevelopmentPromise ??= getIsServerDevelopment();
|
|
96
|
+
return serverDevelopmentPromise;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Prepares the dictionaries for a server integration (Express, Fastify, Hono…)
|
|
100
|
+
* and, in development, keeps them in sync with the content declarations.
|
|
101
|
+
*
|
|
102
|
+
* Replaces wrapping the dev server in `intlayer watch --with`: the watcher runs
|
|
103
|
+
* inside the server, deduplicated through the project's watcher lock, so a
|
|
104
|
+
* server restarted by `tsx watch` / `node --watch` hands it over to its
|
|
105
|
+
* successor, and a remaining `intlayer watch` makes it stand down.
|
|
106
|
+
*
|
|
107
|
+
* Fire-and-forget: the server must not wait on the build to start listening.
|
|
108
|
+
*
|
|
109
|
+
* @param configuration - The resolved Intlayer configuration.
|
|
110
|
+
* @param options - How the integration identifies itself, and whether to watch.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* prepareIntlayerServer(getConfiguration(), { label: 'express-intlayer' });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
const prepareIntlayerServer = (configuration, { label, watch }) => {
|
|
118
|
+
prepareIntlayer(configuration).then(async () => {
|
|
119
|
+
if (!configuration.content.watch) return;
|
|
120
|
+
if (watch ?? await getIsServerDevelopmentOnce()) startContentWatcher(configuration, { label });
|
|
121
|
+
}).catch((error) => {
|
|
122
|
+
getAppLogger(configuration)(["Failed to prepare Intlayer dictionaries:", error], { level: "error" });
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
export { INTLAYER_WATCH_ENV_VAR, getIsDevWatcherCommand, getIsServerDevelopment, prepareIntlayerServer };
|
|
128
|
+
//# sourceMappingURL=prepareIntlayerServer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareIntlayerServer.mjs","names":[],"sources":["../../src/prepareIntlayerServer.ts"],"sourcesContent":["import { getAppLogger } from '@intlayer/config/logger';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport { prepareIntlayer } from './prepareIntlayer';\nimport { getProcessChainCommands } from './utils/getProcessChainCommands';\nimport { startContentWatcher } from './utils/startContentWatcher';\n\n/**\n * Explicit override, for any setup the detection below does not recognise:\n * `INTLAYER_WATCH=true` forces the content watcher on, `INTLAYER_WATCH=false`\n * forces it off.\n */\nexport const INTLAYER_WATCH_ENV_VAR = 'INTLAYER_WATCH';\n\n/** Runtime flags that restart or reload the process on file changes. */\nconst WATCH_MODE_EXEC_ARGUMENTS = ['--watch', '--watch-path', '--hot'];\n\n/**\n * Environment variables set on the child process by a restarting watcher:\n * `node --watch` and `ts-node-dev`.\n */\nconst WATCH_MODE_ENV_VARS = ['WATCH_REPORT_DEPENDENCIES', 'TS_NODE_DEV'];\n\n/**\n * Package script names that start a dev server (`dev`, `start:dev`,\n * `dev:api`, `watch`…), matched on the `npm_lifecycle_event` that npm, pnpm,\n * yarn and bun set for `<pm> run <script>`.\n */\nconst DEV_SCRIPT_NAME_PATTERN = /\\b(dev|develop|development|watch)\\b/;\n\n/**\n * Command lines of the tools that restart a server on file changes, matched on\n * this process and the ones above it. Needed because most of them leave no\n * trace in the child itself.\n *\n * - Dedicated restarters, matched on their executable name (`nodemon`,\n * `watchexec`, `entr`…).\n * - `tsx watch`, whose watch mode is a subcommand.\n * - Any runtime or CLI given `--watch` / `--hot`: `deno run --watch` (which\n * restarts in-process), `nest start --watch`, `tsup --watch --onSuccess`…\n * - The `-w` shorthand of build tools that run the server on each rebuild\n * (`tsdown -w --on-success \"node dist/index.mjs\"`).\n */\nconst DEV_WATCHER_COMMAND_PATTERNS = [\n /(^|[\\s/])(nodemon|ts-node-dev|tsnd|node-dev|tsc-watch|supervisor|watchexec|entr|chokidar|onchange)(\\.c?m?js)?(\\s|$)/,\n /(^|[\\s/])tsx(\\/dist\\/cli\\.m?js)?\\s+watch(\\s|$)/,\n /\\s--(watch|watch-path|hot)(=|\\s|$)/,\n // `-w` alone is too common a flag (`grep -w`), so it only counts after a\n // build tool whose `-w` means watch (`tsdown -w --on-success …`, `tsup -w`).\n /(^|[\\s/])(tsdown|tsup|tsc|rollup|rolldown)(\\/\\S*)?(\\s+\\S+)*\\s+-w(\\s|$)/,\n];\n\n/**\n * Whether a command line is a watcher that restarts its child on changes.\n *\n * @example\n * ```ts\n * getIsDevWatcherCommand('node /app/node_modules/.bin/nodemon src/index.ts'); // → true\n * getIsDevWatcherCommand('node dist/index.js'); // → false\n * ```\n */\nexport const getIsDevWatcherCommand = (command: string): boolean =>\n DEV_WATCHER_COMMAND_PATTERNS.some((pattern) => pattern.test(command));\n\n/**\n * Whether this process runs under a watcher that restarts or reloads it\n * (`bun --watch`/`--hot`, `node --watch`, `ts-node-dev`).\n */\nconst getIsWatchModeProcess = (): boolean =>\n process.execArgv.some((argument) =>\n WATCH_MODE_EXEC_ARGUMENTS.some(\n (flag) => argument === flag || argument.startsWith(`${flag}=`)\n )\n ) || WATCH_MODE_ENV_VARS.some((envVar) => Boolean(process.env[envVar]));\n\n/**\n * Whether a server process runs in development, and so should watch the\n * content declarations.\n *\n * `INTLAYER_WATCH=true|false` decides first, then `NODE_ENV` when set. An unset\n * `NODE_ENV` is not a sign of development (`bun dist/index.mjs` rarely sets\n * it), so the process must then show a positive signal, cheapest first:\n * 1. a runtime watch flag or marker (`bun --watch`/`--hot`, `node --watch`,\n * `ts-node-dev`);\n * 2. a `dev`-like package script;\n * 3. a restarting watcher in its own command line or its ancestors'\n * (`deno run --watch`, `nodemon`, `tsx watch`…), for those started\n * directly rather than through a script.\n */\nexport const getIsServerDevelopment = async (): Promise<boolean> => {\n const watchOverride = process.env[INTLAYER_WATCH_ENV_VAR]?.toLowerCase();\n\n if (watchOverride === 'true' || watchOverride === '1') return true;\n if (watchOverride === 'false' || watchOverride === '0') return false;\n\n const nodeEnv = process.env['NODE_ENV'];\n\n if (nodeEnv) return nodeEnv === 'development';\n\n if (getIsWatchModeProcess()) return true;\n\n if (DEV_SCRIPT_NAME_PATTERN.test(process.env['npm_lifecycle_event'] ?? '')) {\n return true;\n }\n\n const processChainCommands = await getProcessChainCommands();\n\n return processChainCommands.some(getIsDevWatcherCommand);\n};\n\n/**\n * The dev check of this process, computed once: its answer cannot change for\n * the life of the process, and Fastify, Elysia or Remix may register the\n * integration more than once.\n */\nlet serverDevelopmentPromise: Promise<boolean> | undefined;\n\nconst getIsServerDevelopmentOnce = (): Promise<boolean> => {\n serverDevelopmentPromise ??= getIsServerDevelopment();\n\n return serverDevelopmentPromise;\n};\n\nexport type PrepareIntlayerServerOptions = {\n /**\n * How the integration names itself in the watcher lock and in messages, e.g.\n * `express-intlayer`.\n */\n label: string;\n /**\n * Whether to watch the content declarations after preparing them.\n * Defaults to {@link getIsServerDevelopment}.\n */\n watch?: boolean;\n};\n\n/**\n * Prepares the dictionaries for a server integration (Express, Fastify, Hono…)\n * and, in development, keeps them in sync with the content declarations.\n *\n * Replaces wrapping the dev server in `intlayer watch --with`: the watcher runs\n * inside the server, deduplicated through the project's watcher lock, so a\n * server restarted by `tsx watch` / `node --watch` hands it over to its\n * successor, and a remaining `intlayer watch` makes it stand down.\n *\n * Fire-and-forget: the server must not wait on the build to start listening.\n *\n * @param configuration - The resolved Intlayer configuration.\n * @param options - How the integration identifies itself, and whether to watch.\n *\n * @example\n * ```ts\n * prepareIntlayerServer(getConfiguration(), { label: 'express-intlayer' });\n * ```\n */\nexport const prepareIntlayerServer = (\n configuration: IntlayerConfig,\n { label, watch }: PrepareIntlayerServerOptions\n): void => {\n prepareIntlayer(configuration)\n .then(async () => {\n if (!configuration.content.watch) return;\n\n const shouldWatch = watch ?? (await getIsServerDevelopmentOnce());\n\n if (shouldWatch) startContentWatcher(configuration, { label });\n })\n .catch((error: unknown) => {\n getAppLogger(configuration)(\n ['Failed to prepare Intlayer dictionaries:', error],\n { level: 'error' }\n );\n });\n};\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,yBAAyB;;AAGtC,MAAM,4BAA4B;CAAC;CAAW;CAAgB;AAAO;;;;;AAMrE,MAAM,sBAAsB,CAAC,6BAA6B,aAAa;;;;;;AAOvE,MAAM,0BAA0B;;;;;;;;;;;;;;AAehC,MAAM,+BAA+B;CACnC;CACA;CACA;CAGA;AACF;;;;;;;;;;AAWA,MAAa,0BAA0B,YACrC,6BAA6B,MAAM,YAAY,QAAQ,KAAK,OAAO,CAAC;;;;;AAMtE,MAAM,8BACJ,QAAQ,SAAS,MAAM,aACrB,0BAA0B,MACvB,SAAS,aAAa,QAAQ,SAAS,WAAW,GAAG,KAAK,EAAE,CAC/D,CACF,KAAK,oBAAoB,MAAM,WAAW,QAAQ,QAAQ,IAAI,OAAO,CAAC;;;;;;;;;;;;;;;AAgBxE,MAAa,yBAAyB,YAA8B;CAClE,MAAM,gBAAgB,QAAQ,IAAI,uBAAuB,EAAE,YAAY;CAEvE,IAAI,kBAAkB,UAAU,kBAAkB,KAAK,OAAO;CAC9D,IAAI,kBAAkB,WAAW,kBAAkB,KAAK,OAAO;CAE/D,MAAM;CAEN,IAAI,SAAS,OAAO,YAAY;CAEhC,IAAI,sBAAsB,GAAG,OAAO;CAEpC,IAAI,wBAAwB,KAAK,QAAQ,IAAI,0BAA0B,EAAE,GACvE,OAAO;CAKT,QAAO,MAF4B,wBAAwB,EAEhC,CAAC,KAAK,sBAAsB;AACzD;;;;;;AAOA,IAAI;AAEJ,MAAM,mCAAqD;CACzD,6BAA6B,uBAAuB;CAEpD,OAAO;AACT;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,yBACX,eACA,EAAE,OAAO,YACA;CACT,gBAAgB,aAAa,CAAC,CAC3B,KAAK,YAAY;EAChB,IAAI,CAAC,cAAc,QAAQ,OAAO;EAIlC,IAFoB,SAAU,MAAM,2BAA2B,GAE9C,oBAAoB,eAAe,EAAE,MAAM,CAAC;CAC/D,CAAC,CAAC,CACD,OAAO,UAAmB;EACzB,aAAa,aAAa,CAAC,CACzB,CAAC,4CAA4C,KAAK,GAClD,EAAE,OAAO,QAAQ,CACnB;CACF,CAAC;AACL"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/getProcessChainCommands.ts
|
|
6
|
+
/** How long listing the processes may take before giving up. */
|
|
7
|
+
const PROCESS_LIST_TIMEOUT_MS = 2e3;
|
|
8
|
+
/**
|
|
9
|
+
* Parses the output of `ps -A -o pid=,ppid=,command=`.
|
|
10
|
+
*
|
|
11
|
+
* @param output - One `<pid> <ppid> <command>` row per line.
|
|
12
|
+
* @returns The processes, keyed by PID.
|
|
13
|
+
*/
|
|
14
|
+
const parseProcessTable = (output) => {
|
|
15
|
+
const processes = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const line of output.split("\n")) {
|
|
17
|
+
const match = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
|
|
18
|
+
if (!match) continue;
|
|
19
|
+
processes.set(Number(match[1]), {
|
|
20
|
+
parentPid: Number(match[2]),
|
|
21
|
+
command: match[3].trim()
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return processes;
|
|
25
|
+
};
|
|
26
|
+
/** Reads one process from `/proc` (Linux): no subprocess needed. */
|
|
27
|
+
const readLinuxProcess = async (pid) => {
|
|
28
|
+
try {
|
|
29
|
+
const stat = await readFile(`/proc/${pid}/stat`, "utf8");
|
|
30
|
+
const [, parentPidField] = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
|
|
31
|
+
const commandLine = await readFile(`/proc/${pid}/cmdline`, "utf8");
|
|
32
|
+
return {
|
|
33
|
+
parentPid: Number(parentPidField),
|
|
34
|
+
command: commandLine.split("\0").join(" ").trim()
|
|
35
|
+
};
|
|
36
|
+
} catch {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/** Lists every process through one `ps` call (macOS, BSD). */
|
|
41
|
+
const readProcessTable = async () => {
|
|
42
|
+
try {
|
|
43
|
+
const { stdout } = await promisify(execFile)("ps", [
|
|
44
|
+
"-A",
|
|
45
|
+
"-o",
|
|
46
|
+
"pid=,ppid=,command="
|
|
47
|
+
], {
|
|
48
|
+
timeout: PROCESS_LIST_TIMEOUT_MS,
|
|
49
|
+
maxBuffer: 16777216
|
|
50
|
+
});
|
|
51
|
+
return parseProcessTable(stdout);
|
|
52
|
+
} catch {
|
|
53
|
+
return /* @__PURE__ */ new Map();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Whether reading the process list is allowed without side effects.
|
|
58
|
+
*
|
|
59
|
+
* Under Deno, a missing permission makes an interactive process stop and
|
|
60
|
+
* prompt the user, so the list is only read when access is already granted.
|
|
61
|
+
* Node and Bun have no permission model on these calls.
|
|
62
|
+
*/
|
|
63
|
+
const getCanReadProcessList = () => {
|
|
64
|
+
const permissions = globalThis.Deno?.permissions;
|
|
65
|
+
if (!permissions) return true;
|
|
66
|
+
try {
|
|
67
|
+
const descriptor = process.platform === "linux" ? {
|
|
68
|
+
name: "read",
|
|
69
|
+
path: "/proc"
|
|
70
|
+
} : {
|
|
71
|
+
name: "run",
|
|
72
|
+
command: "ps"
|
|
73
|
+
};
|
|
74
|
+
return permissions.querySync?.(descriptor).state === "granted";
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Command lines of this process and of the processes above it, closest first.
|
|
81
|
+
*
|
|
82
|
+
* This process is included because some runtimes restart the script inside
|
|
83
|
+
* the same process, leaving their watch flag only on their own command line
|
|
84
|
+
* (`deno run --watch`).
|
|
85
|
+
*
|
|
86
|
+
* Best effort: resolves to an empty list on Windows, or when the process list
|
|
87
|
+
* cannot be read (sandbox, missing `ps`, Deno without the permission), and
|
|
88
|
+
* never throws.
|
|
89
|
+
*
|
|
90
|
+
* @param maxDepth - How many processes to read at most, this one included.
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* await getProcessChainCommands();
|
|
95
|
+
* // → ['node src/index.js', 'node …/nodemon src/index.js', 'npm run dev']
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
const getProcessChainCommands = async (maxDepth = 6) => {
|
|
99
|
+
if (process.platform === "win32") return [];
|
|
100
|
+
if (!getCanReadProcessList()) return [];
|
|
101
|
+
const getProcess = process.platform === "linux" ? readLinuxProcess : await readProcessTable().then((processes) => async (pid) => processes.get(pid));
|
|
102
|
+
const commands = [];
|
|
103
|
+
let pid = process.pid;
|
|
104
|
+
while (pid > 1 && commands.length < maxDepth) {
|
|
105
|
+
const entry = await getProcess(pid);
|
|
106
|
+
if (!entry) break;
|
|
107
|
+
commands.push(entry.command);
|
|
108
|
+
pid = entry.parentPid;
|
|
109
|
+
}
|
|
110
|
+
return commands;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
export { getProcessChainCommands, parseProcessTable };
|
|
115
|
+
//# sourceMappingURL=getProcessChainCommands.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getProcessChainCommands.mjs","names":[],"sources":["../../../src/utils/getProcessChainCommands.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { promisify } from 'node:util';\n\n/** Process as listed by the OS: its parent and the command line it runs. */\nexport type ProcessEntry = {\n parentPid: number;\n command: string;\n};\n\n/** How long listing the processes may take before giving up. */\nconst PROCESS_LIST_TIMEOUT_MS = 2000;\n\n/**\n * Parses the output of `ps -A -o pid=,ppid=,command=`.\n *\n * @param output - One `<pid> <ppid> <command>` row per line.\n * @returns The processes, keyed by PID.\n */\nexport const parseProcessTable = (\n output: string\n): Map<number, ProcessEntry> => {\n const processes = new Map<number, ProcessEntry>();\n\n for (const line of output.split('\\n')) {\n const match = /^\\s*(\\d+)\\s+(\\d+)\\s+(.*)$/.exec(line);\n\n if (!match) continue;\n\n processes.set(Number(match[1]), {\n parentPid: Number(match[2]),\n command: match[3]!.trim(),\n });\n }\n\n return processes;\n};\n\n/** Reads one process from `/proc` (Linux): no subprocess needed. */\nconst readLinuxProcess = async (\n pid: number\n): Promise<ProcessEntry | undefined> => {\n try {\n const stat = await readFile(`/proc/${pid}/stat`, 'utf8');\n // The command name is wrapped in parentheses and may contain spaces, so the\n // fields are read after the last `)`: `<state> <ppid> …`.\n const [, parentPidField] = stat.slice(stat.lastIndexOf(')') + 2).split(' ');\n const commandLine = await readFile(`/proc/${pid}/cmdline`, 'utf8');\n\n return {\n parentPid: Number(parentPidField),\n command: commandLine.split('\\0').join(' ').trim(),\n };\n } catch {\n return undefined;\n }\n};\n\n/** Lists every process through one `ps` call (macOS, BSD). */\nconst readProcessTable = async (): Promise<Map<number, ProcessEntry>> => {\n try {\n const { stdout } = await promisify(execFile)(\n 'ps',\n ['-A', '-o', 'pid=,ppid=,command='],\n { timeout: PROCESS_LIST_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024 }\n );\n\n return parseProcessTable(stdout);\n } catch {\n return new Map();\n }\n};\n\ntype DenoPermissionState = 'granted' | 'prompt' | 'denied';\n\ntype DenoPermissions = {\n querySync?: (descriptor: {\n name: 'run' | 'read';\n command?: string;\n path?: string;\n }) => { state: DenoPermissionState };\n};\n\n/**\n * Whether reading the process list is allowed without side effects.\n *\n * Under Deno, a missing permission makes an interactive process stop and\n * prompt the user, so the list is only read when access is already granted.\n * Node and Bun have no permission model on these calls.\n */\nconst getCanReadProcessList = (): boolean => {\n const permissions = (\n globalThis as { Deno?: { permissions?: DenoPermissions } }\n ).Deno?.permissions;\n\n if (!permissions) return true;\n\n try {\n const descriptor =\n process.platform === 'linux'\n ? ({ name: 'read', path: '/proc' } as const)\n : ({ name: 'run', command: 'ps' } as const);\n\n return permissions.querySync?.(descriptor).state === 'granted';\n } catch {\n return false;\n }\n};\n\n/**\n * Command lines of this process and of the processes above it, closest first.\n *\n * This process is included because some runtimes restart the script inside\n * the same process, leaving their watch flag only on their own command line\n * (`deno run --watch`).\n *\n * Best effort: resolves to an empty list on Windows, or when the process list\n * cannot be read (sandbox, missing `ps`, Deno without the permission), and\n * never throws.\n *\n * @param maxDepth - How many processes to read at most, this one included.\n *\n * @example\n * ```ts\n * await getProcessChainCommands();\n * // → ['node src/index.js', 'node …/nodemon src/index.js', 'npm run dev']\n * ```\n */\nexport const getProcessChainCommands = async (\n maxDepth = 6\n): Promise<string[]> => {\n if (process.platform === 'win32') return [];\n if (!getCanReadProcessList()) return [];\n\n const getProcess: (pid: number) => Promise<ProcessEntry | undefined> =\n process.platform === 'linux'\n ? readLinuxProcess\n : await readProcessTable().then(\n (processes) => async (pid: number) => processes.get(pid)\n );\n\n const commands: string[] = [];\n let pid = process.pid;\n\n // PID 1 is init/launchd: nothing above it is a dev tool.\n while (pid > 1 && commands.length < maxDepth) {\n const entry = await getProcess(pid);\n\n if (!entry) break;\n\n commands.push(entry.command);\n pid = entry.parentPid;\n }\n\n return commands;\n};\n"],"mappings":";;;;;;AAWA,MAAM,0BAA0B;;;;;;;AAQhC,MAAa,qBACX,WAC8B;CAC9B,MAAM,4BAAY,IAAI,IAA0B;CAEhD,KAAK,MAAM,QAAQ,OAAO,MAAM,IAAI,GAAG;EACrC,MAAM,QAAQ,4BAA4B,KAAK,IAAI;EAEnD,IAAI,CAAC,OAAO;EAEZ,UAAU,IAAI,OAAO,MAAM,EAAE,GAAG;GAC9B,WAAW,OAAO,MAAM,EAAE;GAC1B,SAAS,MAAM,EAAE,CAAE,KAAK;EAC1B,CAAC;CACH;CAEA,OAAO;AACT;;AAGA,MAAM,mBAAmB,OACvB,QACsC;CACtC,IAAI;EACF,MAAM,OAAO,MAAM,SAAS,SAAS,IAAI,QAAQ,MAAM;EAGvD,MAAM,GAAG,kBAAkB,KAAK,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG;EAC1E,MAAM,cAAc,MAAM,SAAS,SAAS,IAAI,WAAW,MAAM;EAEjE,OAAO;GACL,WAAW,OAAO,cAAc;GAChC,SAAS,YAAY,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK;EAClD;CACF,QAAQ;EACN;CACF;AACF;;AAGA,MAAM,mBAAmB,YAAgD;CACvE,IAAI;EACF,MAAM,EAAE,WAAW,MAAM,UAAU,QAAQ,CAAC,CAC1C,MACA;GAAC;GAAM;GAAM;EAAqB,GAClC;GAAE,SAAS;GAAyB,WAAW;EAAiB,CAClE;EAEA,OAAO,kBAAkB,MAAM;CACjC,QAAQ;EACN,uBAAO,IAAI,IAAI;CACjB;AACF;;;;;;;;AAmBA,MAAM,8BAAuC;CAC3C,MAAM,cACJ,WACA,MAAM;CAER,IAAI,CAAC,aAAa,OAAO;CAEzB,IAAI;EACF,MAAM,aACJ,QAAQ,aAAa,UAChB;GAAE,MAAM;GAAQ,MAAM;EAAQ,IAC9B;GAAE,MAAM;GAAO,SAAS;EAAK;EAEpC,OAAO,YAAY,YAAY,UAAU,CAAC,CAAC,UAAU;CACvD,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,MAAa,0BAA0B,OACrC,WAAW,MACW;CACtB,IAAI,QAAQ,aAAa,SAAS,OAAO,CAAC;CAC1C,IAAI,CAAC,sBAAsB,GAAG,OAAO,CAAC;CAEtC,MAAM,aACJ,QAAQ,aAAa,UACjB,mBACA,MAAM,iBAAiB,CAAC,CAAC,MACtB,cAAc,OAAO,QAAgB,UAAU,IAAI,GAAG,CACzD;CAEN,MAAM,WAAqB,CAAC;CAC5B,IAAI,MAAM,QAAQ;CAGlB,OAAO,MAAM,KAAK,SAAS,SAAS,UAAU;EAC5C,MAAM,QAAQ,MAAM,WAAW,GAAG;EAElC,IAAI,CAAC,OAAO;EAEZ,SAAS,KAAK,MAAM,OAAO;EAC3B,MAAM,MAAM;CACd;CAEA,OAAO;AACT"}
|
package/dist/esm/utils/index.mjs
CHANGED
|
@@ -20,7 +20,8 @@ import { resolveRelativePath } from "./resolveRelativePath.mjs";
|
|
|
20
20
|
import { runOnce } from "./runOnce.mjs";
|
|
21
21
|
import { runParallel } from "./runParallel/index.mjs";
|
|
22
22
|
import { sortAlphabetically } from "./sortAlphabetically.mjs";
|
|
23
|
+
import { startContentWatcher } from "./startContentWatcher.mjs";
|
|
23
24
|
import { verifyIdenticObjectFormat } from "./verifyIdenticObjectFormat.mjs";
|
|
24
25
|
import { withFileLock } from "./withFileLock.mjs";
|
|
25
26
|
|
|
26
|
-
export { CLI_CONTENT_WATCHER_ENV_VAR, Queue, acquireContentWatcherLock, assembleJSON, autoDecorateContent, buildComponentFilesList, chunkArray, chunkJSON, claimCliContentWatcher, excludeObjectFormat, formatLocale, formatPath, getChunk, getCliContentWatcherLabel, getContentExtension, getContentWatcherLockFilePath, getContentWatcherOwner, getExtensionFromFormat, getFormatFromExtension, getGlobalLimiter, getPathHash, getTaskLimiter, mergeChunks, pLimit, parallelize, parallelizeGlobal, parseContentDeclarationFileName, reconstructFromSingleChunk, reduceObjectFormat, reportRedundantContentWatcher, resolveObjectPromises, resolveRelativePath, runOnce, runParallel, sortAlphabetically, splitTextByLines, verifyIdenticObjectFormat, withFileLock };
|
|
27
|
+
export { CLI_CONTENT_WATCHER_ENV_VAR, Queue, acquireContentWatcherLock, assembleJSON, autoDecorateContent, buildComponentFilesList, chunkArray, chunkJSON, claimCliContentWatcher, excludeObjectFormat, formatLocale, formatPath, getChunk, getCliContentWatcherLabel, getContentExtension, getContentWatcherLockFilePath, getContentWatcherOwner, getExtensionFromFormat, getFormatFromExtension, getGlobalLimiter, getPathHash, getTaskLimiter, mergeChunks, pLimit, parallelize, parallelizeGlobal, parseContentDeclarationFileName, reconstructFromSingleChunk, reduceObjectFormat, reportRedundantContentWatcher, resolveObjectPromises, resolveRelativePath, runOnce, runParallel, sortAlphabetically, splitTextByLines, startContentWatcher, verifyIdenticObjectFormat, withFileLock };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ps } from "./ps.mjs";
|
|
2
2
|
import { wmic } from "./wmic.mjs";
|
|
3
|
-
import * as os
|
|
3
|
+
import * as os from "node:os";
|
|
4
4
|
|
|
5
5
|
//#region src/utils/runParallel/pidTree.ts
|
|
6
6
|
const platformToMethod = {
|
|
@@ -16,7 +16,7 @@ const methodToFn = {
|
|
|
16
16
|
ps,
|
|
17
17
|
wmic
|
|
18
18
|
};
|
|
19
|
-
let platform = os
|
|
19
|
+
let platform = os.platform();
|
|
20
20
|
if (platform.startsWith("win")) platform = "win";
|
|
21
21
|
const method = platformToMethod[platform];
|
|
22
22
|
/**
|
|
@@ -24,7 +24,7 @@ const method = platformToMethod[platform];
|
|
|
24
24
|
*/
|
|
25
25
|
const getAll = (callback) => {
|
|
26
26
|
if (method === void 0) {
|
|
27
|
-
callback(/* @__PURE__ */ new Error(os
|
|
27
|
+
callback(/* @__PURE__ */ new Error(os.platform() + " is not supported yet, please open an issue (https://github.com/simonepri/pidtree)"), void 0);
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
const listFn = methodToFn[method];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pidTree.mjs","names":[
|
|
1
|
+
{"version":3,"file":"pidTree.mjs","names":[],"sources":["../../../../src/utils/runParallel/pidTree.ts"],"sourcesContent":["import * as os from 'node:os';\nimport { ps } from './ps';\nimport { wmic } from './wmic';\n\ntype Platform =\n | 'darwin'\n | 'sunos'\n | 'freebsd'\n | 'netbsd'\n | 'win'\n | 'linux'\n | 'aix';\ntype Method = 'ps' | 'wmic';\n\ntype ProcessList = [number, number][]; // [PPID, PID][]\n\ninterface ListOptions {\n root?: boolean;\n advanced?: boolean;\n}\n\ntype ProcessInfo = { pid: number; ppid?: number };\ntype ResultType<T extends ListOptions | undefined> = T extends {\n advanced: true;\n}\n ? ProcessInfo[]\n : number[];\n\ntype ProcessListCallback = (err: Error | null, list?: ProcessList) => void;\ntype ListCallback<T extends ListOptions | undefined> = (\n err: Error | null,\n list?: ResultType<T>\n) => void;\n\ntype ProcessListFn = (callback: ProcessListCallback) => void;\n\nconst platformToMethod: Record<Platform, Method> = {\n darwin: 'ps',\n sunos: 'ps',\n freebsd: 'ps',\n netbsd: 'ps',\n win: 'wmic',\n linux: 'ps',\n aix: 'ps',\n};\n\nconst methodToFn: Record<Method, ProcessListFn> = {\n ps,\n wmic,\n};\n\nlet platform: string = os.platform();\nif (platform.startsWith('win')) {\n platform = 'win';\n}\n\nconst method: Method | undefined = platformToMethod[platform as Platform];\n\n/**\n * Gets the list of all the pids of the system.\n */\nconst getAll = (callback: ProcessListCallback): void => {\n if (method === undefined) {\n callback(\n new Error(\n os.platform() +\n ' is not supported yet, please open an issue (https://github.com/simonepri/pidtree)'\n ),\n undefined\n );\n return;\n }\n\n const listFn = methodToFn[method];\n listFn(callback);\n};\n\n/**\n * Get the list of children and grandchildren pids of the given PID.\n * @param PID A PID. If -1 will return all the pids.\n * @param options Optional options object.\n * @param callback Called when the list is ready.\n */\nconst pidtree = <T extends ListOptions | undefined>(\n PID: number | string,\n options: T | ListCallback<T> | undefined,\n callback?: ListCallback<T>\n): void => {\n let normalizedOptions: ListOptions;\n let normalizedCallback: ListCallback<T>;\n\n if (typeof options === 'function') {\n normalizedCallback = options;\n normalizedOptions = {};\n } else {\n normalizedCallback = callback!;\n normalizedOptions = typeof options === 'object' ? options : {};\n }\n\n const parsedPID = parseInt(String(PID), 10);\n if (Number.isNaN(parsedPID) || parsedPID < -1) {\n normalizedCallback(\n new TypeError('The pid provided is invalid') as Error,\n undefined\n );\n return;\n }\n\n getAll((err, processList) => {\n if (err) {\n normalizedCallback(err, undefined);\n return;\n }\n\n if (!processList) {\n normalizedCallback(new Error('Failed to get process list'), undefined);\n return;\n }\n\n // If the user wants the whole list just return it\n if (parsedPID === -1) {\n const result = processList.map(([ppid, pid]) =>\n normalizedOptions.advanced ? { ppid, pid } : pid\n ) as ResultType<T>;\n\n normalizedCallback(null, result);\n return;\n }\n\n let root: ProcessInfo | number | undefined;\n for (let l = 0; l < processList.length; l++) {\n const proc = processList[l];\n\n if (!proc) continue;\n const [ppid, pid] = proc;\n\n if (pid === parsedPID) {\n root = normalizedOptions.advanced\n ? { ppid, pid: parsedPID }\n : parsedPID;\n break;\n }\n\n if (ppid === parsedPID) {\n root = normalizedOptions.advanced ? { pid: parsedPID } : parsedPID; // Special pids like 0 on *nix\n }\n }\n\n if (root === undefined) {\n normalizedCallback(new Error('No matching pid found'), undefined);\n return;\n }\n\n // Build the adjacency Hash Map (pid -> [children of pid])\n const tree: Record<number, number[]> = {};\n const listCopy = [...processList];\n while (listCopy.length > 0) {\n const element = listCopy.pop();\n\n if (!element) continue;\n const ppid = element[0];\n const pid = element[1];\n const children = tree[ppid];\n\n if (children) {\n children.push(pid);\n } else {\n tree[ppid] = [pid];\n }\n }\n\n // Starting by the PID provided by the user, traverse the tree using the\n // adjacency Hash Map until the whole subtree is visited.\n // Each pid encountered while visiting is added to the pids array.\n let idx = 0;\n const pids: (ProcessInfo | number)[] = [root];\n while (idx < pids.length) {\n const curpid = normalizedOptions.advanced\n ? (pids[idx++] as ProcessInfo).pid\n : (pids[idx++] as number);\n const children = tree[curpid];\n\n if (!children) continue;\n const length = children.length;\n for (let j = 0; j < length; j++) {\n const childPid = children[j];\n\n if (childPid === undefined) continue;\n\n pids.push(\n normalizedOptions.advanced\n ? { ppid: curpid, pid: childPid }\n : childPid\n );\n }\n\n delete tree[curpid];\n }\n\n if (!normalizedOptions.root) {\n pids.shift(); // Remove root\n }\n\n normalizedCallback(null, pids as ResultType<T>);\n });\n};\n\nconst pify = <T extends ListOptions | undefined>(\n fn: typeof pidtree,\n arg1: number | string,\n arg2: T | undefined\n): Promise<ResultType<T>> => {\n return new Promise((resolve, reject) => {\n fn(arg1, arg2, (err, data) => {\n if (err) return reject(err);\n if (data === undefined) {\n return reject(new Error('No data returned'));\n }\n resolve(data);\n });\n });\n};\n\n// Node versions prior to 4.0.0 do not define have `startsWith`.\n/* istanbul ignore if */\nif (!String.prototype.startsWith) {\n String.prototype.startsWith = function (suffix: string): boolean {\n return this.substring(0, suffix.length) === suffix;\n };\n}\n\n/**\n * Get the list of children pids of the given pid.\n * @param pid A PID. If -1 will return all the pids.\n * @param options Optional options object.\n * @param callback Called when the list is ready. If not provided a promise is returned instead.\n * @returns Only when the callback is not provided.\n */\nexport const list = <T extends ListOptions | undefined>(\n pid: number | string,\n options?: T | ListCallback<T>,\n callback?: ListCallback<T>\n): Promise<ResultType<T>> | undefined => {\n if (typeof options === 'function') {\n pidtree(pid, undefined, options);\n return;\n }\n\n if (typeof callback === 'function') {\n pidtree(pid, options, callback);\n return;\n }\n\n return pify(pidtree, pid, options);\n};\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { run } from "./bin.mjs";
|
|
2
|
-
import * as os
|
|
2
|
+
import * as os from "node:os";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/runParallel/ps.ts
|
|
5
5
|
/**
|
|
@@ -25,7 +25,7 @@ const ps = (callback) => {
|
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
try {
|
|
28
|
-
const lines = stdout.split(os
|
|
28
|
+
const lines = stdout.split(os.EOL);
|
|
29
29
|
const list = [];
|
|
30
30
|
for (let i = 1; i < lines.length; i++) {
|
|
31
31
|
const line = lines[i];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ps.mjs","names":[
|
|
1
|
+
{"version":3,"file":"ps.mjs","names":[],"sources":["../../../../src/utils/runParallel/ps.ts"],"sourcesContent":["import * as os from 'node:os';\nimport { run } from './bin';\n\ntype ProcessList = [number, number][]; // [PPID, PID][]\ntype ProcessListCallback = (err: Error | null, list?: ProcessList) => void;\n\n/**\n * Gets the list of all the pids of the system through the ps command.\n * @param callback Callback function with error and process list.\n */\nexport const ps = (callback: ProcessListCallback): void => {\n const args = ['-A', '-o', 'ppid,pid'];\n\n run('ps', args, (err, stdout, code) => {\n if (err) {\n callback(err);\n return;\n }\n if (code !== 0) {\n callback(new Error(`pidtree ps command exited with code ${code}`));\n return;\n }\n\n // Example of stdout\n //\n // PPID PID\n // 1 430\n // 430 432\n // 1 727\n // 1 7166\n\n if (!stdout) {\n callback(new Error('No stdout received from ps command'));\n return;\n }\n\n try {\n const lines = stdout.split(os.EOL);\n\n const list: ProcessList = [];\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n\n if (!line) continue;\n\n const trimmed = line.trim();\n\n if (!trimmed) continue;\n\n const parts = trimmed.split(/\\s+/);\n const p0 = parts[0];\n const p1 = parts[1];\n\n if (!p0 || !p1) continue;\n\n const ppid = parseInt(p0, 10); // PPID\n const pid = parseInt(p1, 10); // PID\n\n if (!Number.isNaN(ppid) && !Number.isNaN(pid)) {\n list.push([ppid, pid]);\n }\n }\n\n callback(null, list);\n } catch (error) {\n callback(error instanceof Error ? error : new Error(String(error)));\n }\n });\n};\n"],"mappings":";;;;;;;;AAUA,MAAa,MAAM,aAAwC;CAGzD,IAAI,MAAM;EAFI;EAAM;EAAM;CAEb,IAAI,KAAK,QAAQ,SAAS;EACrC,IAAI,KAAK;GACP,SAAS,GAAG;GACZ;EACF;EACA,IAAI,SAAS,GAAG;GACd,yBAAS,IAAI,MAAM,uCAAuC,MAAM,CAAC;GACjE;EACF;EAUA,IAAI,CAAC,QAAQ;GACX,yBAAS,IAAI,MAAM,oCAAoC,CAAC;GACxD;EACF;EAEA,IAAI;GACF,MAAM,QAAQ,OAAO,MAAM,GAAG,GAAG;GAEjC,MAAM,OAAoB,CAAC;GAC3B,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,OAAO,MAAM;IAEnB,IAAI,CAAC,MAAM;IAEX,MAAM,UAAU,KAAK,KAAK;IAE1B,IAAI,CAAC,SAAS;IAEd,MAAM,QAAQ,QAAQ,MAAM,KAAK;IACjC,MAAM,KAAK,MAAM;IACjB,MAAM,KAAK,MAAM;IAEjB,IAAI,CAAC,MAAM,CAAC,IAAI;IAEhB,MAAM,OAAO,SAAS,IAAI,EAAE;IAC5B,MAAM,MAAM,SAAS,IAAI,EAAE;IAE3B,IAAI,CAAC,OAAO,MAAM,IAAI,KAAK,CAAC,OAAO,MAAM,GAAG,GAC1C,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;GAEzB;GAEA,SAAS,MAAM,IAAI;EACrB,SAAS,OAAO;GACd,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACpE;CACF,CAAC;AACH"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { run } from "./bin.mjs";
|
|
2
|
-
import * as os
|
|
2
|
+
import * as os from "node:os";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/runParallel/wmic.ts
|
|
5
5
|
/**
|
|
@@ -28,7 +28,7 @@ const wmic = (callback) => {
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
try {
|
|
31
|
-
const lines = stdout.split(os
|
|
31
|
+
const lines = stdout.split(os.EOL);
|
|
32
32
|
const list = [];
|
|
33
33
|
for (let i = 1; i < lines.length; i++) {
|
|
34
34
|
const line = lines[i];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wmic.mjs","names":[
|
|
1
|
+
{"version":3,"file":"wmic.mjs","names":[],"sources":["../../../../src/utils/runParallel/wmic.ts"],"sourcesContent":["import type { SpawnOptions } from 'node:child_process';\nimport * as os from 'node:os';\nimport { run } from './bin';\n\ntype ProcessList = [number, number][]; // [PPID, PID][]\ntype ProcessListCallback = (err: Error | null, list?: ProcessList) => void;\n\n/**\n * Gets the list of all the pids of the system through the wmic command.\n * @param callback Callback function with error and process list.\n */\nexport const wmic = (callback: ProcessListCallback): void => {\n const args = ['PROCESS', 'get', 'ParentProcessId,ProcessId'];\n const options: SpawnOptions = {\n windowsHide: true,\n windowsVerbatimArguments: true,\n };\n run('wmic', args, options, (err, stdout, code) => {\n if (err) {\n callback(err);\n return;\n }\n\n if (code !== 0) {\n callback(new Error(`pidtree wmic command exited with code ${code}`));\n return;\n }\n\n // Example of stdout\n //\n // ParentProcessId ProcessId\n // 0 777\n\n if (!stdout) {\n callback(new Error('No stdout received from wmic command'));\n return;\n }\n\n try {\n const lines = stdout.split(os.EOL);\n\n const list: ProcessList = [];\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n\n if (!line) continue;\n const trimmed = line.trim();\n\n if (!trimmed) continue;\n\n const parts = trimmed.split(/\\s+/);\n const p0 = parts[0];\n const p1 = parts[1];\n\n if (!p0 || !p1) continue;\n\n const ppid = parseInt(p0, 10); // PPID\n const pid = parseInt(p1, 10); // PID\n\n if (!Number.isNaN(ppid) && !Number.isNaN(pid)) {\n list.push([ppid, pid]);\n }\n }\n\n callback(null, list);\n } catch (error) {\n callback(error instanceof Error ? error : new Error(String(error)));\n }\n });\n};\n"],"mappings":";;;;;;;;AAWA,MAAa,QAAQ,aAAwC;CAM3D,IAAI,QAAQ;EALE;EAAW;EAAO;CAKjB,GAAG;EAHhB,aAAa;EACb,0BAA0B;CAEJ,IAAI,KAAK,QAAQ,SAAS;EAChD,IAAI,KAAK;GACP,SAAS,GAAG;GACZ;EACF;EAEA,IAAI,SAAS,GAAG;GACd,yBAAS,IAAI,MAAM,yCAAyC,MAAM,CAAC;GACnE;EACF;EAOA,IAAI,CAAC,QAAQ;GACX,yBAAS,IAAI,MAAM,sCAAsC,CAAC;GAC1D;EACF;EAEA,IAAI;GACF,MAAM,QAAQ,OAAO,MAAM,GAAG,GAAG;GAEjC,MAAM,OAAoB,CAAC;GAC3B,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,OAAO,MAAM;IAEnB,IAAI,CAAC,MAAM;IACX,MAAM,UAAU,KAAK,KAAK;IAE1B,IAAI,CAAC,SAAS;IAEd,MAAM,QAAQ,QAAQ,MAAM,KAAK;IACjC,MAAM,KAAK,MAAM;IACjB,MAAM,KAAK,MAAM;IAEjB,IAAI,CAAC,MAAM,CAAC,IAAI;IAEhB,MAAM,OAAO,SAAS,IAAI,EAAE;IAC5B,MAAM,MAAM,SAAS,IAAI,EAAE;IAE3B,IAAI,CAAC,OAAO,MAAM,IAAI,KAAK,CAAC,OAAO,MAAM,GAAG,GAC1C,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;GAEzB;GAEA,SAAS,MAAM,IAAI;EACrB,SAAS,OAAO;GACd,SAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACpE;CACF,CAAC;AACH"}
|