@starci/hfs 4.0.10 → 4.2.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 (275) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/emit/operations.mjs +1 -1
  3. package/emit/type-schema.mjs +2 -1
  4. package/lint/run.mjs +65 -24
  5. package/package.json +1 -1
  6. package/report/order.mjs +2 -0
  7. package/runtime/config.example.yaml +194 -0
  8. package/runtime/engine/by-code-unit.mjs +12 -0
  9. package/runtime/engine/config.mjs +222 -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/release-config.mjs +28 -0
  14. package/runtime/engine/removed-vocabulary.mjs +28 -0
  15. package/runtime/engine/resources-config.mjs +52 -0
  16. package/runtime/engine/runtime-root.mjs +17 -0
  17. package/runtime/engine/secrets.mjs +35 -31
  18. package/runtime/engine/sonar-config.mjs +21 -0
  19. package/runtime/engine/temp-root.mjs +32 -0
  20. package/runtime/knowledge/hfs/canon-pins.yaml +14 -14
  21. package/runtime/knowledge/hfs/peer-integrations.yaml +2 -3
  22. package/runtime/knowledge/hfs/rules.yaml +161 -65
  23. package/runtime/knowledge/hfs/slots.yaml +11 -14
  24. package/runtime/knowledge/patterns/be/api.yaml +7 -24
  25. package/runtime/knowledge/patterns/be/cli.yaml +3 -12
  26. package/runtime/knowledge/patterns/be/realtime.yaml +1 -14
  27. package/runtime/knowledge/patterns/be/webhooks.yaml +0 -19
  28. package/runtime/modules/kernel/failure-codes.yaml +1 -41
  29. package/runtime/modules/kernel/removed-vocabulary.yaml +268 -0
  30. package/runtime/modules/models/registry.yaml +25 -359
  31. package/runtime/modules/models/runtimes.yaml +126 -265
  32. package/runtime/modules/models/tiers.yaml +66 -0
  33. package/runtime/scripts/api/fs/claim-file.mjs +30 -0
  34. package/runtime/scripts/api/fs/ensure-temp-root.mjs +27 -0
  35. package/runtime/scripts/api/fs/forbidden-root.mjs +2 -1
  36. package/runtime/scripts/api/fs/make-temp-dir.mjs +11 -0
  37. package/runtime/scripts/api/fs/safe-remove.mjs +52 -35
  38. package/runtime/scripts/api/git/lib.mjs +34 -1
  39. package/runtime/scripts/api/process/resolve-real-tool.mjs +14 -8
  40. package/runtime/scripts/api/process/run-program.mjs +2 -1
  41. package/runtime/scripts/api/sops/lib.mjs +198 -120
  42. package/runtime/scripts/hfs/allows.mjs +3 -3
  43. package/runtime/scripts/hfs/architecture/ast-walks.mjs +63 -50
  44. package/runtime/scripts/hfs/architecture/automatic-gates.mjs +96 -0
  45. package/runtime/scripts/hfs/architecture/backend.mjs +261 -175
  46. package/runtime/scripts/hfs/architecture/background-unowned.mjs +69 -34
  47. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +55 -54
  48. package/runtime/scripts/hfs/architecture/clones.mjs +179 -106
  49. package/runtime/scripts/hfs/architecture/config-unread.mjs +5 -22
  50. package/runtime/scripts/hfs/architecture/config.mjs +95 -80
  51. package/runtime/scripts/hfs/architecture/connection-map.mjs +233 -141
  52. package/runtime/scripts/hfs/architecture/constructor-deps.mjs +15 -9
  53. package/runtime/scripts/hfs/architecture/context-coupling.mjs +76 -52
  54. package/runtime/scripts/hfs/architecture/context-map.mjs +215 -141
  55. package/runtime/scripts/hfs/architecture/context-owner.mjs +39 -30
  56. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +31 -17
  57. package/runtime/scripts/hfs/architecture/context-transaction.mjs +38 -25
  58. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +86 -40
  59. package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +342 -0
  60. package/runtime/scripts/hfs/architecture/contracts.mjs +263 -455
  61. package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +43 -34
  62. package/runtime/scripts/hfs/architecture/dead-exports.mjs +201 -154
  63. package/runtime/scripts/hfs/architecture/default-deny.mjs +118 -93
  64. package/runtime/scripts/hfs/architecture/doc-language.mjs +2 -1
  65. package/runtime/scripts/hfs/architecture/entrypoint.mjs +45 -30
  66. package/runtime/scripts/hfs/architecture/error-codes.mjs +19 -12
  67. package/runtime/scripts/hfs/architecture/error-masked.mjs +39 -26
  68. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -1
  69. package/runtime/scripts/hfs/architecture/feature-shape.mjs +26 -20
  70. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
  71. package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +190 -0
  72. package/runtime/scripts/hfs/architecture/frontend-routing.mjs +186 -0
  73. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +311 -0
  74. package/runtime/scripts/hfs/architecture/frontend-world.mjs +328 -0
  75. package/runtime/scripts/hfs/architecture/frontend.mjs +109 -829
  76. package/runtime/scripts/hfs/architecture/hfs-graph.mjs +14 -2
  77. package/runtime/scripts/hfs/architecture/hfs.mjs +120 -288
  78. package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +46 -26
  79. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +114 -76
  80. package/runtime/scripts/hfs/architecture/index.mjs +164 -104
  81. package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +33 -28
  82. package/runtime/scripts/hfs/architecture/machine-ast.mjs +180 -144
  83. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +8 -2
  84. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +150 -101
  85. package/runtime/scripts/hfs/architecture/next-data-contract.mjs +51 -43
  86. package/runtime/scripts/hfs/architecture/next-data-key.mjs +338 -0
  87. package/runtime/scripts/hfs/architecture/next-data.mjs +179 -427
  88. package/runtime/scripts/hfs/architecture/owners.mjs +27 -13
  89. package/runtime/scripts/hfs/architecture/package-shape.mjs +51 -23
  90. package/runtime/scripts/hfs/architecture/presentation.mjs +165 -0
  91. package/runtime/scripts/hfs/architecture/reachability.mjs +127 -61
  92. package/runtime/scripts/hfs/architecture/register-once.mjs +112 -63
  93. package/runtime/scripts/hfs/architecture/registration.mjs +187 -110
  94. package/runtime/scripts/hfs/architecture/required-files.mjs +121 -70
  95. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +83 -58
  96. package/runtime/scripts/hfs/architecture/schema-owner.mjs +182 -108
  97. package/runtime/scripts/hfs/architecture/source-names-shape.mjs +318 -0
  98. package/runtime/scripts/hfs/architecture/source-names.mjs +83 -248
  99. package/runtime/scripts/hfs/architecture/sql-owner.mjs +175 -118
  100. package/runtime/scripts/hfs/architecture/sql-returning.mjs +39 -28
  101. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +302 -172
  102. package/runtime/scripts/hfs/architecture/supabase-ast.mjs +1 -1
  103. package/runtime/scripts/hfs/architecture/supabase-be.mjs +55 -45
  104. package/runtime/scripts/hfs/architecture/supabase-results.mjs +182 -0
  105. package/runtime/scripts/hfs/architecture/supabase-tables.mjs +20 -12
  106. package/runtime/scripts/hfs/architecture/supabase.mjs +191 -263
  107. package/runtime/scripts/hfs/architecture/symbols.mjs +175 -99
  108. package/runtime/scripts/hfs/architecture/test-world-files.mjs +82 -41
  109. package/runtime/scripts/hfs/architecture/tiers.mjs +120 -69
  110. package/runtime/scripts/hfs/architecture/transport-owner.mjs +84 -57
  111. package/runtime/scripts/hfs/architecture/type-context.mjs +282 -0
  112. package/runtime/scripts/hfs/architecture/typescript.mjs +116 -276
  113. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +84 -62
  114. package/runtime/scripts/hfs/architecture.mjs +3 -4
  115. package/runtime/scripts/hfs/catalog-once.mjs +15 -0
  116. package/runtime/scripts/hfs/check.mjs +63 -71
  117. package/runtime/scripts/hfs/coverage-scope.mjs +84 -38
  118. package/runtime/scripts/hfs/declaration-shape.mjs +77 -36
  119. package/runtime/scripts/hfs/declaration-slots.mjs +1 -1
  120. package/runtime/scripts/hfs/edition-slots.mjs +5 -3
  121. package/runtime/scripts/hfs/linear-text.mjs +30 -0
  122. package/runtime/scripts/hfs/manifest-shape.mjs +164 -63
  123. package/runtime/scripts/hfs/path-findings.mjs +58 -38
  124. package/runtime/scripts/hfs/pin-findings.mjs +31 -0
  125. package/runtime/scripts/hfs/repo-identity.mjs +21 -4
  126. package/runtime/scripts/hfs/rule-catalog.mjs +194 -0
  127. package/runtime/scripts/hfs/rule-params-shape.mjs +33 -27
  128. package/runtime/scripts/hfs/rules/cli.mjs +5 -1
  129. package/runtime/scripts/hfs/rules/contract-compat.mjs +40 -22
  130. package/runtime/scripts/hfs/rules/contract.mjs +35 -27
  131. package/runtime/scripts/hfs/rules/database-config.mjs +57 -32
  132. package/runtime/scripts/hfs/rules/database-migrations.mjs +52 -31
  133. package/runtime/scripts/hfs/rules/database-plpgsql.mjs +106 -91
  134. package/runtime/scripts/hfs/rules/database-sql.mjs +254 -173
  135. package/runtime/scripts/hfs/rules/database.mjs +36 -23
  136. package/runtime/scripts/hfs/rules/deps.mjs +64 -32
  137. package/runtime/scripts/hfs/rules/docker.mjs +113 -65
  138. package/runtime/scripts/hfs/rules/edition.mjs +118 -111
  139. package/runtime/scripts/hfs/rules/event-bus.mjs +91 -45
  140. package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +17 -16
  141. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +36 -16
  142. package/runtime/scripts/hfs/rules/frontend-tree.mjs +4 -3
  143. package/runtime/scripts/hfs/rules/integration-specs.mjs +75 -41
  144. package/runtime/scripts/hfs/rules/kinds.mjs +59 -25
  145. package/runtime/scripts/hfs/rules/lint-suppression.mjs +9 -3
  146. package/runtime/scripts/hfs/rules/monorepo.mjs +82 -45
  147. package/runtime/scripts/hfs/rules/peer-integrations.mjs +24 -3
  148. package/runtime/scripts/hfs/rules/pipeline.mjs +58 -9
  149. package/runtime/scripts/hfs/rules/proof-commands.mjs +14 -8
  150. package/runtime/scripts/hfs/rules/repo-local-checks.mjs +11 -6
  151. package/runtime/scripts/hfs/rules/saga.mjs +128 -61
  152. package/runtime/scripts/hfs/rules/secrets.mjs +4 -3
  153. package/runtime/scripts/hfs/rules/services.mjs +34 -16
  154. package/runtime/scripts/hfs/rules/stacks.mjs +21 -14
  155. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +32 -23
  156. package/runtime/scripts/hfs/rules/test-topology.mjs +25 -11
  157. package/runtime/scripts/hfs/secret.mjs +59 -48
  158. package/runtime/scripts/hfs/slot-app-view.mjs +102 -0
  159. package/runtime/scripts/hfs/slot-classify.mjs +65 -0
  160. package/runtime/scripts/hfs/slot-errors.mjs +11 -0
  161. package/runtime/scripts/hfs/slot-imports.mjs +76 -0
  162. package/runtime/scripts/hfs/slot-manifest-shape.mjs +73 -0
  163. package/runtime/scripts/hfs/slot-match.mjs +153 -0
  164. package/runtime/scripts/hfs/slot-path.mjs +8 -0
  165. package/runtime/scripts/hfs/slot-required.mjs +76 -0
  166. package/runtime/scripts/hfs/slot-semantic-problems.mjs +148 -0
  167. package/runtime/scripts/hfs/slot-side-problems.mjs +28 -0
  168. package/runtime/scripts/hfs/slots.mjs +74 -695
  169. package/runtime/scripts/hfs/sql/pg-parse.mjs +5 -2
  170. package/runtime/scripts/hfs/trailing-slashes.mjs +8 -0
  171. package/runtime/scripts/hfs/tree.mjs +19 -14
  172. package/runtime/scripts/hfs/typescript-programs.mjs +6 -4
  173. package/runtime/scripts/hfs/view.mjs +1 -1
  174. package/runtime/scripts/lib/dockerfile.mjs +56 -33
  175. package/runtime/scripts/lib/env.mjs +10 -0
  176. package/runtime/scripts/lib/event-contract.mjs +17 -13
  177. package/runtime/scripts/lib/git.mjs +2 -2
  178. package/runtime/scripts/lib/graphql-contract.mjs +158 -322
  179. package/runtime/scripts/lib/graphql-sdl.mjs +262 -0
  180. package/runtime/scripts/lib/i18n.mjs +3 -2
  181. package/runtime/scripts/lib/in-order.mjs +71 -0
  182. package/runtime/scripts/lib/language.mjs +3 -3
  183. package/runtime/scripts/lib/list.mjs +8 -1
  184. package/runtime/scripts/lib/path-key.mjs +38 -5
  185. package/runtime/scripts/lib/pid-alive.mjs +7 -0
  186. package/runtime/scripts/lib/regex.mjs +1 -1
  187. package/runtime/scripts/lib/same-text.mjs +1 -1
  188. package/runtime/scripts/lib/secret-patterns.mjs +6 -2
  189. package/runtime/scripts/lib/sleep-sync.mjs +2 -2
  190. package/runtime/scripts/lib/sops-envelope.mjs +95 -48
  191. package/runtime/scripts/lib/stack-services.mjs +1 -2
  192. package/runtime/scripts/lib/ts-ast.mjs +9 -0
  193. package/runtime/scripts/lib/walk.mjs +6 -1
  194. package/runtime/scripts/lib/yaml-cached.mjs +14 -0
  195. package/scaffold/add-cli-lite.mjs +2 -1
  196. package/scaffold/add-table.mjs +2 -2
  197. package/scaffold/add.mjs +3 -2
  198. package/scaffold/app.mjs +4 -2
  199. package/scaffold/edition-gate.mjs +25 -25
  200. package/scaffold/lite-exports.mjs +2 -1
  201. package/scaffold/service.mjs +5 -4
  202. package/sync/hygiene.mjs +15 -9
  203. package/sync/index.mjs +7 -3
  204. package/templates/app/ci-workflows/github/workflows/ci.yml +4 -4
  205. package/templates/app/ci-workflows/github/workflows/e2e.yml +3 -3
  206. package/templates/app/ci-workflows/github/workflows/images.yml +3 -3
  207. package/templates/app/ci-workflows/sonar-steps.yml +2 -2
  208. package/templates/app/ci-workflows-lite/github/workflows/ci.yml +6 -6
  209. package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +3 -3
  210. package/templates/app/ci-workflows-lite/github/workflows/images.yml +3 -3
  211. package/templates/app/starciwork.gitignore +1 -1
  212. package/templates/be/image/api/Dockerfile +1 -1
  213. package/templates/be/image/cli/Dockerfile +1 -1
  214. package/templates/be/image/worker/Dockerfile +1 -1
  215. package/templates/be/patterns/cli/group.cli.spec.ts.tpl +1 -1
  216. package/templates/be/patterns/cli/group.cli.ts.tpl +2 -2
  217. package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +13 -0
  218. package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +7 -2
  219. package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +4 -1
  220. package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +2 -1
  221. package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +24 -9
  222. package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +3 -2
  223. package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +8 -5
  224. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +1 -1
  225. package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +2 -2
  226. package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +1 -1
  227. package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +2 -2
  228. package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +3 -2
  229. package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +5 -4
  230. package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
  231. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.spec.ts +47 -0
  232. package/templates/be/skeleton/src/modules/platform/primitives/sequence.policy.ts +16 -0
  233. package/templates/be/skeleton-lite/apps/api/Dockerfile +1 -0
  234. package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
  235. package/templates/fe/image/next/Dockerfile +2 -1
  236. package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +2 -2
  237. package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +2 -2
  238. package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +1 -2
  239. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +1 -2
  240. package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +2 -2
  241. package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +7 -1
  242. package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +1 -1
  243. package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +10 -2
  244. package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +10 -3
  245. package/upgrade/index.mjs +9 -2
  246. package/runtime/engine/admission.mjs +0 -308
  247. package/runtime/engine/canonical-json.mjs +0 -10
  248. package/runtime/engine/db/blob.mjs +0 -315
  249. package/runtime/engine/db/ledger-paths.mjs +0 -83
  250. package/runtime/engine/db/ledger.mjs +0 -1118
  251. package/runtime/engine/db/machine-connection.mjs +0 -135
  252. package/runtime/engine/db/machine-schema.mjs +0 -84
  253. package/runtime/engine/db/machine.mjs +0 -1367
  254. package/runtime/engine/db/migrations/machine/0001-init.sql +0 -924
  255. package/runtime/engine/db/migrations/runtime/0001-init.sql +0 -1108
  256. package/runtime/engine/db/provider-reservations.mjs +0 -101
  257. package/runtime/engine/digest.mjs +0 -16
  258. package/runtime/engine/refuse.mjs +0 -11
  259. package/runtime/modules/ops/_labels.yaml +0 -53
  260. package/runtime/scripts/api/node/lib.mjs +0 -14
  261. package/runtime/scripts/api/node/spawn-node.mjs +0 -6
  262. package/runtime/scripts/api/process/lib.mjs +0 -111
  263. package/runtime/scripts/api/process/owned-process.mjs +0 -78
  264. package/runtime/scripts/api/process/stop-owned-process.mjs +0 -9
  265. package/runtime/scripts/connectors/lib.mjs +0 -488
  266. package/runtime/scripts/lib/clip.mjs +0 -19
  267. package/runtime/scripts/lib/display-names.mjs +0 -259
  268. package/runtime/scripts/lib/example-refs.mjs +0 -158
  269. package/runtime/scripts/lib/json-schema.mjs +0 -52
  270. package/runtime/scripts/lib/process-identity.mjs +0 -6
  271. package/runtime/scripts/lib/read-yaml.mjs +0 -18
  272. package/runtime/scripts/lib/redact.mjs +0 -161
  273. package/runtime/scripts/lib/sleep.mjs +0 -7
  274. package/runtime/scripts/lib/source-phrases.mjs +0 -34
  275. 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
  }