@astrale-os/cli 1.0.0-beta.1 → 1.0.0-beta.11

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 (329) hide show
  1. package/README.md +23 -12
  2. package/dist/astrale.js +48048 -56798
  3. package/dist/public/connect-core.js +2011 -30765
  4. package/dist/public/keys/index.js +884 -29483
  5. package/dist/public/paths/index.js +13101 -41459
  6. package/dist/types/admin/binding.d.ts +19 -0
  7. package/dist/types/admin/instance/client.d.ts +22 -0
  8. package/dist/types/admin/instance/index.d.ts +2 -0
  9. package/dist/types/admin/instance/model.d.ts +5 -12
  10. package/dist/types/connection/auth.d.ts +3 -0
  11. package/dist/types/connection/call.d.ts +3 -0
  12. package/dist/types/connection/command.d.ts +27 -0
  13. package/dist/types/connection/credential.d.ts +14 -0
  14. package/dist/types/connection/errors.d.ts +1 -0
  15. package/dist/types/connection/exchange.d.ts +9 -0
  16. package/dist/types/connection/failure/classify.d.ts +2 -0
  17. package/dist/types/connection/failure/index.d.ts +5 -0
  18. package/dist/types/connection/failure/model.d.ts +25 -0
  19. package/dist/types/connection/failure/render.d.ts +3 -0
  20. package/dist/types/connection/index.d.ts +8 -0
  21. package/dist/types/connection/reasons.d.ts +37 -0
  22. package/dist/types/connection/self.d.ts +37 -0
  23. package/dist/types/connection/session.d.ts +31 -0
  24. package/dist/types/connection/target.d.ts +38 -0
  25. package/dist/types/errors.d.ts +1 -1
  26. package/dist/types/lib/admin-instance.d.ts +17 -0
  27. package/dist/types/lib/admin-target.d.ts +11 -36
  28. package/dist/types/lib/config.d.ts +7 -59
  29. package/dist/types/lib/failure-debug.d.ts +1 -0
  30. package/dist/types/lib/idp.d.ts +26 -168
  31. package/dist/types/lib/instance-target.d.ts +1 -1
  32. package/dist/types/lib/instance.d.ts +30 -88
  33. package/dist/types/lib/log.d.ts +5 -3
  34. package/dist/types/lib/meta.d.ts +2 -2
  35. package/dist/types/lib/proc.d.ts +43 -0
  36. package/dist/types/lib/self.d.ts +9 -0
  37. package/dist/types/lib/update.d.ts +91 -0
  38. package/dist/types/lib/validation.d.ts +4 -1
  39. package/package.json +16 -19
  40. package/src/__tests__/fixtures/publication.ts +20 -0
  41. package/src/admin/__tests__/binding.test.ts +31 -0
  42. package/src/admin/__tests__/fixture.ts +101 -0
  43. package/src/admin/binding.ts +98 -0
  44. package/src/admin/catalog/.spec/api.d.ts +2 -2
  45. package/src/admin/catalog/__tests__/client.test.ts +20 -49
  46. package/src/admin/catalog/client.ts +36 -32
  47. package/src/admin/catalog/model.ts +3 -4
  48. package/src/admin/graph/.spec/api.d.ts +4 -10
  49. package/src/admin/graph/nodes.ts +1 -1
  50. package/src/admin/instance/.spec/api.d.ts +2 -2
  51. package/src/admin/instance/__tests__/client.test.ts +39 -164
  52. package/src/admin/instance/client.ts +35 -124
  53. package/src/admin/instance/index.ts +1 -2
  54. package/src/admin/instance/model.ts +11 -20
  55. package/src/commands/__tests__/admin-instance.test.ts +11 -8
  56. package/src/commands/__tests__/auth-login.test.ts +54 -1
  57. package/src/commands/__tests__/call-describe.test.ts +46 -33
  58. package/src/commands/__tests__/call.test.ts +5 -1
  59. package/src/commands/__tests__/domain-install-operation.test.ts +12 -7
  60. package/src/commands/__tests__/domain-install-owned.test.ts +6 -2
  61. package/src/commands/__tests__/domain-list.test.ts +63 -4
  62. package/src/commands/__tests__/install-identity-override.test.ts +19 -19
  63. package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
  64. package/src/commands/__tests__/instance-status.test.ts +139 -0
  65. package/src/commands/__tests__/read-commands.test.ts +35 -10
  66. package/src/commands/__tests__/studio-descriptor.test.ts +22 -0
  67. package/src/commands/__tests__/update.test.ts +48 -19
  68. package/src/commands/call-describe.ts +60 -68
  69. package/src/commands/call.ts +43 -56
  70. package/src/commands/domain/install.ts +13 -17
  71. package/src/commands/domain/list.ts +18 -13
  72. package/src/commands/domain/publish.ts +14 -11
  73. package/src/commands/domain/uninstall.ts +2 -2
  74. package/src/commands/get.ts +14 -15
  75. package/src/commands/identity/__tests__/register.test.ts +3 -3
  76. package/src/commands/identity/register.ts +5 -4
  77. package/src/commands/instance/create.ts +8 -16
  78. package/src/commands/instance/delete.ts +12 -9
  79. package/src/commands/instance/list.ts +2 -2
  80. package/src/commands/instance/status.ts +66 -20
  81. package/src/commands/instance/use.ts +16 -12
  82. package/src/commands/introspect.ts +9 -16
  83. package/src/commands/logs.ts +24 -25
  84. package/src/commands/mutate.ts +6 -5
  85. package/src/commands/query.ts +27 -30
  86. package/src/commands/studio.ts +20 -0
  87. package/src/commands/token.ts +8 -7
  88. package/src/commands/update.ts +38 -14
  89. package/src/commands/view.ts +7 -6
  90. package/src/connection/.spec/api.d.ts +7 -8
  91. package/src/connection/.spec/architecture.md +5 -4
  92. package/src/connection/.spec/flows/session.ts +2 -2
  93. package/src/connection/.spec/laws/connection.ts +6 -2
  94. package/src/connection/.spec/layout.ts +1 -0
  95. package/src/connection/__tests__/credential.test.ts +20 -1
  96. package/src/connection/__tests__/errors.test.ts +43 -32
  97. package/src/connection/__tests__/exchange.test.ts +36 -6
  98. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  99. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  100. package/src/connection/__tests__/self.test.ts +31 -11
  101. package/src/connection/__tests__/session.test.ts +3 -3
  102. package/src/connection/__tests__/target.test.ts +1 -0
  103. package/src/connection/auth.ts +13 -0
  104. package/src/connection/call.ts +1 -1
  105. package/src/connection/credential.ts +2 -2
  106. package/src/connection/errors.ts +1 -355
  107. package/src/connection/exchange.ts +52 -27
  108. package/src/connection/failure/classify.ts +110 -0
  109. package/src/connection/failure/index.ts +18 -0
  110. package/src/connection/failure/model.ts +28 -0
  111. package/src/connection/failure/render.ts +116 -0
  112. package/src/connection/reasons.ts +16 -0
  113. package/src/connection/self.ts +39 -21
  114. package/src/connection/session.ts +19 -8
  115. package/src/errors.ts +3 -2
  116. package/src/graph/.spec/api.d.ts +12 -5
  117. package/src/graph/.spec/layout.ts +9 -1
  118. package/src/graph/__tests__/mutation.test.ts +2 -2
  119. package/src/graph/__tests__/query.test.ts +25 -6
  120. package/src/graph/class.ts +30 -0
  121. package/src/graph/index.ts +1 -0
  122. package/src/graph/query.ts +21 -32
  123. package/src/identity/.spec/api.d.ts +2 -2
  124. package/src/identity/__tests__/registration.test.ts +1 -1
  125. package/src/identity/registration.ts +3 -3
  126. package/src/keys/__tests__/keys.test.ts +4 -0
  127. package/src/keys/credential.ts +1 -0
  128. package/src/lib/__tests__/admin-target.test.ts +12 -0
  129. package/src/lib/__tests__/command-dx.test.ts +1 -1
  130. package/src/lib/__tests__/domain-publication.test.ts +138 -0
  131. package/src/lib/__tests__/idp-session.test.ts +22 -0
  132. package/src/lib/__tests__/idp.test.ts +36 -4
  133. package/src/lib/__tests__/instance-candidates.test.ts +1 -3
  134. package/src/lib/__tests__/instance-target.test.ts +7 -39
  135. package/src/lib/__tests__/instance.test.ts +6 -0
  136. package/src/lib/__tests__/log-errors.test.ts +64 -0
  137. package/src/lib/__tests__/studio-server-deps.test.ts +53 -16
  138. package/src/lib/__tests__/update.test.ts +113 -8
  139. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  140. package/src/lib/__tests__/view-server.test.ts +18 -3
  141. package/src/lib/__tests__/view-session.test.ts +1 -1
  142. package/src/lib/admin-instance.ts +14 -8
  143. package/src/lib/admin-target.ts +21 -2
  144. package/src/lib/command-dx.ts +9 -9
  145. package/src/lib/domain-publication.ts +104 -0
  146. package/src/lib/failure-debug.ts +26 -0
  147. package/src/lib/idp.ts +39 -4
  148. package/src/lib/instance-candidates.ts +2 -2
  149. package/src/lib/instance-target.ts +3 -14
  150. package/src/lib/instance.ts +22 -12
  151. package/src/lib/log.ts +16 -9
  152. package/src/lib/login-flow.ts +13 -3
  153. package/src/lib/meta.ts +2 -2
  154. package/src/lib/provision-instance.ts +34 -57
  155. package/src/lib/update.ts +124 -30
  156. package/src/lib/validation.ts +2 -2
  157. package/src/lib/view/resolve.ts +1 -1
  158. package/src/lib/view/server.ts +17 -3
  159. package/src/program/__tests__/program.test.ts +20 -4
  160. package/src/program/build.ts +2 -2
  161. package/src/setup/__tests__/instance-step.test.ts +27 -10
  162. package/src/setup/steps/instance.ts +22 -15
  163. package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
  164. package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
  165. package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
  166. package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  167. package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
  168. package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
  169. package/studio/client/dist/index.html +3 -2
  170. package/studio/package.json +10 -14
  171. package/studio/server/agent/ask.test.ts +1 -1
  172. package/studio/server/agent/bridge/grant.test.ts +1 -1
  173. package/studio/server/agent/bridge/routes.test.ts +1 -1
  174. package/studio/server/agent/bridge/stdio.ts +8 -1
  175. package/studio/server/agent/conversation.test.ts +10 -5
  176. package/studio/server/agent/conversation.ts +56 -8
  177. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  178. package/studio/server/agent/harness/codex/loadout.ts +18 -21
  179. package/studio/server/agent/harness/gateway/config.ts +20 -18
  180. package/studio/server/agent/harness/selection.ts +9 -1
  181. package/studio/server/agent/prompts/anchors.test.ts +32 -36
  182. package/studio/server/agent/prompts/anchors.ts +20 -49
  183. package/studio/server/agent/prompts/system.test.ts +3 -2
  184. package/studio/server/agent/prompts/system.ts +6 -5
  185. package/studio/server/agent/prompts/turn.ts +13 -4
  186. package/studio/server/agent/routes.test.ts +1 -1
  187. package/studio/server/agent/run/completion.ts +2 -2
  188. package/studio/server/agent/run/coordinator.test.ts +1 -1
  189. package/studio/server/agent/run/preparation.ts +6 -5
  190. package/studio/server/agent/run/transcript.test.ts +35 -1
  191. package/studio/server/agent/run/transcript.ts +169 -3
  192. package/studio/server/agent/run/usage.test.ts +24 -0
  193. package/studio/server/agent/run/usage.ts +26 -1
  194. package/studio/server/api/canvas.ts +43 -0
  195. package/studio/server/api/comments.ts +85 -0
  196. package/studio/server/api/context.ts +61 -0
  197. package/studio/server/api/deployment.ts +39 -0
  198. package/studio/server/api/documents.ts +55 -0
  199. package/studio/server/api/domain.ts +59 -0
  200. package/studio/server/api/http.ts +45 -0
  201. package/studio/server/api/project.ts +57 -0
  202. package/studio/server/api/schema.ts +12 -0
  203. package/studio/server/api/views.ts +57 -0
  204. package/studio/server/api/workspace.ts +60 -0
  205. package/studio/server/api-agent-loadout.test.ts +1 -1
  206. package/studio/server/api.test.ts +147 -0
  207. package/studio/server/api.ts +20 -422
  208. package/studio/server/cache.test.ts +122 -0
  209. package/studio/server/cache.ts +162 -11
  210. package/studio/server/cli-consumers.test.ts +494 -0
  211. package/studio/server/cli.test.ts +88 -0
  212. package/studio/server/cli.ts +240 -0
  213. package/studio/server/client-package.ts +25 -1
  214. package/studio/server/domain.test.ts +50 -40
  215. package/studio/server/domain.ts +83 -52
  216. package/studio/server/environment/dotenv-preview.test.ts +39 -0
  217. package/studio/server/environment/dotenv-preview.ts +27 -0
  218. package/studio/server/environment/files.test.ts +53 -0
  219. package/studio/server/{state/env.ts → environment/files.ts} +7 -48
  220. package/studio/server/{state → handoff}/copy.ts +21 -6
  221. package/studio/server/handoff/service.test.ts +22 -0
  222. package/studio/server/{state/handoff.ts → handoff/service.ts} +21 -14
  223. package/studio/server/index.ts +1 -1
  224. package/studio/server/instances/active.ts +72 -0
  225. package/studio/server/instances/deploy-record.test.ts +31 -0
  226. package/studio/server/instances/deploy-record.ts +37 -0
  227. package/studio/server/instances/deploy.ts +56 -0
  228. package/studio/server/{state/instance.test.ts → instances/probe.test.ts} +25 -21
  229. package/studio/server/instances/probe.ts +67 -0
  230. package/studio/server/instances/status.test.ts +12 -0
  231. package/studio/server/instances/status.ts +49 -0
  232. package/studio/server/introspect/anatomy/client-tree.ts +57 -0
  233. package/studio/server/introspect/anatomy/env-fields.ts +66 -0
  234. package/studio/server/introspect/anatomy/schema-definition.ts +53 -0
  235. package/studio/server/introspect/anatomy/source.ts +181 -0
  236. package/studio/server/introspect/anatomy/views/routes.ts +114 -0
  237. package/studio/server/introspect/anatomy/views.ts +53 -0
  238. package/studio/server/introspect/anatomy-extras.test.ts +106 -35
  239. package/studio/server/introspect/anatomy-extras.ts +7 -726
  240. package/studio/server/introspect/anatomy.test.ts +22 -14
  241. package/studio/server/introspect/anatomy.ts +52 -28
  242. package/studio/server/introspect/bundle.ts +15 -9
  243. package/studio/server/introspect/canonical-schema.test.ts +93 -309
  244. package/studio/server/introspect/canonical-schema.ts +257 -425
  245. package/studio/server/introspect/config-preview.test.ts +32 -0
  246. package/studio/server/introspect/config-preview.ts +119 -0
  247. package/studio/server/introspect/core-extractor.ts +18 -127
  248. package/studio/server/introspect/core.ts +1 -2
  249. package/studio/server/introspect/diff.test.ts +18 -18
  250. package/studio/server/introspect/diff.ts +44 -90
  251. package/studio/server/introspect/extractor.ts +23 -116
  252. package/studio/server/introspect/hash.ts +5 -2
  253. package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
  254. package/studio/server/introspect/overlay-tsmorph.ts +7 -1305
  255. package/studio/server/introspect/overlay.test.ts +26 -54
  256. package/studio/server/introspect/overlay.ts +10 -37
  257. package/studio/server/introspect/revision.test.ts +54 -0
  258. package/studio/server/introspect/revision.ts +49 -0
  259. package/studio/server/introspect/runtime.test.ts +83 -143
  260. package/studio/server/introspect/runtime.ts +40 -11
  261. package/studio/server/introspect/schema-ir-json.test.ts +70 -0
  262. package/studio/server/introspect/schema-ir-json.ts +151 -0
  263. package/studio/server/introspect/schema-refs.test.ts +43 -88
  264. package/studio/server/introspect/schema-refs.ts +14 -49
  265. package/studio/server/introspect/source-overlay/annotations.ts +14 -0
  266. package/studio/server/introspect/source-overlay/handlers.ts +161 -0
  267. package/studio/server/introspect/source-overlay/kernel-calls.ts +46 -0
  268. package/studio/server/introspect/source-overlay/project.ts +248 -0
  269. package/studio/server/introspect/source-overlay/spans.ts +330 -0
  270. package/studio/server/json.ts +46 -0
  271. package/studio/server/lifecycle.ts +5 -1
  272. package/studio/server/sse.test.ts +26 -0
  273. package/studio/server/sse.ts +4 -1
  274. package/studio/server/state/baseline.test.ts +223 -7
  275. package/studio/server/state/baseline.ts +148 -34
  276. package/studio/server/state/comments.test.ts +66 -1
  277. package/studio/server/state/comments.ts +173 -10
  278. package/studio/server/state/context.ts +34 -2
  279. package/studio/server/state/documents.ts +42 -1
  280. package/studio/server/state/integrations.ts +27 -1
  281. package/studio/server/state/layout.test.ts +34 -0
  282. package/studio/server/state/layout.ts +44 -17
  283. package/studio/server/state/settings.ts +29 -22
  284. package/studio/server/state/store.test.ts +122 -0
  285. package/studio/server/state/store.ts +66 -17
  286. package/studio/server/state/visibility.ts +22 -11
  287. package/studio/server/views/model.test.ts +57 -0
  288. package/studio/server/views/model.ts +66 -0
  289. package/studio/server/views/runtime.ts +40 -0
  290. package/studio/server/views/selection-repository.test.ts +31 -0
  291. package/studio/server/views/selection-repository.ts +60 -0
  292. package/studio/server/views/session.test.ts +73 -0
  293. package/studio/server/views/session.ts +166 -0
  294. package/studio/server/views/target.test.ts +78 -0
  295. package/studio/server/views/target.ts +222 -0
  296. package/studio/server/watch.test.ts +11 -11
  297. package/studio/server/watch.ts +9 -5
  298. package/studio/server/workspace/catalog.test.ts +17 -0
  299. package/studio/server/workspace/catalog.ts +43 -0
  300. package/studio/server/{state → workspace}/create.test.ts +1 -1
  301. package/studio/server/{state → workspace}/create.ts +1 -1
  302. package/studio/server/{state → workspace}/git.ts +1 -1
  303. package/studio/server/workspace/updates.ts +83 -0
  304. package/studio/server/workspace-state.ts +1 -1
  305. package/studio/server/workspace-watch.ts +2 -2
  306. package/studio/shared/contracts/README.md +16 -0
  307. package/studio/shared/contracts/agent.ts +270 -0
  308. package/studio/shared/contracts/runtime.ts +50 -0
  309. package/studio/shared/contracts/schema.ts +393 -0
  310. package/studio/shared/contracts/surface.test.ts +317 -0
  311. package/studio/shared/contracts/workspace.ts +265 -0
  312. package/studio/shared/layout.test.ts +116 -0
  313. package/studio/shared/schema/identity.test.ts +34 -0
  314. package/studio/shared/schema/identity.ts +68 -0
  315. package/studio/shared/types.ts +11 -1099
  316. package/studio/tsconfig.json +1 -1
  317. package/viewer/dist/main.js +40 -61
  318. package/.check-workspace.cjs +0 -40
  319. package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
  320. package/studio/client/dist/assets/elk-api-D2xgMJvi.js +0 -1
  321. package/studio/client/dist/assets/index-BMdnsIJA.css +0 -1
  322. package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
  323. package/studio/client/dist/assets/index-D-vRV8w7.js +0 -8
  324. package/studio/client/dist/assets/index-LGSWRrk8.js +0 -81
  325. package/studio/server/state/catalog.ts +0 -117
  326. package/studio/server/state/instance.ts +0 -280
  327. package/studio/server/state/updates.ts +0 -63
  328. package/studio/server/state/views.test.ts +0 -286
  329. package/studio/server/state/views.ts +0 -535
