@starci/hfs 4.0.10 → 4.1.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 (271) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/emit/operations.mjs +1 -1
  3. package/emit/type-schema.mjs +2 -1
  4. package/lint/run.mjs +3 -2
  5. package/package.json +1 -1
  6. package/report/order.mjs +2 -0
  7. package/runtime/config.example.yaml +187 -0
  8. package/runtime/engine/by-code-unit.mjs +12 -0
  9. package/runtime/engine/config.mjs +221 -239
  10. package/runtime/engine/invalid-config.mjs +5 -5
  11. package/runtime/engine/model-config.mjs +90 -0
  12. package/runtime/engine/orca-config.mjs +3 -1
  13. package/runtime/engine/removed-vocabulary.mjs +28 -0
  14. package/runtime/engine/resources-config.mjs +52 -0
  15. package/runtime/engine/runtime-root.mjs +17 -0
  16. package/runtime/engine/secrets.mjs +35 -31
  17. package/runtime/engine/sonar-config.mjs +21 -0
  18. package/runtime/engine/temp-root.mjs +32 -0
  19. package/runtime/knowledge/hfs/canon-pins.yaml +14 -14
  20. package/runtime/knowledge/hfs/peer-integrations.yaml +2 -3
  21. package/runtime/knowledge/hfs/rules.yaml +98 -65
  22. package/runtime/knowledge/hfs/slots.yaml +11 -14
  23. package/runtime/knowledge/patterns/be/api.yaml +7 -24
  24. package/runtime/knowledge/patterns/be/cli.yaml +3 -12
  25. package/runtime/knowledge/patterns/be/realtime.yaml +1 -14
  26. package/runtime/knowledge/patterns/be/webhooks.yaml +0 -19
  27. package/runtime/modules/kernel/failure-codes.yaml +1 -41
  28. package/runtime/modules/kernel/removed-vocabulary.yaml +168 -0
  29. package/runtime/modules/models/registry.yaml +25 -359
  30. package/runtime/modules/models/runtimes.yaml +92 -256
  31. package/runtime/modules/models/tiers.yaml +65 -0
  32. package/runtime/scripts/api/fs/claim-file.mjs +30 -0
  33. package/runtime/scripts/api/fs/ensure-temp-root.mjs +27 -0
  34. package/runtime/scripts/api/fs/forbidden-root.mjs +2 -1
  35. package/runtime/scripts/api/fs/make-temp-dir.mjs +11 -0
  36. package/runtime/scripts/api/fs/safe-remove.mjs +52 -35
  37. package/runtime/scripts/api/git/lib.mjs +34 -1
  38. package/runtime/scripts/api/process/resolve-real-tool.mjs +14 -8
  39. package/runtime/scripts/api/process/run-program.mjs +2 -1
  40. package/runtime/scripts/api/sops/lib.mjs +198 -120
  41. package/runtime/scripts/hfs/allows.mjs +3 -3
  42. package/runtime/scripts/hfs/architecture/ast-walks.mjs +63 -50
  43. package/runtime/scripts/hfs/architecture/automatic-gates.mjs +96 -0
  44. package/runtime/scripts/hfs/architecture/backend.mjs +261 -175
  45. package/runtime/scripts/hfs/architecture/background-unowned.mjs +69 -34
  46. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +55 -54
  47. package/runtime/scripts/hfs/architecture/clones.mjs +179 -106
  48. package/runtime/scripts/hfs/architecture/config-unread.mjs +5 -22
  49. package/runtime/scripts/hfs/architecture/config.mjs +95 -80
  50. package/runtime/scripts/hfs/architecture/connection-map.mjs +233 -141
  51. package/runtime/scripts/hfs/architecture/constructor-deps.mjs +15 -9
  52. package/runtime/scripts/hfs/architecture/context-coupling.mjs +76 -52
  53. package/runtime/scripts/hfs/architecture/context-map.mjs +215 -141
  54. package/runtime/scripts/hfs/architecture/context-owner.mjs +39 -30
  55. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +31 -17
  56. package/runtime/scripts/hfs/architecture/context-transaction.mjs +38 -25
  57. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +86 -40
  58. package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +342 -0
  59. package/runtime/scripts/hfs/architecture/contracts.mjs +263 -455
  60. package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +43 -34
  61. package/runtime/scripts/hfs/architecture/dead-exports.mjs +201 -154
  62. package/runtime/scripts/hfs/architecture/default-deny.mjs +118 -93
  63. package/runtime/scripts/hfs/architecture/doc-language.mjs +2 -1
  64. package/runtime/scripts/hfs/architecture/entrypoint.mjs +45 -30
  65. package/runtime/scripts/hfs/architecture/error-codes.mjs +19 -12
  66. package/runtime/scripts/hfs/architecture/error-masked.mjs +39 -26
  67. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -1
  68. package/runtime/scripts/hfs/architecture/feature-shape.mjs +26 -20
  69. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
  70. package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +190 -0
  71. package/runtime/scripts/hfs/architecture/frontend-routing.mjs +186 -0
  72. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +311 -0
  73. package/runtime/scripts/hfs/architecture/frontend-world.mjs +328 -0
  74. package/runtime/scripts/hfs/architecture/frontend.mjs +109 -829
  75. package/runtime/scripts/hfs/architecture/hfs-graph.mjs +14 -2
  76. package/runtime/scripts/hfs/architecture/hfs.mjs +120 -288
  77. package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +46 -26
  78. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +114 -76
  79. package/runtime/scripts/hfs/architecture/index.mjs +158 -102
  80. package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +33 -28
  81. package/runtime/scripts/hfs/architecture/machine-ast.mjs +180 -144
  82. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +8 -2
  83. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +150 -101
  84. package/runtime/scripts/hfs/architecture/next-data-contract.mjs +51 -43
  85. package/runtime/scripts/hfs/architecture/next-data-key.mjs +338 -0
  86. package/runtime/scripts/hfs/architecture/next-data.mjs +179 -427
  87. package/runtime/scripts/hfs/architecture/owners.mjs +27 -13
  88. package/runtime/scripts/hfs/architecture/package-shape.mjs +51 -23
  89. package/runtime/scripts/hfs/architecture/presentation.mjs +165 -0
  90. package/runtime/scripts/hfs/architecture/reachability.mjs +127 -61
  91. package/runtime/scripts/hfs/architecture/register-once.mjs +112 -63
  92. package/runtime/scripts/hfs/architecture/registration.mjs +187 -110
  93. package/runtime/scripts/hfs/architecture/required-files.mjs +100 -68
  94. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +83 -58
  95. package/runtime/scripts/hfs/architecture/schema-owner.mjs +182 -108
  96. package/runtime/scripts/hfs/architecture/source-names-shape.mjs +318 -0
  97. package/runtime/scripts/hfs/architecture/source-names.mjs +83 -248
  98. package/runtime/scripts/hfs/architecture/sql-owner.mjs +175 -118
  99. package/runtime/scripts/hfs/architecture/sql-returning.mjs +39 -28
  100. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +302 -172
  101. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +1 -1
  102. package/runtime/scripts/hfs/architecture/supabase-be.mjs +55 -45
  103. package/runtime/scripts/hfs/architecture/supabase-results.mjs +182 -0
  104. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +20 -12
  105. package/runtime/scripts/hfs/architecture/supabase.mjs +191 -263
  106. package/runtime/scripts/hfs/architecture/symbols.mjs +175 -99
  107. package/runtime/scripts/hfs/architecture/test-world-files.mjs +82 -41
  108. package/runtime/scripts/hfs/architecture/tiers.mjs +120 -69
  109. package/runtime/scripts/hfs/architecture/transport-owner.mjs +84 -57
  110. package/runtime/scripts/hfs/architecture/type-context.mjs +282 -0
  111. package/runtime/scripts/hfs/architecture/typescript.mjs +116 -276
  112. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +84 -62
  113. package/runtime/scripts/hfs/architecture.mjs +3 -4
  114. package/runtime/scripts/hfs/check.mjs +61 -70
  115. package/runtime/scripts/hfs/coverage-scope.mjs +84 -38
  116. package/runtime/scripts/hfs/declaration-shape.mjs +77 -36
  117. package/runtime/scripts/hfs/declaration-slots.mjs +1 -1
  118. package/runtime/scripts/hfs/edition-slots.mjs +5 -3
  119. package/runtime/scripts/hfs/linear-text.mjs +30 -0
  120. package/runtime/scripts/hfs/manifest-shape.mjs +164 -63
  121. package/runtime/scripts/hfs/path-findings.mjs +58 -38
  122. package/runtime/scripts/hfs/pin-findings.mjs +31 -0
  123. package/runtime/scripts/hfs/repo-identity.mjs +5 -2
  124. package/runtime/scripts/hfs/rule-catalog.mjs +194 -0
  125. package/runtime/scripts/hfs/rule-params-shape.mjs +33 -27
  126. package/runtime/scripts/hfs/rules/cli.mjs +5 -1
  127. package/runtime/scripts/hfs/rules/contract-compat.mjs +40 -22
  128. package/runtime/scripts/hfs/rules/contract.mjs +35 -27
  129. package/runtime/scripts/hfs/rules/database-config.mjs +57 -32
  130. package/runtime/scripts/hfs/rules/database-migrations.mjs +52 -31
  131. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +106 -91
  132. package/runtime/scripts/hfs/rules/database-sql.mjs +254 -173
  133. package/runtime/scripts/hfs/rules/database.mjs +36 -23
  134. package/runtime/scripts/hfs/rules/deps.mjs +64 -32
  135. package/runtime/scripts/hfs/rules/docker.mjs +113 -65
  136. package/runtime/scripts/hfs/rules/edition.mjs +118 -111
  137. package/runtime/scripts/hfs/rules/event-bus.mjs +91 -45
  138. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +17 -16
  139. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +36 -16
  140. package/runtime/scripts/hfs/rules/frontend-tree.mjs +4 -3
  141. package/runtime/scripts/hfs/rules/integration-specs.mjs +75 -41
  142. package/runtime/scripts/hfs/rules/kinds.mjs +59 -25
  143. package/runtime/scripts/hfs/rules/lint-suppression.mjs +9 -3
  144. package/runtime/scripts/hfs/rules/monorepo.mjs +82 -45
  145. package/runtime/scripts/hfs/rules/peer-integrations.mjs +24 -3
  146. package/runtime/scripts/hfs/rules/pipeline.mjs +58 -9
  147. package/runtime/scripts/hfs/rules/proof-commands.mjs +14 -8
  148. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +11 -6
  149. package/runtime/scripts/hfs/rules/saga.mjs +128 -61
  150. package/runtime/scripts/hfs/rules/secrets.mjs +4 -3
  151. package/runtime/scripts/hfs/rules/services.mjs +34 -16
  152. package/runtime/scripts/hfs/rules/stacks.mjs +21 -14
  153. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +32 -23
  154. package/runtime/scripts/hfs/rules/test-topology.mjs +25 -11
  155. package/runtime/scripts/hfs/secret.mjs +59 -48
  156. package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
  157. package/runtime/scripts/hfs/slot-classify.mjs +65 -0
  158. package/runtime/scripts/hfs/slot-errors.mjs +11 -0
  159. package/runtime/scripts/hfs/slot-imports.mjs +76 -0
  160. package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
  161. package/runtime/scripts/hfs/slot-match.mjs +153 -0
  162. package/runtime/scripts/hfs/slot-path.mjs +8 -0
  163. package/runtime/scripts/hfs/slot-required.mjs +76 -0
  164. package/runtime/scripts/hfs/slot-semantic-problems.mjs +148 -0
  165. package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
  166. package/runtime/scripts/hfs/slots.mjs +72 -693
  167. package/runtime/scripts/hfs/sql/pg-parse.mjs +5 -2
  168. package/runtime/scripts/hfs/trailing-slashes.mjs +8 -0
  169. package/runtime/scripts/hfs/tree.mjs +19 -14
  170. package/runtime/scripts/hfs/typescript-programs.mjs +6 -4
  171. package/runtime/scripts/hfs/view.mjs +1 -1
  172. package/runtime/scripts/lib/dockerfile.mjs +56 -33
  173. package/runtime/scripts/lib/event-contract.mjs +17 -13
  174. package/runtime/scripts/lib/git.mjs +2 -2
  175. package/runtime/scripts/lib/graphql-contract.mjs +158 -322
  176. package/runtime/scripts/lib/graphql-sdl.mjs +262 -0
  177. package/runtime/scripts/lib/i18n.mjs +3 -2
  178. package/runtime/scripts/lib/in-order.mjs +71 -0
  179. package/runtime/scripts/lib/language.mjs +3 -3
  180. package/runtime/scripts/lib/list.mjs +8 -1
  181. package/runtime/scripts/lib/path-key.mjs +38 -5
  182. package/runtime/scripts/lib/pid-alive.mjs +7 -0
  183. package/runtime/scripts/lib/regex.mjs +1 -1
  184. package/runtime/scripts/lib/same-text.mjs +1 -1
  185. package/runtime/scripts/lib/secret-patterns.mjs +6 -2
  186. package/runtime/scripts/lib/sleep-sync.mjs +2 -2
  187. package/runtime/scripts/lib/sops-envelope.mjs +95 -48
  188. package/runtime/scripts/lib/stack-services.mjs +1 -2
  189. package/runtime/scripts/lib/ts-ast.mjs +9 -0
  190. package/runtime/scripts/lib/walk.mjs +6 -1
  191. package/scaffold/add-cli-lite.mjs +2 -1
  192. package/scaffold/add-table.mjs +2 -2
  193. package/scaffold/add.mjs +3 -2
  194. package/scaffold/app.mjs +4 -2
  195. package/scaffold/edition-gate.mjs +25 -25
  196. package/scaffold/lite-exports.mjs +2 -1
  197. package/scaffold/service.mjs +5 -4
  198. package/sync/hygiene.mjs +15 -9
  199. package/sync/index.mjs +7 -3
  200. package/templates/app/ci-workflows/github/workflows/ci.yml +4 -4
  201. package/templates/app/ci-workflows/github/workflows/e2e.yml +3 -3
  202. package/templates/app/ci-workflows/github/workflows/images.yml +3 -3
  203. package/templates/app/ci-workflows/sonar-steps.yml +2 -2
  204. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +6 -6
  205. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
  206. package/templates/app/ci-workflows-lite/github/workflows/images.yml +3 -3
  207. package/templates/app/starciwork.gitignore +1 -1
  208. package/templates/be/image/api/Dockerfile +1 -1
  209. package/templates/be/image/cli/Dockerfile +1 -1
  210. package/templates/be/image/worker/Dockerfile +1 -1
  211. package/templates/be/patterns/cli/group.cli.spec.ts.tpl +1 -1
  212. package/templates/be/patterns/cli/group.cli.ts.tpl +2 -2
  213. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +13 -0
  214. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +7 -2
  215. package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +4 -1
  216. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +2 -1
  217. package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +24 -9
  218. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +3 -2
  219. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +8 -5
  220. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +1 -1
  221. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +2 -2
  222. package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +1 -1
  223. package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +2 -2
  224. package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +3 -2
  225. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +5 -4
  226. package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
  227. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +47 -0
  228. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.ts +16 -0
  229. package/templates/be/skeleton-lite/apps/api/Dockerfile +1 -0
  230. package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
  231. package/templates/fe/image/next/Dockerfile +2 -1
  232. package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +2 -2
  233. package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +2 -2
  234. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -2
  235. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +1 -2
  236. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +2 -2
  237. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +7 -1
  238. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +1 -1
  239. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +10 -2
  240. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +10 -3
  241. package/upgrade/index.mjs +9 -2
  242. package/runtime/engine/admission.mjs +0 -308
  243. package/runtime/engine/canonical-json.mjs +0 -10
  244. package/runtime/engine/db/blob.mjs +0 -315
  245. package/runtime/engine/db/ledger-paths.mjs +0 -83
  246. package/runtime/engine/db/ledger.mjs +0 -1118
  247. package/runtime/engine/db/machine-connection.mjs +0 -135
  248. package/runtime/engine/db/machine-schema.mjs +0 -84
  249. package/runtime/engine/db/machine.mjs +0 -1367
  250. package/runtime/engine/db/migrations/machine/0001-init.sql +0 -924
  251. package/runtime/engine/db/migrations/runtime/0001-init.sql +0 -1108
  252. package/runtime/engine/db/provider-reservations.mjs +0 -101
  253. package/runtime/engine/digest.mjs +0 -16
  254. package/runtime/engine/refuse.mjs +0 -11
  255. package/runtime/modules/ops/_labels.yaml +0 -53
  256. package/runtime/scripts/api/node/lib.mjs +0 -14
  257. package/runtime/scripts/api/node/spawn-node.mjs +0 -6
  258. package/runtime/scripts/api/process/lib.mjs +0 -111
  259. package/runtime/scripts/api/process/owned-process.mjs +0 -78
  260. package/runtime/scripts/api/process/stop-owned-process.mjs +0 -9
  261. package/runtime/scripts/connectors/lib.mjs +0 -488
  262. package/runtime/scripts/lib/clip.mjs +0 -19
  263. package/runtime/scripts/lib/display-names.mjs +0 -259
  264. package/runtime/scripts/lib/example-refs.mjs +0 -158
  265. package/runtime/scripts/lib/json-schema.mjs +0 -52
  266. package/runtime/scripts/lib/process-identity.mjs +0 -6
  267. package/runtime/scripts/lib/read-yaml.mjs +0 -18
  268. package/runtime/scripts/lib/redact.mjs +0 -161
  269. package/runtime/scripts/lib/sleep.mjs +0 -7
  270. package/runtime/scripts/lib/source-phrases.mjs +0 -34
  271. package/runtime/scripts/lib/sqlite.mjs +0 -21
