@deepseek-ai/dsh-app-boot 0.2.2-alpha.3 → 0.2.2-rc.2

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.
Files changed (73) hide show
  1. package/README.i18n.yaml +4 -4
  2. package/README.md +4 -2
  3. package/README.zh.md +4 -2
  4. package/lib/index.js +37 -17
  5. package/lib/types/bundle-platform.d.ts +4 -2
  6. package/lib/types/bundle-platform.d.ts.map +1 -0
  7. package/lib/types/bundle-platform.js.map +1 -0
  8. package/lib/types/compatibility-preflight.d.ts.map +1 -0
  9. package/lib/types/compatibility-preflight.js.map +1 -0
  10. package/lib/types/config-schema/collect.d.ts.map +1 -0
  11. package/lib/types/config-schema/collect.js.map +1 -0
  12. package/lib/types/config-schema/document.d.ts.map +1 -0
  13. package/lib/types/config-schema/document.js.map +1 -0
  14. package/lib/types/config-schema/index.d.ts.map +1 -0
  15. package/lib/types/config-schema/index.js.map +1 -0
  16. package/lib/types/config-schema/native.d.ts.map +1 -0
  17. package/lib/types/config-schema/native.js.map +1 -0
  18. package/lib/types/config-schema/pattern.d.ts.map +1 -0
  19. package/lib/types/config-schema/pattern.js.map +1 -0
  20. package/lib/types/config-schema/projector.d.ts.map +1 -0
  21. package/lib/types/config-schema/projector.js.map +1 -0
  22. package/lib/types/config-schema/types.d.ts.map +1 -0
  23. package/lib/types/config-schema/types.js.map +1 -0
  24. package/lib/types/index.d.ts +1 -1
  25. package/lib/types/index.d.ts.map +1 -0
  26. package/lib/types/index.js.map +1 -0
  27. package/lib/types/package-meta.d.ts.map +1 -0
  28. package/lib/types/package-meta.js.map +1 -0
  29. package/lib/types/plugin-compatibility.d.ts.map +1 -0
  30. package/lib/types/plugin-compatibility.js.map +1 -0
  31. package/lib/types/profile-compatibility.d.ts.map +1 -0
  32. package/lib/types/profile-compatibility.js.map +1 -0
  33. package/lib/types/profile-context.d.ts.map +1 -0
  34. package/lib/types/profile-context.js.map +1 -0
  35. package/lib/types/profile-plugins.d.ts.map +1 -0
  36. package/lib/types/profile-plugins.js.map +1 -0
  37. package/lib/types/profile-resolution/legacy-links.d.ts.map +1 -0
  38. package/lib/types/profile-resolution/legacy-links.js.map +1 -0
  39. package/lib/types/profile-resolution/resolver.d.ts.map +1 -0
  40. package/lib/types/profile-resolution/resolver.js.map +1 -0
  41. package/lib/types/profile-resolution/service.d.ts.map +1 -0
  42. package/lib/types/profile-resolution/service.js.map +1 -0
  43. package/lib/types/profile-resolution/worker-bootstrap.d.ts.map +1 -0
  44. package/lib/types/profile-resolution/worker-bootstrap.js.map +1 -0
  45. package/lib/types/profile-sanitize.d.ts.map +1 -0
  46. package/lib/types/profile-sanitize.js.map +1 -0
  47. package/lib/types/profile.d.ts.map +1 -0
  48. package/lib/types/profile.js.map +1 -0
  49. package/lib/types/startup-diagnostics.d.ts.map +1 -0
  50. package/lib/types/startup-diagnostics.js.map +1 -0
  51. package/package.json +25 -21
  52. package/src/bundle-platform.ts +35 -0
  53. package/src/compatibility-preflight.ts +187 -0
  54. package/src/config-schema/collect.ts +249 -0
  55. package/src/config-schema/document.ts +169 -0
  56. package/src/config-schema/index.ts +36 -0
  57. package/src/config-schema/native.ts +16 -0
  58. package/src/config-schema/pattern.ts +62 -0
  59. package/src/config-schema/projector.ts +455 -0
  60. package/src/config-schema/types.ts +95 -0
  61. package/src/index.ts +1286 -0
  62. package/src/package-meta.ts +200 -0
  63. package/src/plugin-compatibility.ts +117 -0
  64. package/src/profile-compatibility.ts +141 -0
  65. package/src/profile-context.ts +85 -0
  66. package/src/profile-plugins.ts +133 -0
  67. package/src/profile-resolution/legacy-links.ts +33 -0
  68. package/src/profile-resolution/resolver.ts +1072 -0
  69. package/src/profile-resolution/service.ts +148 -0
  70. package/src/profile-resolution/worker-bootstrap.ts +12 -0
  71. package/src/profile-sanitize.ts +34 -0
  72. package/src/profile.ts +762 -0
  73. package/src/startup-diagnostics.ts +104 -0
