@nocobase/app-plugin-foundation 0.0.0-stage → 0.1.0-beta.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.
Files changed (220) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +86 -2
  3. package/dist/cli/index.d.ts +4 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +16 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/cli/module-manifest.d.ts +9 -0
  8. package/dist/cli/module-manifest.d.ts.map +1 -0
  9. package/dist/cli/module-manifest.js +12 -0
  10. package/dist/cli/module-manifest.js.map +1 -0
  11. package/dist/client/api.d.ts +17 -0
  12. package/dist/client/api.d.ts.map +1 -0
  13. package/dist/client/api.js +51 -0
  14. package/dist/client/api.js.map +1 -0
  15. package/dist/client/business-modules.d.ts +5 -0
  16. package/dist/client/business-modules.d.ts.map +1 -0
  17. package/dist/client/business-modules.js +81 -0
  18. package/dist/client/business-modules.js.map +1 -0
  19. package/dist/client/components/capability-icons.d.ts +4 -0
  20. package/dist/client/components/capability-icons.d.ts.map +1 -0
  21. package/dist/client/components/capability-icons.js +17 -0
  22. package/dist/client/components/capability-icons.js.map +1 -0
  23. package/dist/client/components/format.d.ts +7 -0
  24. package/dist/client/components/format.d.ts.map +1 -0
  25. package/dist/client/components/format.js +42 -0
  26. package/dist/client/components/format.js.map +1 -0
  27. package/dist/client/components/foundation-page.d.ts +15 -0
  28. package/dist/client/components/foundation-page.d.ts.map +1 -0
  29. package/dist/client/components/foundation-page.js +207 -0
  30. package/dist/client/components/foundation-page.js.map +1 -0
  31. package/dist/client/components/foundation-translation.d.ts +12 -0
  32. package/dist/client/components/foundation-translation.d.ts.map +1 -0
  33. package/dist/client/components/foundation-translation.js +19 -0
  34. package/dist/client/components/foundation-translation.js.map +1 -0
  35. package/dist/client/components/module-matrix.d.ts +9 -0
  36. package/dist/client/components/module-matrix.d.ts.map +1 -0
  37. package/dist/client/components/module-matrix.js +56 -0
  38. package/dist/client/components/module-matrix.js.map +1 -0
  39. package/dist/client/components/ui/button.d.ts +8 -0
  40. package/dist/client/components/ui/button.d.ts.map +1 -0
  41. package/dist/client/components/ui/button.js +35 -0
  42. package/dist/client/components/ui/button.js.map +1 -0
  43. package/dist/client/components/ui/collapsible.d.ts +7 -0
  44. package/dist/client/components/ui/collapsible.d.ts.map +1 -0
  45. package/dist/client/components/ui/collapsible.js +13 -0
  46. package/dist/client/components/ui/collapsible.js.map +1 -0
  47. package/dist/client/components/ui/table.d.ts +11 -0
  48. package/dist/client/components/ui/table.d.ts.map +1 -0
  49. package/dist/client/components/ui/table.js +28 -0
  50. package/dist/client/components/ui/table.js.map +1 -0
  51. package/dist/client/index.d.ts +6 -0
  52. package/dist/client/index.d.ts.map +1 -0
  53. package/dist/client/index.js +7 -0
  54. package/dist/client/index.js.map +1 -0
  55. package/dist/client/locales/de-DE.d.ts +4 -0
  56. package/dist/client/locales/de-DE.d.ts.map +1 -0
  57. package/dist/client/locales/de-DE.js +315 -0
  58. package/dist/client/locales/de-DE.js.map +1 -0
  59. package/dist/client/locales/en-US.d.ts +317 -0
  60. package/dist/client/locales/en-US.d.ts.map +1 -0
  61. package/dist/client/locales/en-US.js +315 -0
  62. package/dist/client/locales/en-US.js.map +1 -0
  63. package/dist/client/locales/es-ES.d.ts +4 -0
  64. package/dist/client/locales/es-ES.d.ts.map +1 -0
  65. package/dist/client/locales/es-ES.js +316 -0
  66. package/dist/client/locales/es-ES.js.map +1 -0
  67. package/dist/client/locales/fr-FR.d.ts +4 -0
  68. package/dist/client/locales/fr-FR.d.ts.map +1 -0
  69. package/dist/client/locales/fr-FR.js +316 -0
  70. package/dist/client/locales/fr-FR.js.map +1 -0
  71. package/dist/client/locales/id-ID.d.ts +4 -0
  72. package/dist/client/locales/id-ID.d.ts.map +1 -0
  73. package/dist/client/locales/id-ID.js +315 -0
  74. package/dist/client/locales/id-ID.js.map +1 -0
  75. package/dist/client/locales/index.d.ts +4 -0
  76. package/dist/client/locales/index.d.ts.map +1 -0
  77. package/dist/client/locales/index.js +14 -0
  78. package/dist/client/locales/index.js.map +1 -0
  79. package/dist/client/locales/ja-JP.d.ts +4 -0
  80. package/dist/client/locales/ja-JP.d.ts.map +1 -0
  81. package/dist/client/locales/ja-JP.js +315 -0
  82. package/dist/client/locales/ja-JP.js.map +1 -0
  83. package/dist/client/locales/pt-BR.d.ts +4 -0
  84. package/dist/client/locales/pt-BR.d.ts.map +1 -0
  85. package/dist/client/locales/pt-BR.js +316 -0
  86. package/dist/client/locales/pt-BR.js.map +1 -0
  87. package/dist/client/locales/vi-VN.d.ts +4 -0
  88. package/dist/client/locales/vi-VN.d.ts.map +1 -0
  89. package/dist/client/locales/vi-VN.js +315 -0
  90. package/dist/client/locales/vi-VN.js.map +1 -0
  91. package/dist/client/locales/zh-CN.d.ts +4 -0
  92. package/dist/client/locales/zh-CN.d.ts.map +1 -0
  93. package/dist/client/locales/zh-CN.js +315 -0
  94. package/dist/client/locales/zh-CN.js.map +1 -0
  95. package/dist/client/locales/zh-TW.d.ts +4 -0
  96. package/dist/client/locales/zh-TW.d.ts.map +1 -0
  97. package/dist/client/locales/zh-TW.js +315 -0
  98. package/dist/client/locales/zh-TW.js.map +1 -0
  99. package/dist/client/navigation.d.ts +8 -0
  100. package/dist/client/navigation.d.ts.map +1 -0
  101. package/dist/client/navigation.js +21 -0
  102. package/dist/client/navigation.js.map +1 -0
  103. package/dist/client/plugin.d.ts +11 -0
  104. package/dist/client/plugin.d.ts.map +1 -0
  105. package/dist/client/plugin.js +10 -0
  106. package/dist/client/plugin.js.map +1 -0
  107. package/dist/client/provider-inventory.d.ts +6 -0
  108. package/dist/client/provider-inventory.d.ts.map +1 -0
  109. package/dist/client/provider-inventory.js +49 -0
  110. package/dist/client/provider-inventory.js.map +1 -0
  111. package/dist/client/routes.d.ts +18 -0
  112. package/dist/client/routes.d.ts.map +1 -0
  113. package/dist/client/routes.js +41 -0
  114. package/dist/client/routes.js.map +1 -0
  115. package/dist/package.d.ts +2 -0
  116. package/dist/package.d.ts.map +1 -0
  117. package/dist/package.js +2 -0
  118. package/dist/package.js.map +1 -0
  119. package/dist/server/capabilities/data.d.ts +7 -0
  120. package/dist/server/capabilities/data.d.ts.map +1 -0
  121. package/dist/server/capabilities/data.js +108 -0
  122. package/dist/server/capabilities/data.js.map +1 -0
  123. package/dist/server/capabilities/index.d.ts +5 -0
  124. package/dist/server/capabilities/index.d.ts.map +1 -0
  125. package/dist/server/capabilities/index.js +24 -0
  126. package/dist/server/capabilities/index.js.map +1 -0
  127. package/dist/server/capabilities/intelligence.d.ts +4 -0
  128. package/dist/server/capabilities/intelligence.d.ts.map +1 -0
  129. package/dist/server/capabilities/intelligence.js +97 -0
  130. package/dist/server/capabilities/intelligence.js.map +1 -0
  131. package/dist/server/capabilities/messaging.d.ts +4 -0
  132. package/dist/server/capabilities/messaging.d.ts.map +1 -0
  133. package/dist/server/capabilities/messaging.js +108 -0
  134. package/dist/server/capabilities/messaging.js.map +1 -0
  135. package/dist/server/capabilities/people.d.ts +4 -0
  136. package/dist/server/capabilities/people.d.ts.map +1 -0
  137. package/dist/server/capabilities/people.js +151 -0
  138. package/dist/server/capabilities/people.js.map +1 -0
  139. package/dist/server/capabilities/platform.d.ts +10 -0
  140. package/dist/server/capabilities/platform.d.ts.map +1 -0
  141. package/dist/server/capabilities/platform.js +143 -0
  142. package/dist/server/capabilities/platform.js.map +1 -0
  143. package/dist/server/capabilities/processes.d.ts +3 -0
  144. package/dist/server/capabilities/processes.d.ts.map +1 -0
  145. package/dist/server/capabilities/processes.js +86 -0
  146. package/dist/server/capabilities/processes.js.map +1 -0
  147. package/dist/server/capabilities/types.d.ts +38 -0
  148. package/dist/server/capabilities/types.d.ts.map +1 -0
  149. package/dist/server/capabilities/types.js +23 -0
  150. package/dist/server/capabilities/types.js.map +1 -0
  151. package/dist/server/catalog.d.ts +3 -0
  152. package/dist/server/catalog.d.ts.map +1 -0
  153. package/dist/server/catalog.js +44 -0
  154. package/dist/server/catalog.js.map +1 -0
  155. package/dist/server/index.d.ts +6 -0
  156. package/dist/server/index.d.ts.map +1 -0
  157. package/dist/server/index.js +5 -0
  158. package/dist/server/index.js.map +1 -0
  159. package/dist/server/inventory.d.ts +48 -0
  160. package/dist/server/inventory.d.ts.map +1 -0
  161. package/dist/server/inventory.js +158 -0
  162. package/dist/server/inventory.js.map +1 -0
  163. package/dist/server/module-manifest.d.ts +10 -0
  164. package/dist/server/module-manifest.d.ts.map +1 -0
  165. package/dist/server/module-manifest.js +65 -0
  166. package/dist/server/module-manifest.js.map +1 -0
  167. package/dist/server/module-sources.d.ts +5 -0
  168. package/dist/server/module-sources.d.ts.map +1 -0
  169. package/dist/server/module-sources.js +535 -0
  170. package/dist/server/module-sources.js.map +1 -0
  171. package/dist/server/packages.d.ts +18 -0
  172. package/dist/server/packages.d.ts.map +1 -0
  173. package/dist/server/packages.js +62 -0
  174. package/dist/server/packages.js.map +1 -0
  175. package/dist/server/plugin.d.ts +4 -0
  176. package/dist/server/plugin.d.ts.map +1 -0
  177. package/dist/server/plugin.js +12 -0
  178. package/dist/server/plugin.js.map +1 -0
  179. package/dist/server/provider-inventory.d.ts +13 -0
  180. package/dist/server/provider-inventory.d.ts.map +1 -0
  181. package/dist/server/provider-inventory.js +180 -0
  182. package/dist/server/provider-inventory.js.map +1 -0
  183. package/dist/server/providers/foundation.d.ts +7 -0
  184. package/dist/server/providers/foundation.d.ts.map +1 -0
  185. package/dist/server/providers/foundation.js +24 -0
  186. package/dist/server/providers/foundation.js.map +1 -0
  187. package/dist/server/providers/index.d.ts +4 -0
  188. package/dist/server/providers/index.d.ts.map +1 -0
  189. package/dist/server/providers/index.js +6 -0
  190. package/dist/server/providers/index.js.map +1 -0
  191. package/dist/server/routes/index.d.ts +6 -0
  192. package/dist/server/routes/index.d.ts.map +1 -0
  193. package/dist/server/routes/index.js +54 -0
  194. package/dist/server/routes/index.js.map +1 -0
  195. package/dist/server/routes/schemas.d.ts +7 -0
  196. package/dist/server/routes/schemas.d.ts.map +1 -0
  197. package/dist/server/routes/schemas.js +138 -0
  198. package/dist/server/routes/schemas.js.map +1 -0
  199. package/dist/server/services/foundation.d.ts +37 -0
  200. package/dist/server/services/foundation.d.ts.map +1 -0
  201. package/dist/server/services/foundation.js +175 -0
  202. package/dist/server/services/foundation.js.map +1 -0
  203. package/dist/server/source-evidence.d.ts +19 -0
  204. package/dist/server/source-evidence.d.ts.map +1 -0
  205. package/dist/server/source-evidence.js +169 -0
  206. package/dist/server/source-evidence.js.map +1 -0
  207. package/dist/server/tokens.d.ts +12 -0
  208. package/dist/server/tokens.d.ts.map +1 -0
  209. package/dist/server/tokens.js +3 -0
  210. package/dist/server/tokens.js.map +1 -0
  211. package/dist/shared/available-capabilities.d.ts +6 -0
  212. package/dist/shared/available-capabilities.d.ts.map +1 -0
  213. package/dist/shared/available-capabilities.js +66 -0
  214. package/dist/shared/available-capabilities.js.map +1 -0
  215. package/dist/shared/foundation.d.ts +142 -0
  216. package/dist/shared/foundation.d.ts.map +1 -0
  217. package/dist/shared/foundation.js +39 -0
  218. package/dist/shared/foundation.js.map +1 -0
  219. package/package.json +106 -3
  220. package/skills/nocobase-app-plugin-foundation/SKILL.md +68 -0