@@ -1,171 +1,207 @@
1
1
  import path from 'node:path';
2
2
  import { canonical } from './config.mjs';
3
- import { sourceLocation } from '../../lib/ts-ast.mjs';
3
+ import { hasAnyFlag, sourceLocation } from '../../lib/ts-ast.mjs';
4
+
5
+ const HELPER_DEPTH = 4;
6
+
7
+ const checkerOf = (env, sourceFile) => env.context.checkerFor(sourceFile.fileName);
8
+
9
+ function aliased(env, checker, symbol) {
10
+ const { ts } = env;
11
+ const seen = new Set();
12
+ let current = symbol;
13
+ while (current && hasAnyFlag(current.flags, ts.SymbolFlags.Alias) && !seen.has(current)) {
14
+ seen.add(current);
15
+ let target;
16
+ try { target = checker.getAliasedSymbol(current); } catch { break; }
17
+ if (!target || target === current || !(target.declarations?.length)) break;
18
+ current = target;
19
+ }
20
+ return current;
21
+ }
4
22
 
5
- /**
6
- * The small TypeScript reading kit the R33/R38/R39/R41/R45/R84/R86 machine checks share (connection-map, sql-owner,
7
- * register-once, error-masked, default-deny-app-guard, entrypoint-only-in-apps, error-home). Everything is decided by
8
- * the checker and by where a declaration lives (the HFS graph), never by a variable name: a framework symbol is
9
- * recognised by the package it is imported from, a capability class by the file that declares it.
10
- */
11
- export function machineKit({ config, context, graph }) {
12
- const ts = context.ts;
13
- const resolver = graph.resolver;
14
-
15
- const checkerOf = sourceFile => context.checkerFor(sourceFile.fileName);
16
-
17
- const aliased = (checker, symbol) => {
18
- const seen = new Set();
19
- let current = symbol;
20
- while (current && (current.flags & ts.SymbolFlags.Alias) && !seen.has(current)) {
21
- seen.add(current);
22
- let target;
23
- try { target = checker.getAliasedSymbol(current); } catch { break; }
24
- if (!target || target === current || !(target.declarations?.length)) break;
25
- current = target;
26
- }
27
- return current;
28
- };
23
+ function symbolAt(env, checker, node) {
24
+ const { ts } = env;
25
+ if (!node) return null;
26
+ if (ts.isShorthandPropertyAssignment(node.parent ?? {}) && node.parent.name === node) return checker.getShorthandAssignmentValueSymbol(node.parent) ?? null;
27
+ return checker.getSymbolAtLocation(node) ?? null;
28
+ }
29
29
 
30
- const symbolAt = (checker, node) => {
31
- if (!node) return null;
32
- if (ts.isShorthandPropertyAssignment(node.parent ?? {}) && node.parent.name === node) return checker.getShorthandAssignmentValueSymbol(node.parent) ?? null;
33
- return checker.getSymbolAtLocation(node) ?? null;
34
- };
30
+ /** {name, module} when `target` is bound directly by an import specifier, default import or namespace import, else null. */
31
+ function ownImportBinding(env, checker, target) {
32
+ const { ts } = env;
33
+ const declaration = symbolAt(env, checker, target)?.declarations?.[0];
34
+ if (!declaration) return null;
35
+ if (ts.isImportSpecifier(declaration)) return { name: (declaration.propertyName ?? declaration.name).text, module: declaration.parent.parent.parent.moduleSpecifier.text };
36
+ if (ts.isImportClause(declaration)) return { name: 'default', module: declaration.parent.moduleSpecifier.text };
37
+ if (ts.isNamespaceImport(declaration)) return { name: '*', module: declaration.parent.parent.moduleSpecifier.text };
38
+ return null;
39
+ }
35
40
 
36
- /** {name, module} when `node` (identifier, or namespace.member) is bound by an import from a module specifier, else null. */
37
- const importBinding = (checker, node) => {
38
- const own = target => {
39
- const symbol = symbolAt(checker, target);
40
- const declaration = symbol?.declarations?.[0];
41
- if (!declaration) return null;
42
- if (ts.isImportSpecifier(declaration)) return { name: (declaration.propertyName ?? declaration.name).text, module: declaration.parent.parent.parent.moduleSpecifier.text };
43
- if (ts.isImportClause(declaration)) return { name: 'default', module: declaration.parent.moduleSpecifier.text };
44
- if (ts.isNamespaceImport(declaration)) return { name: '*', module: declaration.parent.parent.moduleSpecifier.text };
45
- return null;
46
- };
47
- if (ts.isIdentifier(node)) return own(node);
48
- if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression)) {
49
- const namespace = own(node.expression);
50
- return namespace?.name === '*' ? { name: node.name.text, module: namespace.module } : null;
51
- }
52
- return null;
53
- };
41
+ /** {name, module} when `node` (identifier, or namespace.member) is bound by an import from a module specifier, else null. */
42
+ function importBinding(env, checker, node) {
43
+ const { ts } = env;
44
+ if (ts.isIdentifier(node)) return ownImportBinding(env, checker, node);
45
+ if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression)) {
46
+ const namespace = ownImportBinding(env, checker, node.expression);
47
+ return namespace?.name === '*' ? { name: node.name.text, module: namespace.module } : null;
48
+ }
49
+ return null;
50
+ }
54
51
 