@@ -0,0 +1,122 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import { decodeBundleCacheEntry } from './cache'
4
+
5
+ function entry(): Record<string, unknown> {
6
+ return {
7
+ version: 5,
8
+ key: 'cache-key',
9
+ futureEntryField: { version: 5 },
10
+ bundle: {
11
+ domainId: 'notes',
12
+ renderFingerprint: 'render-hash',
13
+ schemaMode: 'canonical-preview',
14
+ extractedBy: 'runtime-bun',
15
+ depsInstalled: true,
16
+ ir: {
17
+ format: 'astrale.dsl',
18
+ version: 'v1',
19
+ domain: 'notes.example.dev',
20
+ classes: {},
21
+ importsByKey: {},
22
+ importedClassesByKey: {},
23
+ functions: {},
24
+ views: {},
25
+ policies: {},
26
+ dependencies: [],
27
+ core: {},
28
+ },
29
+ overlay: {
30
+ origin: 'notes.example.dev',
31
+ requires: [],
32
+ crossDomainImports: [],
33
+ mixins: [],
34
+ handlerLinks: [],
35
+ sourceSpans: {},
36
+ annotations: [],
37
+ },
38
+ schemaRoot: {
39
+ format: 'astrale.dsl',
40
+ version: 'v1',
41
+ origin: 'notes.example.dev',
42
+ },
43
+ error: null,
44
+ extractedAt: '2026-08-20T00:00:00.000Z',
45
+ futureBundleField: true,
46
+ },
47
+ }
48
+ }
49
+
50
+ test('bundle cache admits known structure while ignoring future fields', () => {
51
+ const decoded = decodeBundleCacheEntry(entry())
52
+
53
+ expect(decoded).toMatchObject({
54
+ version: 5,
55
+ key: 'cache-key',
56
+ bundle: { domainId: 'notes', schemaMode: 'canonical-preview' },
57
+ })
58
+ expect(decoded && 'futureEntryField' in decoded).toBe(false)
59
+ expect(decoded && 'futureBundleField' in decoded.bundle).toBe(false)
60
+ })
61
+
62
+ function canonicalEntry(): Record<string, unknown> {
63
+ const value = entry()
64
+ const bundle = value.bundle as Record<string, unknown>
65
+ bundle.schemaMode = 'canonical-admitted'
66
+ bundle.schemaRevision = `sha256:${'a'.repeat(64)}`
67
+ bundle.ir = {
68
+ format: 'astrale.dsl',
69
+ version: 'v1',
70
+ domain: 'notes.example.dev',
71
+ classes: {},
72
+ importsByKey: {},
73
+ importedClassesByKey: {},
74
+ functions: {},
75
+ views: {},
76
+ policies: {},
77
+ dependencies: [],
78
+ core: {},
79
+ }
80
+ bundle.schemaRoot = {
81
+ format: 'astrale.dsl',
82
+ version: 'v1',
83
+ origin: 'notes.example.dev',
84
+ }
85
+ return value
86
+ }
87
+
88
+ test('canonical-admitted cache entries require their root and a valid revision', () => {
89
+ expect(decodeBundleCacheEntry(canonicalEntry())).toBeDefined()
90
+
91
+ const missingRoot = canonicalEntry()
92
+ delete (missingRoot.bundle as Record<string, unknown>).schemaRoot
93
+ expect(decodeBundleCacheEntry(missingRoot)).toBeUndefined()
94
+
95
+ const invalidRoot = canonicalEntry()
96
+ const invalidRootBundle = invalidRoot.bundle as Record<string, unknown>
97
+ invalidRootBundle.schemaRoot = { canonical: true }
98
+ expect(decodeBundleCacheEntry(invalidRoot)).toBeUndefined()
99
+
100
+ const missingRevision = canonicalEntry()
101
+ delete (missingRevision.bundle as Record<string, unknown>).schemaRevision
102
+ expect(decodeBundleCacheEntry(missingRevision)).toBeUndefined()
103
+
104
+ const invalidRevision = canonicalEntry()
105
+ const invalidRevisionBundle = invalidRevision.bundle as Record<string, unknown>
106
+ invalidRevisionBundle.schemaRevision = 'sha256:not-a-revision'
107
+ expect(decodeBundleCacheEntry(invalidRevision)).toBeUndefined()
108
+ })
109
+
110
+ test('bundle cache rejects corrupt nested business shapes', () => {
111
+ const corruptIr = entry()
112
+ const bundle = corruptIr.bundle as Record<string, unknown>
113
+ const ir = bundle.ir as Record<string, unknown>
114
+ ir.classes = { Broken: null }
115
+ expect(decodeBundleCacheEntry(corruptIr)).toBeUndefined()
116
+
117
+ const corruptOverlay = entry()
118
+ const secondBundle = corruptOverlay.bundle as Record<string, unknown>
119
+ const overlay = secondBundle.overlay as Record<string, unknown>
120
+ overlay.handlerLinks = [{ owner: 'Thing' }]
121
+ expect(decodeBundleCacheEntry(corruptOverlay)).toBeUndefined()
122
+ })
@@ -7,13 +7,17 @@ import { createHash } from 'node:crypto'
7
7
  import { existsSync, readFileSync } from 'node:fs'