@@ -0,0 +1,148 @@
1
+ /** Package metadata resolved through one runtime interception. */
2
+
3
+ import { existsSync, readFileSync } from 'node:fs'
4
+ import { createRequire } from 'node:module'
5
+ import { join } from 'node:path'
6
+ import { Service, type Context } from '@deepseek-ai/cordis'
7
+ import type { PluginLocalizedMeta } from '@deepseek-ai/dsh-package-manifest'
8
+ import {
9
+ barePackageName,
10
+ installRuntimeInterception,
11
+ registerWorkerResolution,
12
+ type RuntimeInterception,
13
+ } from './resolver.ts'
14
+ import { ProfileRuntimeResolution, type RuntimeResolution } from '../profile.ts'
15
+ import { readPluginMeta } from '../package-meta.ts'
16
+
17
+ declare module '@deepseek-ai/cordis' {
18
+ interface Context {
19
+ /** Deterministic package lookup for configured plugin specifiers. */
20
+ pluginPackages: PluginPackages
21
+ }
22
+ }
23
+
24
+ /** The package that owns a resolved module. */
25
+ export interface PluginPackage {
26
+ /** Manifest package name. */
27
+ name: string
28
+ /** Manifest version when declared. */
29
+ version: string | undefined
30
+ /** Absolute package directory. */
31
+ dir: string
32
+ /** Absolute package.json path. */
33
+ manifestPath: string
34
+ /** Parsed manifest shared by metadata readers. */
35
+ manifest: Record<string, unknown>
36
+ }
37
+
38
+ /** Optional runtime interception installed and owned by {@link PluginPackages}. */
39
+ export interface PluginPackagesConfig {
40
+ /** Complete package table; omit it to expose native package lookup only. */
41
+ resolution?: RuntimeResolution
42
+ }
43
+
44
+ function readPackage(dir: string, fallbackName: string): PluginPackage | undefined {
45
+ const manifestPath = join(dir, 'package.json')
46
+ if (!existsSync(manifestPath)) return undefined
47
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as Record<string, unknown>
48
+ const name = manifest.name
49
+ const version = manifest.version
50
+ return {
51
+ name: typeof name === 'string' ? name : fallbackName,
52
+ version: typeof version === 'string' ? version : undefined,
53
+ dir,
54
+ manifestPath,
55
+ manifest,
56
+ }
57
+ }
58
+
59
+ /** Package lookup shared by metadata consumers in one profile process. */
60
+ export class PluginPackages extends Service {
61
+ private packages = new Map<string, PluginPackage | undefined>()
62
+ private readonly interception: RuntimeInterception | undefined
63
+ private disposeWorkerResolution: (() => void) | undefined
64
+ private current: RuntimeResolution | undefined
65
+
66
+ constructor(ctx: Context, config: PluginPackagesConfig = {}) {
67
+ super(ctx, 'pluginPackages')
68
+ if (config.resolution === undefined) return
69
+ this.current = config.resolution
70
+ const interception = installRuntimeInterception(config.resolution)
71
+ this.disposeWorkerResolution = registerWorkerResolution(config.resolution)
72
+ this.interception = interception
73
+ ctx.effect(() => () => {
74
+ this.disposeWorkerResolution?.()
75
+ interception.dispose()
76
+ }, 'profile package resolution')
77
+ }
78
+
79
+ /** The published runtime resolution, or `undefined` when Node resolves packages alone. */
80
+ get resolution(): RuntimeResolution | undefined {
81
+ return this.current
82
+ }
83
+
84
+ /**
85
+ * Publish a complete successor generation for this process and subsequently created Workers.
86
+ * Profile mappings and local package names may be removed after their plugins stop; linked roots may also be removed.
87
+ * Retained profile mappings may change their declarer and version, but not their normalized directory or scope;
88
+ * installation mappings must remain unchanged. Publication does not unload modules or clear Node caches.
89
+ * @param successor - fully constructed generation accepted by {@link RuntimeInterception.replace}.
90
+ */
91
+ replace(successor: RuntimeResolution): void {
92
+ if (this.interception === undefined) throw new Error('plugin-packages: runtime resolution is not installed')
93
+ this.interception.replace(successor)
94
+ this.current = successor
95
+ this.packages = new Map()
96
+ this.disposeWorkerResolution?.()
97
+ this.disposeWorkerResolution = registerWorkerResolution(successor)
98
+ }
99
+
100
+ /**
101
+ * Publish the latest generation computed by the installed resolution through {@link replace}. Package contents
102
+ * and loaded modules are not reloaded.
103
+ * @throws when no resolution is installed, it is plain data rather than a {@link ProfileRuntimeResolution},
104
+ * reading the latest files fails, or the successor is rejected. A computed resolution without a profile can refresh.
105
+ */
106
+ async refresh(): Promise<void> {
107
+ if (!(this.current instanceof ProfileRuntimeResolution)) {
108
+ throw new Error('plugin-packages: the installed runtime resolution cannot be recomputed')
109
+ }
110
+ this.replace(await this.current.computeLatestResolution())
111
+ }
112
+
113
+ /**
114
+ * Locate the package named by a specifier without requiring a package export.
115
+ * @param specifier - module specifier whose package owns the requested module.
116
+ * @param parentURL - URL whose Node lookup order applies.
117
+ * @returns the parsed package, or undefined when no package owns the request.
118
+ */
119
+ packageOf(specifier: string, parentURL: string): PluginPackage | undefined {
120
+ const name = barePackageName(specifier)
121
+ if (name === undefined) return undefined
122
+ const dir = this.interception === undefined
123
+ ? packageDirFromParent(name, parentURL)
124
+ : this.interception.packageDir(name, parentURL)
125
+ if (dir === undefined) return undefined
126
+ const key = JSON.stringify({ dir, name })
127
+ if (!this.packages.has(key)) this.packages.set(key, readPackage(dir, name))
128
+ return this.packages.get(key)
129
+ }
130
+
131
+ /**
132
+ * Read display metadata without loading or activating the target plugin.
133
+ * @param specifier - configured package module, including package subpaths.
134
+ * @param parentURL - owning Loader tree's resolution base.
135
+ * @returns local display metadata or its diagnostic; undefined for non-package requests or absent metadata.
136
+ */
137
+ metaOf(specifier: string, parentURL: string): PluginLocalizedMeta | undefined {
138
+ return readPluginMeta(specifier, parentURL)
139
+ }
140
+ }
141
+
142
+ function packageDirFromParent(name: string, parentURL: string): string | undefined {
143
+ for (const searchPath of createRequire(parentURL).resolve.paths(name) as string[]) {
144
+ const candidate = join(searchPath, name)
145
+ if (existsSync(join(candidate, 'package.json'))) return candidate
146
+ }
147
+ return undefined
148
+ }
@@ -0,0 +1,12 @@
1
+ /** Install the inherited runtime resolution in one Harness-owned Worker. */
2
+
3
+ import { getEnvironmentData } from 'node:worker_threads'
4
+ import { installRuntimeInterception } from './resolver.ts'
5
+ import type { RuntimeResolution } from '../profile.ts'
6
+
7
+ const registration = getEnvironmentData(
8
+ '@deepseek-ai/dsh-app-boot/profile-resolution',
9
+ ) as {
10
+ resolution: RuntimeResolution
11
+ } | undefined
12
+ if (registration !== undefined) installRuntimeInterception(registration.resolution)
@@ -0,0 +1,34 @@
1
+ /** Filesystem recovery for callers that own profile shutdown and write exclusion. */
2
+
3
+ import { existsSync, renameSync } from 'node:fs'
4
+ import { join } from 'node:path'
5
+ import { PROFILE_PATCH_FILENAME, readProfileManifest } from './profile.ts'
6
+ import { writeProfileBundles } from './profile-plugins.ts'
7
+
8
+ /**
9
+ * Back up the profile patch and retain only the caller's recovery bundles.
10
+ * The caller must stop the profile and exclude concurrent profile writes.
11
+ * Installed packages and other manifest fields are preserved; patches are never parsed.
12
+ * Failures propagate and may leave completed changes in place for a retry.
13
+ * @param binName - Diagnostic prefix for invalid profile manifests.
14
+ * @param profileDir - Profile directory to recover without loading its plugins.
15
+ * @param bundles - Ordered bundles to enable after recovery.
16
+ * @returns Backup path with a Unix millisecond timestamp and optional collision ordinal, or undefined if absent.
17
+ */
18
+ export function sanitizeProfile(binName: string, profileDir: string, bundles: readonly string[]): string | undefined {
19
+ const manifest = existsSync(join(profileDir, 'package.json')) ? readProfileManifest(binName, profileDir) : undefined
20
+ const patchPath = join(profileDir, PROFILE_PATCH_FILENAME)
21
+ const backupBase = `${patchPath}.bak-${Date.now()}`
22
+ let backupPath: string | undefined = backupBase
23
+ let ordinal = 0
24
+ // Caller-owned write exclusion keeps the selected destination absent until rename.
25
+ while (existsSync(backupPath)) backupPath = `${backupBase}-${++ordinal}`
26
+ try {
27
+ renameSync(patchPath, backupPath)
28
+ } catch (error) {
29
+ if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) throw error
30
+ backupPath = undefined
31
+ }
32
+ if (manifest !== undefined) writeProfileBundles(profileDir, manifest, bundles)
33
+ return backupPath
34
+ }