55
- const isImportOf = (checker, node, name, moduleName) => {
56
- const binding = importBinding(checker, node);
57
- return Boolean(binding) && binding.name === name && binding.module === moduleName;
58
- };
52
+ function isImportOf(env, checker, node, name, moduleName) {
53
+ const binding = importBinding(env, checker, node);
54
+ return Boolean(binding) && binding.name === name && binding.module === moduleName;
55
+ }
59
56
 
60
- /** The declarations `node` resolves to through aliases. */
61
- const declarationsOf = (checker, node) => aliased(checker, symbolAt(checker, node))?.declarations ?? [];
57
+ /** The declarations `node` resolves to through aliases. */
58
+ const declarationsOf = (env, checker, node) => aliased(env, checker, symbolAt(env, checker, node))?.declarations ?? [];
62
59
 
63
- /** The repository-relative graph path of the file declaring `declaration`, or null when it is outside the program. */
64
- const graphPath = declaration => graph.abs(canonical(declaration.getSourceFile().fileName));
60
+ /** The repository-relative graph path of the file declaring `declaration`, or null when it is outside the program. */
61
+ const graphPath = (env, declaration) => env.graph.abs(canonical(declaration.getSourceFile().fileName));
65
62
 
66
- const graphFile = rel => graph.files.get(rel) ?? null;
63
+ const graphFile = (env, rel) => env.graph.files.get(rel) ?? null;
67
64
 