8
8
  import { join, relative } from 'node:path'
9
9
 
10
- import type { DomainAnatomy, StudioCore, StudioSchemaBundle } from '../shared/types'
10
+ import type { DomainAnatomy, SchemaOverlay, StudioCore, StudioSchemaBundle } from '../shared/types'
11
11
 
12
+ import { isSchemaRevision } from '../shared/types'
12
13
  import { invalidateClientPackage, resolveClientPackage } from './client-package'
13
14
  import { getDomain } from './domain'
14
15
  import { buildAnatomy } from './introspect/anatomy'
15
16
  import { buildBundle } from './introspect/bundle'
17
+ import { isCanonicalDomainSchemaV1 } from './introspect/canonical-schema'
16
18
  import { buildCore } from './introspect/core'
19
+ import { decodeSchemaIR } from './introspect/schema-ir-json'
20
+ import { asBoolean, asFiniteNumber, asJsonRecord, asString, asStringArray } from './json'
17
21
  import { hashAnatomyFiles } from './state/baseline'
18
22
  import { readJson, writeJson } from './state/store'
19
23
 
@@ -22,7 +26,7 @@ const anatomies = new Map<string, Promise<DomainAnatomy>>()
22
26
  const cores = new Map<string, StudioCore>()
23
27
 
24
28
  const BUNDLE_CACHE_FILE = '.cache/schema-bundle.json'