@@ -0,0 +1,10 @@
1
+ import type { FoundationModuleSources } from '../shared/foundation.js';
2
+ export declare const MODULE_MANIFEST_FILE: string;
3
+ /** Deployment metadata contains association evidence only, never source text or filesystem locations. */
4
+ export declare function readModuleManifest(rootDir: string, packageName: string): Promise<FoundationModuleSources | undefined>;
5
+ /** Repeat builds converge on the same bytes; replace the artifact only after a complete write. */
6
+ export declare function writeModuleManifest(rootDir: string): Promise<{
7
+ readonly routes: number;
8
+ readonly complete: boolean;
9
+ }>;
10
+ //# sourceMappingURL=module-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-manifest.d.ts","sourceRoot":"","sources":["../../server/module-manifest.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE,eAAO,MAAM,oBAAoB,EAAE,MAAkC,CAAC;AA0BtE,yGAAyG;AACzG,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC,CAmB9C;AAED,kGAAkG;AAClG,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAqBlE"}
@@ -0,0 +1,65 @@
1
+ import { mkdir, readFile, rename, stat, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { z } from 'zod';
4
+ export const MODULE_MANIFEST_FILE = 'foundation-modules.json';
5
+ const manifestSchema = z
6
+ .object({
7
+ schemaVersion: z.literal(1),
8
+ packageName: z.string(),
9
+ sources: z
10
+ .object({
11
+ complete: z.boolean(),
12
+ routes: z
13
+ .array(z
14
+ .object({
15
+ packageName: z.string(),
16
+ routeName: z.string(),
17
+ providers: z.array(z.string()),
18
+ apiProviders: z.array(z.string()).optional(),
19
+ complete: z.boolean(),
20
+ })
21
+ .strict())
22
+ .max(10000),
23
+ })
24
+ .strict(),
25
+ })
26
+ .strict();
27
+ /** Deployment metadata contains association evidence only, never source text or filesystem locations. */
28
+ export async function readModuleManifest(rootDir, packageName) {
29
+ const file = path.join(rootDir, 'server', MODULE_MANIFEST_FILE);
30
+ try {
31
+ if ((await stat(file)).size > 5_000_000)
32
+ return undefined;
33
+ const parsed = manifestSchema.safeParse(JSON.parse(await readFile(file, 'utf8')));
34
+ if (!parsed.success ||
35
+ parsed.data.packageName !== packageName ||
36
+ parsed.data.sources.routes.some((route) => route.packageName !== packageName))
37
+ return undefined;
38
+ return parsed.data.sources;
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ }
44
+ /** Repeat builds converge on the same bytes; replace the artifact only after a complete write. */
45
+ export async function writeModuleManifest(rootDir) {
46
+ const { collectModuleSources } = await import('./module-sources.js');
47
+ const manifest = JSON.parse(await readFile(path.join(rootDir, 'package.json'), 'utf8'));
48
+ const sources = await collectModuleSources(rootDir);
49
+ const document = manifestSchema.parse({
50
+ schemaVersion: 1,
51
+ packageName: manifest.name,
52
+ sources,
53
+ });
54
+ const file = path.join(rootDir, 'dist', 'server', MODULE_MANIFEST_FILE);
55
+ const contents = JSON.stringify(document) + '\n';
56
+ await mkdir(path.dirname(file), { recursive: true });
57
+ const previous = await readFile(file, 'utf8').catch(() => undefined);
58
+ if (previous !== contents) {
59
+ const temporary = `${file}.${process.pid}.tmp`;
60
+ await writeFile(temporary, contents);
61
+ await rename(temporary, file);
62
+ }
63
+ return { routes: sources.routes.length, complete: sources.complete };
64
+ }
65
+ //# sourceMappingURL=module-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-manifest.js","sourceRoot":"","sources":["../../server/module-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { FoundationModuleSources } from '../shared/foundation.js';
2
+ import type { RuntimeProviderEvidence } from './provider-inventory.js';
3
+ /** Reads static declarations and value imports only. Never imports an application module or invokes a loader. */
4
+ export declare function collectModuleSources(rootDir: string, runtime?: readonly RuntimeProviderEvidence[]): Promise<FoundationModuleSources>;
5
+ //# sourceMappingURL=module-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-sources.d.ts","sourceRoot":"","sources":["../../server/module-sources.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,uBAAuB,EACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAmBvE,iHAAiH;AACjH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,SAAS,uBAAuB,EAAO,GAC/C,OAAO,CAAC,uBAAuB,CAAC,CAgGlC"}
@@ -0,0 +1,535 @@
1
+ import { readFile, realpath, stat } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import ts from 'typescript';
4
+ import { SourceEvidence } from './source-evidence.js';
5
+ import { readModuleManifest } from './module-manifest.js';
6
+ const MAX_FILES = 400;
7
+ const MAX_BYTES = 512_000;
8
+ const SOURCE_SUFFIXES = ['.ts', '.tsx', '.js', '.jsx', '.mjs'];
9
+ /** Reads static declarations and value imports only. Never imports an application module or invokes a loader. */
10
+ export async function collectModuleSources(rootDir, runtime = []) {
11
+ const routes = [];
12
+ let complete = true;
13
+ const roots = new Map([['', rootDir]]);
14
+ let overridden = new Set();
15
+ for (const entry of runtime)
16
+ if (entry.rootDir)
17
+ roots.set(entry.name, entry.rootDir);
18
+ for (const [name, directory] of roots) {
19
+ if (!directory || name === '@nocobase/app-plugin-foundation')
20
+ continue;
21
+ try {
22
+ const graph = await SourceGraph.create(directory);
23
+ if (!name)
24
+ overridden = await graph.overrides();
25
+ const packageName = name || graph.packageName;
26
+ if (!packageName) {
27
+ complete = false;
28
+ continue;
29
+ }
30
+ const entry = await graph.entry();
31
+ if (!entry) {
32
+ const built = !name
33
+ ? await readModuleManifest(directory, packageName)
34
+ : undefined;
35
+ if (built) {
36
+ routes.push(...built.routes);
37
+ complete &&= built.complete;
38
+ }
39
+ else
40
+ complete = false;
41
+ continue;
42
+ }
43
+ const declarationFiles = await graph.walk(entry, false);
44
+ await graph.prepare(entry);
45
+ const endpoints = await graph.apiDependencies();
46
+ for (const source of declarationFiles.sources) {
47
+ const declarations = [];
48
+ const visit = (node) => {
49
+ if (ts.isObjectLiteralExpression(node)) {
50
+ const routeName = property(node, 'name');
51
+ const loader = property(node, 'componentLoader');
52
+ if (routeName && ts.isStringLiteralLike(routeName) && loader)
53
+ declarations.push({ name: routeName.text, loader });
54
+ }
55
+ ts.forEachChild(node, visit);
56
+ };
57
+ visit(source.ast);
58
+ for (const declaration of declarations) {
59
+ if (overridden.has(`${packageName}:${declaration.name}`)) {
60
+ routes.push({
61
+ packageName,
62
+ routeName: declaration.name,
63
+ providers: [],
64
+ complete: false,
65
+ });
66
+ continue;
67
+ }
68
+ const imports = importsIn(declaration.loader);
69
+ const providers = new Set();
70
+ const apiProviders = new Set();
71
+ let routeComplete = declarationFiles.complete && imports.length > 0;
72
+ for (const specifier of imports) {
73
+ const target = await graph.local(source.file, specifier);
74
+ if (!target) {
75
+ if (!isLocal(specifier))
76
+ addProvider(providers, specifier);
77
+ else
78
+ routeComplete = false;
79
+ continue;
80
+ }
81
+ const page = graph.evidence(target);
82
+ for (const provider of page.providers)
83
+ providers.add(provider);
84
+ for (const file of page.sources) {
85
+ for (const request of graph.requests(file)) {
86
+ for (const endpoint of endpoints)
87
+ if (request.method === endpoint.method &&
88
+ sameApiPath(request.path, endpoint.path))
89
+ for (const provider of endpoint.providers)
90
+ apiProviders.add(provider);
91
+ }
92
+ }
93
+ routeComplete &&= page.complete;
94
+ }
95
+ routes.push({
96
+ packageName,
97
+ routeName: declaration.name,
98
+ providers: [...providers].sort(),
99
+ ...(apiProviders.size
100
+ ? { apiProviders: [...apiProviders].sort() }
101
+ : {}),
102
+ complete: routeComplete,
103
+ });
104
+ }
105
+ }
106
+ complete &&= declarationFiles.complete;
107
+ }
108
+ catch {
109
+ complete = false;
110
+ }
111
+ }
112
+ return { routes, complete };
113
+ }
114
+ /** A bounded package-local graph; symlinks, aliases and relative paths cannot escape the owning package. */
115
+ class SourceGraph {
116
+ root;
117
+ packageName;
118
+ aliases;
119
+ files = new Map();
120
+ analysis;
121
+ constructor(root, packageName, aliases) {
122
+ this.root = root;
123
+ this.packageName = packageName;
124
+ this.aliases = aliases;
125
+ }
126
+ static async create(root) {
127
+ const directory = await realpath(root);
128
+ const manifest = JSON.parse(await readFile(path.join(directory, 'package.json'), 'utf8'));
129
+ const record = isRecord(manifest) ? manifest : {};
130
+ return new SourceGraph(directory, typeof record.name === 'string' ? record.name : '', isRecord(record.imports) ? record.imports : {});
131
+ }
132
+ async resolve(base) {
133
+ if (!inside(this.root, path.resolve(base)))
134
+ return undefined;
135
+ const extension = path.extname(base);
136
+ const stem = SOURCE_SUFFIXES.includes(extension)
137
+ ? base.slice(0, -extension.length)
138
+ : base;
139
+ for (const candidate of [
140
+ ...SOURCE_SUFFIXES.map((suffix) => stem + suffix),
141
+ base,
142
+ ...SOURCE_SUFFIXES.map((suffix) => path.join(base, 'index' + suffix)),
143
+ ]) {
144
+ try {
145
+ const file = await realpath(candidate);
146
+ if (!SOURCE_SUFFIXES.includes(path.extname(file)) ||
147
+ !inside(this.root, file) ||
148
+ path
149
+ .relative(this.root, file)
150
+ .split(path.sep)
151
+ .includes('node_modules'))
152
+ continue;
153
+ if ((await stat(file)).isFile())
154
+ return file;
155
+ }
156
+ catch {
157
+ /* Missing alternatives are expected when resolving compiled .js references to .tsx sources. */
158
+ }
159
+ }
160
+ return undefined;
161
+ }
162
+ async entry() {
163
+ return ((await this.resolve(path.join(this.root, 'client/routes.js'))) ??
164
+ (await this.resolve(path.join(this.root, 'dist/client/routes.js'))));
165
+ }
166
+ async prepare(entry) {
167
+ await this.walk(entry, true);
168
+ const server = (await this.resolve(path.join(this.root, 'server/routes/index.js'))) ??
169
+ (await this.resolve(path.join(this.root, 'server/routes.js')));
170
+ if (server)
171
+ await this.walk(server, false);
172
+ const resolutions = new Map();
173
+ for (const source of this.files.values()) {
174
+ if (!source)
175
+ continue;
176
+ for (const specifier of [
177
+ ...source.imports,
178
+ ...dynamicImports(source.ast),
179
+ ]) {
180
+ const target = await this.local(source.file, specifier);
181
+ if (target)
182
+ resolutions.set(`${source.file}\0${specifier}`, target);
183
+ }
184
+ }
185
+ this.analysis = new SourceEvidence([...this.files.values()].filter((source) => Boolean(source)), resolutions);
186
+ }
187
+ evidence(file, roots) {
188
+ const slice = this.analysis?.collect(file, roots);
189
+ const providers = new Set();
190
+ for (const reference of slice?.references ?? [])
191
+ addProvider(providers, reference);
192
+ return {
193
+ providers,
194
+ complete: slice?.complete ?? false,
195
+ sources: [...(slice?.nodes ?? [])].flatMap(([name, nodes]) => {
196
+ const source = this.files.get(name);
197
+ return source ? [{ ...source, nodes }] : [];
198
+ }),
199
+ };
200
+ }
201
+ requests(source) {
202
+ return requestPaths(source.ast, source.nodes, (node) => this.analysis?.symbol(node));
203
+ }
204
+ async overrides() {
205
+ const ids = new Set();
206
+ const entry = await this.resolve(path.join(this.root, 'client/route-overrides.js'));
207
+ if (!entry)
208
+ return ids;
209
+ const graph = await this.walk(entry, false);
210
+ for (const source of graph.sources) {
211
+ const visit = (node) => {
212
+ if (ts.isObjectLiteralExpression(node)) {
213
+ const id = property(node, 'routeId');
214
+ if (id && ts.isStringLiteralLike(id))
215
+ ids.add(id.text);
216
+ }
217
+ ts.forEachChild(node, visit);
218
+ };
219
+ visit(source.ast);
220
+ }
221
+ return ids;
222
+ }
223
+ /** Only explicit, root-mounted Hono endpoints and literal API-client requests can establish this bridge. */
224
+ async apiDependencies() {
225
+ const entry = (await this.resolve(path.join(this.root, 'server/routes/index.js'))) ??
226
+ (await this.resolve(path.join(this.root, 'server/routes.js')));
227
+ if (!entry)
228
+ return [];
229
+ const graph = this.evidence(entry);
230
+ const endpoints = [];
231
+ for (const source of graph.sources) {
232
+ if (!source.imports.includes('hono'))
233
+ continue;
234
+ const mounts = new Map();
235
+ const routes = [];
236
+ const reachableScope = (node) => {
237
+ for (let parent = node.parent; parent; parent = parent.parent)
238
+ if (ts.isFunctionLike(parent))
239
+ return source.nodes.has(parent);
240
+ return true;
241
+ };
242
+ const visit = (node) => {
243
+ if (ts.isCallExpression(node) &&
244
+ reachableScope(node) &&
245
+ ts.isPropertyAccessExpression(node.expression)) {
246
+ const first = node.arguments[0];
247
+ if (first && ts.isStringLiteralLike(first)) {
248
+ const method = node.expression.name.text;
249
+ const child = node.arguments[1];
250
+ if (method === 'route' && child && ts.isIdentifier(child))
251
+ mounts.set(child.text, [
252
+ ...(mounts.get(child.text) ?? []),
253
+ first.text,
254
+ ]);
255
+ if (['get', 'post', 'put', 'patch', 'delete'].includes(method) &&
256
+ first.text.startsWith('/') &&
257
+ ts.isIdentifier(node.expression.expression))
258
+ routes.push({
259
+ router: node.expression.expression.text,
260
+ path: first.text,
261
+ method: method.toUpperCase(),
262
+ handlers: node.arguments.slice(1),
263
+ });
264
+ }
265
+ }
266
+ ts.forEachChild(node, visit);
267
+ };
268
+ visit(source.ast);
269
+ if (!routes.length)
270
+ continue;
271
+ for (const route of routes) {
272
+ // Prefixes composed elsewhere, factories and dynamic routing remain unknown rather than guessed.
273
+ if (!(mounts.get(route.router) ?? []).includes('/'))
274
+ continue;
275
+ endpoints.push({
276
+ path: route.path,
277
+ method: route.method,
278
+ providers: [
279
+ ...this.evidence(source.file, route.handlers).providers,
280
+ ].sort(),
281
+ });
282
+ }
283
+ }
284
+ return endpoints;
285
+ }
286
+ async local(from, specifier) {
287
+ if (specifier.startsWith('.'))
288
+ return this.resolve(path.resolve(path.dirname(from), specifier));
289
+ if (!specifier.startsWith('#'))
290
+ return undefined;
291
+ for (const [alias, value] of Object.entries(this.aliases).sort(([a], [b]) => b.length - a.length)) {
292
+ const star = alias.indexOf('*');
293
+ const matches = star < 0
294
+ ? alias === specifier
295
+ : specifier.startsWith(alias.slice(0, star)) &&
296
+ specifier.endsWith(alias.slice(star + 1));
297
+ if (!matches)
298
+ continue;
299
+ const replacement = star < 0
300
+ ? ''
301
+ : specifier.slice(star, specifier.length - (alias.length - star - 1));
302
+ for (const target of targets(value)) {
303
+ if (!target.startsWith('./'))
304
+ continue;
305
+ const resolved = await this.resolve(path.resolve(this.root, target.replace('*', replacement)));
306
+ if (resolved)
307
+ return resolved;
308
+ }
309
+ }
310
+ return undefined;
311
+ }
312
+ async walk(entry, includePages) {
313
+ const pending = [entry];
314
+ const visited = new Set();
315
+ const sources = [];
316
+ const providers = new Set();
317
+ let complete = true;
318
+ while (pending.length) {
319
+ const file = pending.shift();
320
+ if (!file || visited.has(file))
321
+ continue;
322
+ visited.add(file);
323
+ const source = await this.read(file);
324
+ if (!source) {
325
+ complete = false;
326
+ continue;
327
+ }
328
+ sources.push(source);
329
+ for (const specifier of source.imports) {
330
+ if (!isLocal(specifier)) {
331
+ addProvider(providers, specifier);
332
+ continue;
333
+ }
334
+ const target = await this.local(file, specifier);
335
+ if (!target) {
336
+ complete = false;
337
+ continue;
338
+ }
339
+ pending.push(target);
340
+ }
341
+ if (includePages) {
342
+ for (const specifier of dynamicImports(source.ast)) {
343
+ const target = await this.local(file, specifier);
344
+ if (target)
345
+ pending.push(target);
346
+ else if (isLocal(specifier))
347
+ complete = false;
348
+ else
349
+ addProvider(providers, specifier);
350
+ }
351
+ }
352
+ }
353
+ return { sources, providers, complete };
354
+ }
355
+ async read(file) {
356
+ if (this.files.has(file))
357
+ return this.files.get(file);
358
+ if (this.files.size >= MAX_FILES)
359
+ return undefined;
360
+ this.files.set(file, undefined);
361
+ try {
362
+ if ((await stat(file)).size > MAX_BYTES)
363
+ return undefined;
364
+ const ast = ts.createSourceFile(file, await readFile(file, 'utf8'), ts.ScriptTarget.Latest, true);
365
+ const source = { file, ast, imports: staticImports(ast) };
366
+ this.files.set(file, source);
367
+ return source;
368
+ }
369
+ catch {
370
+ return undefined;
371
+ }
372
+ }
373
+ }
374
+ function property(object, name) {
375
+ const entry = object.properties.find((item) => item.name &&
376
+ (ts.isIdentifier(item.name) || ts.isStringLiteralLike(item.name)) &&
377
+ item.name.text === name);
378
+ return entry && ts.isPropertyAssignment(entry)
379
+ ? entry.initializer
380
+ : undefined;
381
+ }
382
+ function staticImports(node) {
383
+ return node.statements.flatMap((statement) => {
384
+ if (ts.isImportDeclaration(statement) &&
385
+ ts.isStringLiteralLike(statement.moduleSpecifier)) {
386
+ const clause = statement.importClause;
387
+ if (clause?.isTypeOnly)
388
+ return [];
389
+ if (clause?.namedBindings &&
390
+ ts.isNamedImports(clause.namedBindings) &&
391
+ !clause.name &&
392
+ clause.namedBindings.elements.length > 0 &&
393
+ clause.namedBindings.elements.every((element) => element.isTypeOnly))
394
+ return [];
395
+ return [statement.moduleSpecifier.text];
396
+ }
397
+ if (ts.isExportDeclaration(statement) &&
398
+ !statement.isTypeOnly &&
399
+ statement.moduleSpecifier &&
400
+ ts.isStringLiteralLike(statement.moduleSpecifier)) {
401
+ if (statement.exportClause &&
402
+ ts.isNamedExports(statement.exportClause) &&
403
+ statement.exportClause.elements.length > 0 &&
404
+ statement.exportClause.elements.every((element) => element.isTypeOnly))
405
+ return [];
406
+ return [statement.moduleSpecifier.text];
407
+ }
408
+ return [];
409
+ });
410
+ }
411
+ function dynamicImports(node) {
412
+ return importsIn(node);
413
+ }
414
+ function requestPaths(source, selected, symbol) {
415
+ const paths = [];
416
+ const clients = new Set();
417
+ const hooks = new Set();
418
+ for (const statement of source.statements) {
419
+ if (!ts.isImportDeclaration(statement) ||
420
+ !ts.isStringLiteralLike(statement.moduleSpecifier) ||
421
+ statement.moduleSpecifier.text !== '@nocobase/app-client')
422
+ continue;
423
+ const bindings = statement.importClause?.namedBindings;
424
+ if (bindings && ts.isNamedImports(bindings))
425
+ for (const binding of bindings.elements)
426
+ if ((binding.propertyName?.text ?? binding.name.text) === 'useApiClient')
427
+ hooks.add(symbol(binding.name));
428
+ }
429
+ const findClients = (node) => {
430
+ if (ts.isVariableDeclaration(node) &&
431
+ ts.isIdentifier(node.name) &&
432
+ node.initializer &&
433
+ ts.isCallExpression(node.initializer) &&
434
+ selected.has(node.initializer) &&
435
+ ts.isIdentifier(node.initializer.expression) &&
436
+ symbol(node.initializer.expression) &&
437
+ hooks.has(symbol(node.initializer.expression)))
438
+ clients.add(symbol(node.name));
439
+ ts.forEachChild(node, findClients);
440
+ };
441
+ findClients(source);
442
+ const visit = (node) => {
443
+ if (ts.isCallExpression(node) &&
444
+ selected.has(node) &&
445
+ ts.isPropertyAccessExpression(node.expression) &&
446
+ node.expression.name.text === 'request' &&
447
+ ts.isIdentifier(node.expression.expression) &&
448
+ symbol(node.expression.expression) &&
449
+ clients.has(symbol(node.expression.expression))) {
450
+ const argument = node.arguments[0];
451
+ const value = argument && ts.isObjectLiteralExpression(argument)
452
+ ? property(argument, 'path')
453
+ : undefined;
454
+ const declaredMethod = argument && ts.isObjectLiteralExpression(argument)
455
+ ? property(argument, 'method')
456
+ : undefined;
457
+ const method = declaredMethod === undefined
458
+ ? 'GET'
459
+ : ts.isStringLiteralLike(declaredMethod)
460
+ ? declaredMethod.text
461
+ : undefined;
462
+ if (!method)
463
+ return;
464
+ if (value && ts.isStringLiteralLike(value))
465
+ paths.push({ path: value.text, method });
466
+ if (value && ts.isTemplateExpression(value))
467
+ paths.push({
468
+ method,
469
+ path: value.head.text +
470
+ value.templateSpans
471
+ .map((span) => ':dynamic' + span.literal.text)
472
+ .join(''),
473
+ });
474
+ }
475
+ ts.forEachChild(node, visit);
476
+ };
477
+ visit(source);
478
+ return paths;
479
+ }
480
+ function sameApiPath(request, endpoint) {
481
+ const segments = (value) => value.split('?')[0].split('/').filter(Boolean);
482
+ const left = segments(request), right = segments(endpoint);
483
+ return (left.length === right.length &&
484
+ left.every((part, index) => right[index]?.startsWith(':') ? part.length > 0 : part === right[index]));
485
+ }
486
+ function importsIn(node) {
487
+ const found = [];
488
+ const visit = (child) => {
489
+ if (ts.isCallExpression(child) &&
490
+ child.expression.kind === ts.SyntaxKind.ImportKeyword &&
491
+ child.arguments[0] &&
492
+ ts.isStringLiteralLike(child.arguments[0]))
493
+ found.push(child.arguments[0].text);
494
+ ts.forEachChild(child, visit);
495
+ };
496
+ visit(node);
497
+ return found;
498
+ }
499
+ function packageOf(specifier) {
500
+ return specifier
501
+ .split('/')
502
+ .slice(0, specifier.startsWith('@') ? 2 : 1)
503
+ .join('/');
504
+ }
505
+ function addProvider(providers, specifier) {
506
+ const name = packageOf(specifier);
507
+ // The server SDK also provides routing and configuration. Those generic imports do not establish use of jobs,
508
+ // queues, secrets or caching merely because the SDK as a whole provides operational facilities.
509
+ if (name === '@nocobase/app-server' &&
510
+ !/^@nocobase\/app-server\/(?:jobs|queue|secrets|caching)(?:\/|$)/.test(specifier))
511
+ return;
512
+ providers.add(name);
513
+ }
514
+ function isLocal(specifier) {
515
+ return (specifier.startsWith('.') ||
516
+ specifier.startsWith('#') ||
517
+ specifier.startsWith('/'));
518
+ }
519
+ function inside(root, file) {
520
+ const relative = path.relative(root, file);
521
+ return (relative !== '..' &&
522
+ !relative.startsWith(`..${path.sep}`) &&
523
+ !path.isAbsolute(relative));
524
+ }
525
+ function isRecord(value) {
526
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
527
+ }
528
+ function targets(value) {
529
+ if (typeof value === 'string')
530
+ return [value];
531
+ if (!isRecord(value))
532
+ return [];
533
+ return [value.development, value.import, value.default].flatMap((item) => targets(item));
534
+ }
535
+ //# sourceMappingURL=module-sources.js.map