68
- /** The capability (owner) root, slot and tier of a declaration, or null. */
69
- const ownerOfDeclaration = declaration => {
70
- const rel = graphPath(declaration);
71
- const file = rel ? graphFile(rel) : null;
72
- return file?.owner ? { rel, root: file.owner.root, slot: file.owner.slot, tier: file.tier, name: path.posix.basename(file.owner.root) } : null;
73
- };
65
+ /** The capability (owner) root, slot and tier of a declaration, or null. */
66
+ function ownerOfDeclaration(env, declaration) {
67
+ const rel = graphPath(env, declaration);
68
+ const file = rel ? graphFile(env, rel) : null;
69
+ return file?.owner ? { rel, root: file.owner.root, slot: file.owner.slot, tier: file.tier, name: path.posix.basename(file.owner.root) } : null;
70
+ }
74
71
 
75
- /** The string a node evaluates to: a literal, or a const whose type is a string literal. */
76
- const stringValue = (checker, node) => {
77
- if (!node) return null;
78
- if (ts.isStringLiteralLike(node)) return node.text;
79
- let type;
80
- try { type = checker.getTypeAtLocation(node); } catch { return null; }
81
- if (type?.isStringLiteral?.()) return type.value;
82
- const declaration = declarationsOf(checker, ts.isPropertyAccessExpression(node) ? node.name : node)[0];
83
- if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer) return stringValue(checker, declaration.initializer);
84
- return null;
85
- };
72
+ /** The string a node evaluates to: a literal, or a const whose type is a string literal. */
73
+ function stringValue(env, checker, node) {
74
+ const { ts } = env;
75
+ if (!node) return null;
76
+ if (ts.isStringLiteralLike(node)) return node.text;
77
+ let type;
78
+ try { type = checker.getTypeAtLocation(node); } catch { return null; }
79
+ if (type?.isStringLiteral?.()) return type.value;
80
+ const declaration = declarationsOf(env, checker, ts.isPropertyAccessExpression(node) ? node.name : node)[0];
81
+ if (declaration && ts.isVariableDeclaration(declaration) && declaration.initializer) return stringValue(env, checker, declaration.initializer);
82
+ return null;
83
+ }
86
84
 