25
- const BUNDLE_CACHE_VERSION = 3
29
+ const BUNDLE_CACHE_VERSION = 5
26
30
  const LOCKFILES = ['bun.lock', 'pnpm-lock.yaml', 'package-lock.json', 'yarn.lock']
27
31
  const TOOL_INPUTS = [
28
32
  'cache.ts',
@@ -33,6 +37,11 @@ const TOOL_INPUTS = [
33
37
  'introspect/canonical-schema.ts',
34
38
  'introspect/overlay.ts',
35
39
  'introspect/overlay-tsmorph.ts',
40
+ 'introspect/source-overlay/annotations.ts',
41
+ 'introspect/source-overlay/handlers.ts',
42
+ 'introspect/source-overlay/kernel-calls.ts',
43
+ 'introspect/source-overlay/project.ts',
44
+ 'introspect/source-overlay/spans.ts',
36
45
  'introspect/hash.ts',
37
46
  'state/baseline.ts',
38
47
  '../shared/types.ts',
@@ -44,6 +53,147 @@ interface BundleCacheEntry {
44
53
  bundle: StudioSchemaBundle
45
54
  }
46
55
 
56
+ function isCrossDomainImport(value: unknown): boolean {
57
+ const record = asJsonRecord(value)
58
+ const ref = asJsonRecord(record?.ref)
59
+ return (
60
+ typeof record?.name === 'string' &&
61
+ typeof record.origin === 'string' &&
62
+ ref?.kind === 'class' &&
63
+ typeof ref.origin === 'string' &&
64
+ typeof ref.name === 'string'
65
+ )
66
+ }
67
+
68
+ function isHandlerLink(value: unknown): boolean {
69
+ const record = asJsonRecord(value)
70
+ return (
71
+ typeof record?.owner === 'string' &&
72
+ ['class', 'function'].includes(String(record.ownerKind)) &&
73
+ ['action', 'workflow'].includes(String(record.kind)) &&
74
+ typeof record.method === 'string' &&
75
+ typeof record.static === 'boolean' &&
76
+ typeof record.implemented === 'boolean'
77
+ )
78
+ }
79
+
80
+ function isSourceSpan(value: unknown): boolean {
81
+ const record = asJsonRecord(value)
82
+ return (
83
+ typeof record?.file === 'string' &&
84
+ asFiniteNumber(record.startLine) !== undefined &&
85
+ asFiniteNumber(record.endLine) !== undefined &&
86
+ (record.doc === undefined || typeof record.doc === 'string')
87
+ )
88
+ }
89
+
90
+ function isAnnotation(value: unknown): boolean {
91
+ const record = asJsonRecord(value)
92
+ return (
93
+ typeof record?.target === 'string' &&
94
+ (record.severity === 'warn' || record.severity === 'info') &&
95
+ (record.code === 'COMPILE_ERROR' || record.code === 'EDGE_PROP_TYPE_MISSING') &&
96
+ typeof record.message === 'string'
97
+ )
98
+ }
99
+
100
+ function decodeOverlay(value: unknown): SchemaOverlay | undefined {
101
+ const record = asJsonRecord(value)
102
+ const requires = asStringArray(record?.requires)
103
+ const crossDomainImports = record?.crossDomainImports
104
+ const mixins = record?.mixins
105
+ const handlerLinks = record?.handlerLinks
106
+ const sourceSpans = asJsonRecord(record?.sourceSpans)
107
+ const annotations = record?.annotations
108
+ if (
109
+ typeof record?.origin !== 'string' ||
110
+ !requires ||
111
+ !Array.isArray(crossDomainImports) ||
112
+ !crossDomainImports.every(isCrossDomainImport) ||
113
+ !Array.isArray(mixins) ||
114
+ !mixins.every(isCrossDomainImport) ||
115
+ !Array.isArray(handlerLinks) ||
116
+ !handlerLinks.every(isHandlerLink) ||
117
+ !sourceSpans ||
118
+ !Object.values(sourceSpans).every(isSourceSpan) ||
119
+ !Array.isArray(annotations) ||
120
+ !annotations.every(isAnnotation)
121
+ ) {
122
+ return undefined
123
+ }
124
+ return record as unknown as SchemaOverlay
125
+ }
126
+
127
+ function decodeBundle(value: unknown): StudioSchemaBundle | undefined {
128
+ const record = asJsonRecord(value)
129
+ if (!record) return undefined
130
+ const domainId = asString(record.domainId)
131
+ const renderFingerprint = asString(record.renderFingerprint)
132
+ const schemaMode = (['canonical-admitted', 'canonical-preview', 'unavailable'] as const).find(
133
+ (candidate) => candidate === record.schemaMode,
134
+ )
135
+ const extractedBy = record.extractedBy
136
+ const depsInstalled = asBoolean(record.depsInstalled)
137
+ const ir = record.ir === null ? null : decodeSchemaIR(record.ir)
138
+ const overlay = decodeOverlay(record.overlay)
139
+ const extractedAt = asString(record.extractedAt)
140
+ if (
141
+ !domainId ||
142
+ renderFingerprint === undefined ||
143
+ !schemaMode ||
144
+ (extractedBy !== 'runtime-bun' && extractedBy !== 'static-tsmorph-fallback') ||
145
+ depsInstalled === undefined ||
146
+ ir === undefined ||
147
+ !overlay ||
148
+ !extractedAt
149
+ ) {
150
+ return undefined
151
+ }
152
+ const schemaRevision = record.schemaRevision
153
+ if (schemaRevision !== undefined && !isSchemaRevision(schemaRevision)) return undefined
154
+ if (
155
+ schemaMode === 'canonical-admitted' &&
156
+ (!isSchemaRevision(schemaRevision) || !isCanonicalDomainSchemaV1(record.schemaRoot))
157
+ ) {
158
+ return undefined
159
+ }
160
+ const errorRecord = record.error === null ? null : asJsonRecord(record.error)
161
+ const errorMessage = asString(errorRecord?.message)
162
+ if (record.error !== undefined && record.error !== null && errorMessage === undefined) {
163
+ return undefined
164
+ }
165
+ return {
166
+ domainId,
167
+ renderFingerprint,
168
+ schemaMode,
169
+ ...(schemaRevision === undefined ? {} : { schemaRevision }),
170
+ extractedBy,
171
+ depsInstalled,
172
+ ir,
173
+ ...(record.schemaRoot === undefined ? {} : { schemaRoot: record.schemaRoot }),
174
+ overlay,
175
+ ...(record.error === undefined
176
+ ? {}
177
+ : record.error === null
178
+ ? { error: null }
179
+ : {
180
+ error: {
181
+ message: errorMessage!,
182
+ ...(typeof errorRecord!.file === 'string' ? { file: errorRecord!.file } : {}),
183
+ },
184
+ }),
185
+ extractedAt,
186
+ }
187
+ }
188
+
189
+ export function decodeBundleCacheEntry(value: unknown): BundleCacheEntry | undefined {
190
+ const record = asJsonRecord(value)
191
+ const version = asFiniteNumber(record?.version)
192
+ const key = asString(record?.key)
193
+ const bundle = decodeBundle(record?.bundle)
194
+ return version !== undefined && key !== undefined && bundle ? { version, key, bundle } : undefined
195
+ }
196
+
47
197
  function hashFileIfPresent(hash: ReturnType<typeof createHash>, label: string, file: string): void {
48
198
  try {
49
199
  if (!existsSync(file)) return
@@ -77,7 +227,7 @@ function bundleCacheKey(root: string, schemaDirName: string): string {
77
227
  }
78
228
 
79
229
  function readCachedBundle(root: string, key: string): StudioSchemaBundle | null {
80
- const entry = readJson<BundleCacheEntry | null>(root, BUNDLE_CACHE_FILE, null)
230
+ const entry = readJson(root, BUNDLE_CACHE_FILE, decodeBundleCacheEntry, null)
81
231
  if (!entry || entry.version !== BUNDLE_CACHE_VERSION || entry.key !== key || !entry.bundle)
82
232
  return null
83
233
  return entry.bundle
@@ -119,12 +269,14 @@ export async function getAnatomy(id: string, rebuild = false): Promise<DomainAna
119
269
  const h = getDomain(id)
120
270
  if (!h) return null
121
271
  if (!rebuild && anatomies.has(id)) return anatomies.get(id)!
122
- const anatomy = resolveClientPackage(h.root, rebuild).then((client) =>
123
- buildAnatomy({
124
- root: h.root,
125
- schemaDirName: h.schemaDirName,
126
- clientDir: client.status === 'available' ? client.sourceDir : undefined,
127
- }),
272
+ const anatomy = Promise.all([resolveClientPackage(h.root, rebuild), getBundle(id, rebuild)]).then(
273
+ ([client, bundle]) =>
274
+ buildAnatomy({
275
+ root: h.root,
276
+ schemaDirName: h.schemaDirName,
277
+ clientDir: client.status === 'available' ? client.sourceDir : undefined,
278
+ canonicalViews: bundle?.ir?.format === 'astrale.dsl' ? (bundle.ir.views ?? {}) : undefined,
279
+ }),
128
280
  )
129
281
  anatomies.set(id, anatomy)
130
282
  return anatomy
@@ -146,7 +298,6 @@ export function invalidate(id: string, what: 'schema' | 'anatomy' | 'all'): void
146
298
  const domain = getDomain(id)
147
299
  if (domain) invalidateClientPackage(domain.root)
148
300
  }
149
- // Core is derived from the canonical schema (or the legacy composition entry),
150
- // both of which belong to the anatomy invalidation set.
301
+ // Core is derived from the canonical Schema and belongs to the anatomy set.
151
302
  if (what !== 'schema') cores.delete(id)
152
303
  }