87
- const walk = (node, visit) => {
88
- const step = child => {
89
- if (visit(child) === false) return;
90
- ts.forEachChild(child, step);
91
- };
92
- step(node);
85
+ function walk(env, node, visit) {
86
+ const step = child => {
87
+ if (visit(child) === false) return;
88
+ env.ts.forEachChild(child, step);
93
89
  };
90
+ step(node);
91
+ }
94
92
 
95
- const decorators = node => (ts.canHaveDecorators?.(node) ? ts.getDecorators(node) ?? [] : []);
96
- const isExported = node => Boolean(ts.getCombinedModifierFlags?.(node) & ts.ModifierFlags.Export);
93
+ const decorators = (env, node) => (env.ts.canHaveDecorators?.(node) ? env.ts.getDecorators(node) ?? [] : []);
94
+ const isExported = (env, node) => hasAnyFlag(env.ts.getCombinedModifierFlags?.(node), env.ts.ModifierFlags.Export);
97
95
 
98
- const propertyNameText = name => {
99
- if (!name) return null;
100
- if (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) return name.text;
101
- return null;
102
- };
96
+ function propertyNameText(env, name) {
97
+ if (!name) return null;
98
+ if (env.ts.isIdentifier(name) || env.ts.isStringLiteralLike(name)) return name.text;
99
+ return null;
100
+ }
103
101
 
104
- /** The object literal properties of `literal` by static name. */
105
- const propertyOf = (literal, key) => literal.properties.find(property => (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property)) && propertyNameText(property.name) === key);
102
+ /** The object literal properties of `literal` by static name. */
103
+ const propertyOf = (env, literal, key) => literal.properties
104
+ .find(property => (env.ts.isPropertyAssignment(property) || env.ts.isShorthandPropertyAssignment(property)) && propertyNameText(env, property.name) === key);
106
105
 
107
- const valueOfProperty = property => (ts.isPropertyAssignment(property) ? property.initializer : property.name);
106
+ const valueOfProperty = (env, property) => (env.ts.isPropertyAssignment(property) ? property.initializer : property.name);
108
107
 
109
- const at = (rel, sourceFile, node, extra = {}) => ({ path: rel, ...sourceLocation(sourceFile, node), ...extra });
108
+ const at = (env, rel, sourceFile, node, extra = {}) => ({ path: rel, ...sourceLocation(sourceFile, node), ...extra });
110
109
 
111
- /** The app root file of an app (apps/<name>/src/app.module.ts), or null when the program does not hold it. */
112
- const appRoot = name => {
113
- const rel = `apps/${name}/src/app.module.ts`;
114
- return graphFile(rel);
115
- };
110
+ /** The app root file of an app (apps/<name>/src/app.module.ts), or null when the program does not hold it. */
111
+ const appRoot = (env, name) => graphFile(env, `apps/${name}/src/app.module.ts`);
112
+
113
+ /** The body a call or a spread identifier resolves to when it is a function or a const declared in a file of the program (not a package), else null. */
114
+ function helperBodyOf(env, checker, node) {
115
+ const { ts } = env;
116
+ const target = ts.isCallExpression(node) ? node.expression : node;
117
+ for (const declaration of declarationsOf(env, checker, ts.isPropertyAccessExpression(target) ? target.name : target)) {
118
+ if (!graphPath(env, declaration)) continue;
119
+ if (ts.isFunctionDeclaration(declaration) && declaration.body) return { declaration, body: declaration.body };
120
+ if (ts.isVariableDeclaration(declaration) && declaration.initializer) return { declaration, body: declaration.initializer };
121
+ }
122
+ return null;
123
+ }
116
124
 
117
- /** The `{ provide: <token>, ... }` provider literals of `file` whose token is `name` imported from `moduleName`, in source order. */
118
- const HELPER_DEPTH = 4;
125
+ /** The provider entry of an object literal that provides `name` imported from `moduleName`, or null. */
126
+ function providerEntry(env, search, checker, literal, anchor) {
127
+ const provide = propertyOf(env, literal, 'provide');
128
+ if (!provide || !isImportOf(env, checker, valueOfProperty(env, provide), search.name, search.moduleName)) return null;
129
+ const use = propertyOf(env, literal, 'useClass');
130
+ return { node: anchor ?? literal, useClass: use ? valueOfProperty(env, use) : null, checker };
131
+ }
119
132
 
120
- /** The body a call or a spread identifier resolves to when it is a function or a const declared in a file of the program (not a package), else null. */
121
- const helperBodyOf = (checker, node) => {
122
- const target = ts.isCallExpression(node) ? node.expression : node;
123
- for (const declaration of declarationsOf(checker, ts.isPropertyAccessExpression(target) ? target.name : target)) {
124
- if (!graphPath(declaration)) continue;
125
- if (ts.isFunctionDeclaration(declaration) && declaration.body) return { declaration, body: declaration.body };
126
- if (ts.isVariableDeclaration(declaration) && declaration.initializer) return { declaration, body: declaration.initializer };
133
+ /** The expression that may reference a helper: the argument of a spread, or the call itself. */
134
+ function helperReferenceOf(ts, node) {
135
+ if (ts.isSpreadElement(node)) return node.expression;
136
+ return ts.isCallExpression(node) ? node : null;
137
+ }
138
+
139
+ function followHelper(env, search, checker, node, anchor, depth) {
140
+ const reference = helperReferenceOf(env.ts, node);
141
+ if (!reference || depth >= HELPER_DEPTH) return;
142
+ const helper = helperBodyOf(env, checker, reference);
143
+ if (!helper || search.seen.has(helper.declaration)) return;
144
+ search.seen.add(helper.declaration);
145
+ scanProviders(env, search, helper.body, anchor ?? node, depth + 1);
146
+ }
147
+
148
+ function scanProviders(env, search, root, anchor, depth) {
149
+ const checker = checkerOf(env, root.getSourceFile());
150
+ walk(env, root, node => {
151
+ if (!env.ts.isObjectLiteralExpression(node)) {
152
+ followHelper(env, search, checker, node, anchor, depth);
153
+ return;
127
154
  }
128
- return null;
129
- };
155
+ const entry = providerEntry(env, search, checker, node, anchor);
156
+ if (entry) search.found.push(entry);
157
+ });
158
+ }
130
159
 
131
- /**
132
- * The `{ provide: <token>, ... }` provider literals of `file` whose token is `name` imported from `moduleName`, in source order.
133
- * A call or a spread of a function or a const declared in another file of the program is followed (to a depth of four), so a
134
- * helper that builds the `APP_GUARD` or `APP_FILTER` entries outside the app root file is read too: the entry's `node` is then the
135
- * call or spread inside the app root (where the entry is provided), its `useClass` and `checker` those of the helper's file.
136
- */
137
- const providersOf = (file, name, moduleName) => {
138
- const found = [];
139
- const seen = new Set();
140
- const scan = (root, anchor, depth) => {
141
- const checker = checkerOf(root.getSourceFile());
142
- walk(root, node => {
143
- if (ts.isObjectLiteralExpression(node)) {
144
- const provide = propertyOf(node, 'provide');
145
- if (provide && isImportOf(checker, valueOfProperty(provide), name, moduleName)) {
146
- const use = propertyOf(node, 'useClass');
147
- found.push({ node: anchor ?? node, useClass: use ? valueOfProperty(use) : null, checker });
148
- }
149
- return true;
150
- }
151
- const reference = ts.isSpreadElement(node) ? node.expression : (ts.isCallExpression(node) ? node : null);
152
- if (reference && depth < HELPER_DEPTH) {
153
- const helper = helperBodyOf(checker, reference);
154
- if (helper && !seen.has(helper.declaration)) {
155
- seen.add(helper.declaration);
156
- scan(helper.body, anchor ?? node, depth + 1);
157
- }
158
- }
159
- return true;
160
- });
161
- };
162
- scan(file.sourceFile, null, 0);
163
- return found.sort((a, b) => a.node.getStart() - b.node.getStart());
164
- };
160
+ /**
161
+ * The `{ provide: <token>, ... }` provider literals of `file` whose token is `name` imported from `moduleName`, in source order.
162
+ * A call or a spread of a function or a const declared in another file of the program is followed (to a depth of four), so a
163
+ * helper that builds the `APP_GUARD` or `APP_FILTER` entries outside the app root file is read too: the entry's `node` is then the
164
+ * call or spread inside the app root (where the entry is provided), its `useClass` and `checker` those of the helper's file.
165
+ */
166
+ function providersOf(env, file, name, moduleName) {
167
+ const search = { name, moduleName, found: [], seen: new Set() };
168
+ scanProviders(env, search, file.sourceFile, null, 0);
169
+ return search.found.sort((a, b) => a.node.getStart() - b.node.getStart());
170
+ }
165
171
 
166
- return { ts, resolver, providersOf, checkerOf, aliased, symbolAt, importBinding, isImportOf, declarationsOf, graphPath, graphFile, ownerOfDeclaration,
167
- stringValue, walk, decorators, isExported, propertyNameText, propertyOf, valueOfProperty, at, appRoot };
172
+ /**
173
+ * The small TypeScript reading kit the R33/R38/R39/R41/R45/R84/R86 machine checks share (connection-map, sql-owner,
174
+ * register-once, error-masked, default-deny-app-guard, entrypoint-only-in-apps, error-home). Everything is decided by
175
+ * the checker and by where a declaration lives (the HFS graph), never by a variable name: a framework symbol is
176
+ * recognised by the package it is imported from, a capability class by the file that declares it.
177
+ */
178
+ export function machineKit({ context, graph }) {
179
+ const env = { ts: context.ts, context, graph };
180
+ const bound = operation => (...args) => operation(env, ...args);
181
+ return {
182
+ ts: env.ts,
183
+ resolver: graph.resolver,
184
+ providersOf: bound(providersOf),
185
+ checkerOf: bound(checkerOf),
186
+ aliased: bound(aliased),
187
+ symbolAt: bound(symbolAt),
188
+ importBinding: bound(importBinding),
189
+ isImportOf: bound(isImportOf),
190
+ declarationsOf: bound(declarationsOf),
191
+ graphPath: bound(graphPath),
192
+ graphFile: bound(graphFile),
193
+ ownerOfDeclaration: bound(ownerOfDeclaration),
194
+ stringValue: bound(stringValue),
195
+ walk: bound(walk),
196
+ decorators: bound(decorators),
197
+ isExported: bound(isExported),
198
+ propertyNameText: bound(propertyNameText),
199
+ propertyOf: bound(propertyOf),
200
+ valueOfProperty: bound(valueOfProperty),
201
+ at: bound(at),
202
+ appRoot: bound(appRoot),
203
+ };
168
204
  }
169
205
 
170
- export const upperSnake = name => name.replace(/-/g, '_').toUpperCase();
206
+ export const upperSnake = name => name.replaceAll('-', '_').toUpperCase();
171
207
  export const pascal = name => name.split('-').filter(Boolean).map(part => part[0].toUpperCase() + part.slice(1)).join('');
@@ -10,6 +10,10 @@ const TEMPLATE_ROOTS = [
10
10
  path.resolve(import.meta.dirname, '..', '..', '..', '..', 'templates'),
11
11
  ];
12
12
  const managedScriptCache = new Map();
13
+ const SCRIPT_ENTRY_START = String.raw`^\s*"`;
14
+ const SCRIPT_ENTRY_NAME = String.raw`([^"\n]+)`;
15
+ const SCRIPT_ENTRY_END = String.raw`":\s*"`;
16
+ const SCRIPT_ENTRY = new RegExp([SCRIPT_ENTRY_START, SCRIPT_ENTRY_NAME, SCRIPT_ENTRY_END].join(''), 'gmu');
13
17
 
14
18
  /** The script names of a profile/edition template after its partials are expanded. */
15
19
  export function managedScriptNames(profile, edition = 'full') {
@@ -18,10 +22,12 @@ export function managedScriptNames(profile, edition = 'full') {
18
22
  const slot = loadSlotManifest().slots.find((entry) => entry.id === `${profile}.package-manifest`);
19
23
  const group = slot && managedGroupOf(slot, edition);
20
24
  const templates = group && TEMPLATE_ROOTS.find((dir) => fs.existsSync(path.join(dir, profile, group, 'package.json')));
21
- if (!templates) throw Error(`The managed package-scripts template of profile ${profile} cannot be found next to the runtime.`);
25
+ if (!templates) throw new Error(`The managed package-scripts template of profile ${profile} cannot be found next to the runtime.`);
22
26
  const text = fs.readFileSync(path.join(templates, profile, group, 'package.json'), 'utf8')
23
27
  .replace(/^\{\{> ([\w./-]+)\}\}\r?\n/gmu, (_, partial) => fs.readFileSync(path.join(templates, partial), 'utf8'));
24
- managedScriptCache.set(key, new Set([...text.matchAll(/^\s*"([A-Za-z0-9:_.-]+)":\s*"/gmu)].map((match) => match[1])));
28
+ // The key is bounded by quotes, never the colon, so `[^"]` cannot backtrack; the strict script-name class filters after.
29
+ const names = [...text.matchAll(SCRIPT_ENTRY)].map((match) => match[1]);
30
+ managedScriptCache.set(key, new Set(names.filter((name) => /^[A-Za-z0-9:_.-]+$/.test(name))));
25
31
  }
26
32
  return managedScriptCache.get(key);
27
33
  }