@astrale-os/cli 1.0.0-beta.2 → 1.0.0-beta.20

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 (282) hide show
  1. package/README.md +16 -1
  2. package/dist/astrale.js +51105 -58057
  3. package/dist/public/connect-core.js +2085 -30702
  4. package/dist/public/keys/index.js +884 -29476
  5. package/dist/public/paths/index.js +13101 -41452
  6. package/dist/types/admin/contract.d.ts +26 -0
  7. package/dist/types/admin/instance/client.d.ts +20 -0
  8. package/dist/types/admin/instance/index.d.ts +2 -0
  9. package/dist/types/admin/instance/model.d.ts +6 -10
  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 +25 -173
  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/output.d.ts +1 -1
  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 +109 -0
  38. package/dist/types/lib/validation.d.ts +4 -1
  39. package/package.json +15 -20
  40. package/src/__tests__/fixtures/publication.ts +20 -0
  41. package/src/admin/.spec/architecture.md +12 -5
  42. package/src/admin/__tests__/fixture.ts +25 -0
  43. package/src/admin/catalog/.spec/api.d.ts +1 -3
  44. package/src/admin/catalog/.spec/architecture.md +5 -4
  45. package/src/admin/catalog/__tests__/client.test.ts +143 -69
  46. package/src/admin/catalog/client.ts +40 -54
  47. package/src/admin/catalog/model.ts +3 -4
  48. package/src/admin/contract.ts +46 -0
  49. package/src/admin/graph/.spec/api.d.ts +4 -10
  50. package/src/admin/graph/nodes.ts +1 -1
  51. package/src/admin/instance/.spec/api.d.ts +5 -9
  52. package/src/admin/instance/.spec/architecture.md +7 -7
  53. package/src/admin/instance/__tests__/client.test.ts +157 -175
  54. package/src/admin/instance/client.ts +39 -128
  55. package/src/admin/instance/index.ts +1 -2
  56. package/src/admin/instance/model.ts +12 -18
  57. package/src/commands/__tests__/admin-instance.test.ts +11 -8
  58. package/src/commands/__tests__/call-describe.test.ts +46 -33
  59. package/src/commands/__tests__/call.test.ts +34 -0
  60. package/src/commands/__tests__/domain-install-operation.test.ts +7 -22
  61. package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
  62. package/src/commands/__tests__/domain-list.test.ts +5 -25
  63. package/src/commands/__tests__/install-direct.test.ts +115 -8
  64. package/src/commands/__tests__/install-identity-override.test.ts +3 -14
  65. package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
  66. package/src/commands/__tests__/instance-status.test.ts +139 -0
  67. package/src/commands/__tests__/introspect-parse.test.ts +80 -1
  68. package/src/commands/__tests__/logs.test.ts +250 -2
  69. package/src/commands/__tests__/read-commands.test.ts +62 -10
  70. package/src/commands/__tests__/token-ttl.test.ts +49 -4
  71. package/src/commands/__tests__/update.test.ts +25 -3
  72. package/src/commands/__tests__/view.test.ts +49 -0
  73. package/src/commands/call-describe.ts +60 -68
  74. package/src/commands/call.ts +71 -59
  75. package/src/commands/domain/install.ts +54 -35
  76. package/src/commands/domain/list.ts +1 -1
  77. package/src/commands/domain/publish.ts +14 -11
  78. package/src/commands/domain/uninstall.ts +2 -2
  79. package/src/commands/get.ts +14 -15
  80. package/src/commands/identity/__tests__/register.test.ts +3 -3
  81. package/src/commands/identity/register.ts +5 -4
  82. package/src/commands/instance/create.ts +8 -16
  83. package/src/commands/instance/delete.ts +12 -9
  84. package/src/commands/instance/list.ts +2 -2
  85. package/src/commands/instance/status.ts +66 -20
  86. package/src/commands/instance/use.ts +16 -12
  87. package/src/commands/introspect.ts +48 -35
  88. package/src/commands/logs.ts +123 -33
  89. package/src/commands/mutate.ts +6 -5
  90. package/src/commands/query.ts +35 -32
  91. package/src/commands/token.ts +42 -27
  92. package/src/commands/ui/__tests__/commands.test.ts +152 -0
  93. package/src/commands/ui/add.ts +51 -0
  94. package/src/commands/ui/doctor.ts +13 -0
  95. package/src/commands/ui/init.ts +38 -0
  96. package/src/commands/ui/list.ts +26 -0
  97. package/src/commands/ui/preset-apply.ts +19 -0
  98. package/src/commands/ui/preset-list.ts +12 -0
  99. package/src/commands/ui/shared.ts +25 -0
  100. package/src/commands/update.ts +31 -11
  101. package/src/commands/view.ts +82 -37
  102. package/src/connection/.spec/api.d.ts +7 -8
  103. package/src/connection/.spec/architecture.md +5 -4
  104. package/src/connection/.spec/flows/session.ts +2 -2
  105. package/src/connection/.spec/laws/connection.ts +6 -2
  106. package/src/connection/.spec/layout.ts +1 -0
  107. package/src/connection/__tests__/credential.test.ts +20 -1
  108. package/src/connection/__tests__/errors.test.ts +27 -20
  109. package/src/connection/__tests__/exchange.test.ts +30 -1
  110. package/src/connection/__tests__/failure-fixtures.ts +39 -0
  111. package/src/connection/__tests__/failure-safety.test.ts +75 -0
  112. package/src/connection/__tests__/self.test.ts +31 -11
  113. package/src/connection/__tests__/session.test.ts +3 -3
  114. package/src/connection/__tests__/target.test.ts +1 -0
  115. package/src/connection/auth.ts +13 -0
  116. package/src/connection/call.ts +1 -1
  117. package/src/connection/credential.ts +2 -2
  118. package/src/connection/errors.ts +1 -355
  119. package/src/connection/exchange.ts +47 -23
  120. package/src/connection/failure/classify.ts +110 -0
  121. package/src/connection/failure/index.ts +18 -0
  122. package/src/connection/failure/model.ts +28 -0
  123. package/src/connection/failure/render.ts +116 -0
  124. package/src/connection/reasons.ts +16 -0
  125. package/src/connection/self.ts +39 -21
  126. package/src/connection/session.ts +19 -8
  127. package/src/errors.ts +3 -2
  128. package/src/graph/.spec/api.d.ts +12 -5
  129. package/src/graph/.spec/layout.ts +9 -1
  130. package/src/graph/__tests__/mutation.test.ts +2 -2
  131. package/src/graph/__tests__/query.test.ts +25 -6
  132. package/src/graph/class.ts +30 -0
  133. package/src/graph/index.ts +1 -0
  134. package/src/graph/query.ts +21 -32
  135. package/src/identity/.spec/api.d.ts +2 -2
  136. package/src/identity/__tests__/registration.test.ts +1 -1
  137. package/src/identity/registration.ts +3 -3
  138. package/src/keys/__tests__/keys.test.ts +4 -0
  139. package/src/keys/credential.ts +1 -0
  140. package/src/lib/__tests__/admin-target.test.ts +12 -0
  141. package/src/lib/__tests__/binary.test.ts +16 -1
  142. package/src/lib/__tests__/command-dx.test.ts +1 -1
  143. package/src/lib/__tests__/domain-publication.test.ts +10 -16
  144. package/src/lib/__tests__/idp.test.ts +34 -4
  145. package/src/lib/__tests__/instance-candidates.test.ts +1 -3
  146. package/src/lib/__tests__/instance-target.test.ts +7 -39
  147. package/src/lib/__tests__/log-errors.test.ts +64 -0
  148. package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
  149. package/src/lib/__tests__/update.test.ts +264 -11
  150. package/src/lib/__tests__/view-assets.test.ts +14 -0
  151. package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
  152. package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
  153. package/src/lib/__tests__/view-open-intent.test.ts +0 -2
  154. package/src/lib/__tests__/view-server.test.ts +26 -3
  155. package/src/lib/__tests__/view-session.test.ts +2 -1
  156. package/src/lib/admin-instance.ts +14 -8
  157. package/src/lib/admin-target.ts +21 -2
  158. package/src/lib/binary.ts +22 -5
  159. package/src/lib/command-dx.ts +9 -9
  160. package/src/lib/domain-publication.ts +9 -7
  161. package/src/lib/failure-debug.ts +26 -0
  162. package/src/lib/idp.ts +34 -3
  163. package/src/lib/instance-candidates.ts +2 -2
  164. package/src/lib/instance-target.ts +3 -14
  165. package/src/lib/instance.ts +16 -11
  166. package/src/lib/log.ts +16 -9
  167. package/src/lib/output.ts +1 -1
  168. package/src/lib/proc.ts +7 -2
  169. package/src/lib/provision-instance.ts +34 -57
  170. package/src/lib/update.ts +298 -45
  171. package/src/lib/validation.ts +2 -2
  172. package/src/lib/view/assets.ts +11 -2
  173. package/src/lib/view/external-open-intent.ts +41 -0
  174. package/src/lib/view/external-open-origins.ts +37 -0
  175. package/src/lib/view/host-capabilities.ts +17 -0
  176. package/src/lib/view/resolve.ts +1 -1
  177. package/src/lib/view/server.ts +18 -3
  178. package/src/lib/view/session.ts +2 -0
  179. package/src/program/.spec/api.d.ts +1 -0
  180. package/src/program/__tests__/program.test.ts +163 -6
  181. package/src/program/argv.ts +14 -0
  182. package/src/program/build.ts +26 -4
  183. package/src/program/command.ts +1 -0
  184. package/src/program/index.ts +1 -0
  185. package/src/program/registry.ts +2 -1
  186. package/src/setup/__tests__/instance-step.test.ts +27 -10
  187. package/src/setup/steps/instance.ts +22 -15
  188. package/src/telemetry/__tests__/recorder.test.ts +1 -0
  189. package/src/telemetry/recorder.ts +9 -1
  190. package/src/ui/.spec/api.d.ts +14 -0
  191. package/src/ui/.spec/architecture.md +10 -0
  192. package/src/ui/.spec/laws.ts +36 -0
  193. package/src/ui/.spec/layout.ts +16 -0
  194. package/src/ui/__tests__/ui.test.ts +981 -0
  195. package/src/ui/index.ts +13 -0
  196. package/src/ui/lock.ts +87 -0
  197. package/src/ui/model.ts +83 -0
  198. package/src/ui/operations.ts +710 -0
  199. package/src/ui/project.ts +219 -0
  200. package/src/ui/release.ts +321 -0
  201. package/src/ui/runner.ts +18 -0
  202. package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
  203. package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
  204. package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
  205. package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  206. package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
  207. package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
  208. package/studio/client/dist/index.html +3 -2
  209. package/studio/package.json +7 -13
  210. package/studio/server/agent/ask.test.ts +1 -1
  211. package/studio/server/agent/bridge/grant.test.ts +1 -1
  212. package/studio/server/agent/bridge/routes.test.ts +1 -1
  213. package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
  214. package/studio/server/agent/harness/gateway/token.test.ts +1 -1
  215. package/studio/server/agent/harness/gateway/token.ts +3 -3
  216. package/studio/server/agent/prompts/anchors.test.ts +32 -36
  217. package/studio/server/agent/prompts/anchors.ts +20 -49
  218. package/studio/server/agent/prompts/system.test.ts +3 -2
  219. package/studio/server/agent/prompts/system.ts +3 -3
  220. package/studio/server/agent/routes.test.ts +1 -1
  221. package/studio/server/agent/run/coordinator.test.ts +1 -1
  222. package/studio/server/api/canvas.ts +0 -1
  223. package/studio/server/api-agent-loadout.test.ts +1 -1
  224. package/studio/server/api.test.ts +1 -1
  225. package/studio/server/cache.test.ts +23 -12
  226. package/studio/server/cache.ts +12 -15
  227. package/studio/server/domain.test.ts +50 -68
  228. package/studio/server/domain.ts +82 -63
  229. package/studio/server/handoff/copy.ts +1 -1
  230. package/studio/server/index.ts +1 -1
  231. package/studio/server/instances/probe.test.ts +1 -1
  232. package/studio/server/introspect/anatomy/views/routes.ts +46 -37
  233. package/studio/server/introspect/anatomy/views.ts +9 -14
  234. package/studio/server/introspect/anatomy-extras.test.ts +26 -42
  235. package/studio/server/introspect/anatomy.test.ts +22 -14
  236. package/studio/server/introspect/anatomy.ts +6 -9
  237. package/studio/server/introspect/bundle.ts +0 -3
  238. package/studio/server/introspect/canonical-schema.test.ts +94 -389
  239. package/studio/server/introspect/canonical-schema.ts +227 -457
  240. package/studio/server/introspect/core-extractor.ts +18 -127
  241. package/studio/server/introspect/core.ts +1 -2
  242. package/studio/server/introspect/diff.test.ts +11 -7
  243. package/studio/server/introspect/diff.ts +18 -55
  244. package/studio/server/introspect/extractor.ts +22 -117
  245. package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
  246. package/studio/server/introspect/overlay.test.ts +26 -54
  247. package/studio/server/introspect/overlay.ts +10 -37
  248. package/studio/server/introspect/revision.test.ts +1 -1
  249. package/studio/server/introspect/revision.ts +1 -1
  250. package/studio/server/introspect/runtime.test.ts +65 -183
  251. package/studio/server/introspect/runtime.ts +8 -25
  252. package/studio/server/introspect/schema-ir-json.test.ts +70 -0
  253. package/studio/server/introspect/schema-ir-json.ts +38 -68
  254. package/studio/server/introspect/schema-refs.test.ts +43 -88
  255. package/studio/server/introspect/schema-refs.ts +14 -49
  256. package/studio/server/introspect/source-overlay/handlers.ts +116 -636
  257. package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
  258. package/studio/server/introspect/source-overlay/spans.ts +15 -45
  259. package/studio/server/state/baseline.test.ts +14 -9
  260. package/studio/server/state/baseline.ts +3 -5
  261. package/studio/server/state/visibility.ts +1 -5
  262. package/studio/server/views/model.ts +3 -3
  263. package/studio/server/views/target.test.ts +17 -74
  264. package/studio/server/views/target.ts +24 -50
  265. package/studio/server/watch.test.ts +11 -11
  266. package/studio/server/watch.ts +8 -4
  267. package/studio/server/workspace-watch.ts +2 -2
  268. package/studio/shared/contracts/schema.ts +38 -107
  269. package/studio/shared/contracts/surface.test.ts +13 -15
  270. package/studio/shared/contracts/workspace.ts +2 -11
  271. package/studio/shared/schema/identity.test.ts +20 -44
  272. package/studio/shared/schema/identity.ts +9 -23
  273. package/viewer/dist/main.js +30 -51
  274. package/.check-workspace.cjs +0 -40
  275. package/src/admin/binding.ts +0 -168
  276. package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
  277. package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
  278. package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
  279. package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
  280. package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
  281. package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
  282. package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
@@ -11,7 +11,6 @@ import { admittedBundleRevision } from './revision'
11
11
  import { coreExtract, runtimeExtract } from './runtime'
12
12
 
13
13
  const roots: string[] = []
14
-
15
14
  afterEach(() => {
16
15
  while (roots.length > 0) rmSync(roots.pop()!, { recursive: true, force: true })
17
16
  })
@@ -22,170 +21,114 @@ function fixtureRoot(label: string): string {
22
21
  mkdirSync(join(root, 'schema'), { recursive: true })
23
22
  writeFileSync(join(root, 'package.json'), '{"type":"module"}\n')
24
23
  writeFileSync(join(root, 'astrale.config.ts'), 'export default {}\n')
25
- return root
26
- }
27
-
28
- function linkCurrentSdk(root: string): void {
29
24
  const sdk = realpathSync(join(import.meta.dir, '../../../node_modules/@astrale-os/sdk'))
30
25
  const scope = join(root, 'node_modules', '@astrale-os')
31
26
  mkdirSync(scope, { recursive: true })
32
27
  symlinkSync(sdk, join(scope, 'sdk'), 'dir')
28
+ return root
33
29
  }
34
30
 
35
31
  function currentFixture(): DomainHandle {
36
32
  const root = fixtureRoot('canonical-runtime')
37
- linkCurrentSdk(root)
38
- const schemaIndex = join(root, 'schema', 'index.ts')
39
- const domainFile = join(root, 'implementation.ts')
33
+ const schemaIndex = join(root, 'schema/index.ts')
34
+ const applicationFile = join(root, 'application.ts')
40
35
  writeFileSync(
41
36
  schemaIndex,
42
37
  `
43
- import {
44
- bundle, defineSchema, domain, edge, edgeClass, fn, node, nodeClass,
45
- nodeInterface, output, property, view,
46
- } from '@astrale-os/sdk/schema'
47
-
48
- const Named = nodeInterface()
49
- export const Directory = defineSchema('directory.runtime.test', {
50
- interfaces: { Named },
51
- })
52
-
53
- const rename = fn({
54
- input: {
55
- type: 'object', properties: { title: { type: 'string' } },
56
- required: ['title'], additionalProperties: false,
57
- },
58
- output: { type: 'boolean' },
59
- auth: 'authenticated',
60
- })
61
- const Document = nodeClass({
62
- implements: [Named],
63
- properties: { title: property({ type: 'string' }, { required: true }) },
64
- methods: { rename },
65
- })
66
- const owned_by = edgeClass.directed({
67
- source: { as: 'document', accepts: [Document], outgoing: '0..*' },
68
- target: { as: 'owner', accepts: [Document], incoming: '0..*' },
69
- })
70
- const welcome = node(Document, { title: 'Welcome' })
71
-
72
- export const schema = defineSchema('documents.runtime.test', {
73
- dependencies: [Directory],
74
- classes: { Document, owned_by },
75
- functions: {
76
- exportAll: fn({
77
- input: { type: 'object', properties: {}, required: [], additionalProperties: false },
78
- output: output.binary(),
79
- auth: 'authenticated',
80
- }),
81
- },
82
- views: { editor: view({ target: Document, auth: 'optional' }) },
83
- core: {
84
- nodes: { welcome },
85
- edges: [edge(welcome, owned_by, domain(), {})],
86
- },
87
- })
88
- export const installedBundle = bundle.create(schema)
89
- `,
38
+ import { bundle, core, defineSchema, nodeClass, view } from '@astrale-os/sdk/schema'
39
+ const Named = nodeClass({ properties: {} })
40
+ export const DirectorySchema = defineSchema('directory.runtime.test', {
41
+ classes: { Named },
42
+ })
43
+ const Document = nodeClass({ extends: [Named], properties: {} })
44
+ const welcome = core.node(Document, {})
45
+ export const schema = defineSchema('documents.runtime.test', {
46
+ dependencies: { directory: DirectorySchema },
47
+ classes: { Document },
48
+ views: { editor: view({ target: Document }) },
49
+ core: { nodes: { welcome } },
50
+ })
51
+ export const installedBundle = bundle.create(schema)
52
+ `,
90
53
  )
91
- // Canonical core extraction must not import the effectful composition entry.
92
- writeFileSync(domainFile, `throw new Error('composition entry was imported')\n`)
54
+ writeFileSync(applicationFile, `throw new Error('Application entry was imported')\n`)
93
55
  return {
94
56
  id: 'documents-runtime-test',
95
57
  root,
96
58
  configFile: join(root, 'astrale.config.ts'),
97
- domainFile,
59
+ applicationFile,
98
60
  schemaDirName: 'schema',
99
61
  schemaDir: join(root, 'schema'),
100
62
  schemaIndex,
101
63
  }
102
64
  }
103
65
 
104
- describe('current SDK schema extractor', () => {
105
- test('resolves the domain SDK, returns its raw root, and projects current members', async () => {
66
+ describe('SDK V1 schema extractor', () => {
67
+ test('uses the authored SDK for admission and projects exact imported Classes', async () => {
106
68
  const handle = currentFixture()
107
69
  const result = await runtimeExtract(handle.schemaIndex, handle.root)
108
-
109
- expect(result.ok).toBe(true)
110
- expect(result.schemaMode).toBe('canonical-admitted')
111
- expect(result.revision).toMatch(/^sha256:[0-9a-f]{64}$/)
112
- expect(result.root).toMatchObject({
113
- format: 'astrale.dsl',
114
- version: 'v1',
115
- origin: 'documents.runtime.test',
116
- })
117
- expect(result.ir).toMatchObject({
118
- format: 'astrale.dsl',
119
- domain: 'documents.runtime.test',
120
- functions: {
121
- exportAll: {
122
- static: true,
123
- inheritance: 'default',
124
- output: { mode: 'binary' },
125
- },
70
+ expect(result).toMatchObject({
71
+ ok: true,
72
+ schemaMode: 'canonical-admitted',
73
+ root: {
74
+ format: 'astrale.dsl',
75
+ version: 'v1',
76
+ origin: 'documents.runtime.test',
126
77
  },
127
- views: {
128
- editor: {
129
- auth: 'optional',
130
- target: { kind: 'definition' },
78
+ ir: {
79
+ domain: 'documents.runtime.test',
80
+ views: { editor: { auth: 'required', target: { kind: 'definition' } } },
81
+ importsByKey: {
82
+ 'directory.runtime.test:class.Named': {
83
+ ref: { origin: 'directory.runtime.test', kind: 'class', name: 'Named' },
84
+ },
131
85
  },
132
86
  },
133
87
  })
134
- expect(result.importedInterfaces?.Named).toMatchObject({
135
- name: 'Named',
136
- origin: 'directory.runtime.test',
137
- })
88
+ expect(result.revision).toMatch(/^sha256:[0-9a-f]{64}$/)
138
89
  })
139
90
 
140
- test('transports the DSL revision and fingerprints the root only for rendering', async () => {
91
+ test('transports the DSL revision while keeping render identity separate', async () => {
141
92
  const handle = currentFixture()
142
93
  const extracted = await runtimeExtract(handle.schemaIndex, handle.root)
143
- expect(extracted.ok).toBe(true)
144
- if (extracted.revision === null) throw new Error('expected an admitted schema revision')
145
-
146
- const bundle = await buildBundle(handle)
147
- expect(bundle.schemaRoot).toEqual(extracted.root)
148
- expect(bundle.schemaMode).toBe('canonical-admitted')
149
- expect(bundle.schemaRevision).toBe(extracted.revision)
150
- expect(bundle.renderFingerprint).toBe(renderFingerprintOf(extracted.root))
151
- expect(bundle.renderFingerprint).not.toBe(renderFingerprintOf(extracted.ir))
94
+ if (extracted.revision === null) throw new Error('expected an admitted Schema revision')
95
+ const built = await buildBundle(handle)
96
+ expect(built.schemaRoot).toEqual(extracted.root)
97
+ expect(built.schemaRevision).toBe(extracted.revision)
98
+ expect(built.renderFingerprint).toBe(renderFingerprintOf(extracted.root))
99
+ expect(built.renderFingerprint).not.toBe(renderFingerprintOf(extracted.ir))
152
100
  })
153
101
 
154
- test('re-admits installed Bundle JSON before comparing its DSL revision', async () => {
102
+ test('re-admits installed Bundle JSON before comparing its revision', async () => {
155
103
  const handle = currentFixture()
156
104
  const extracted = await runtimeExtract(handle.schemaIndex, handle.root)
157
- if (extracted.revision === null) throw new Error('expected an admitted local schema revision')
105
+ if (extracted.revision === null) throw new Error('expected an admitted Schema revision')
158
106
  const fixture = (await import(handle.schemaIndex)) as { installedBundle: unknown }
159
107
  const installedWire = JSON.parse(JSON.stringify(fixture.installedBundle))
160
-
161
108
  expect(await admittedBundleRevision(handle.root, installedWire)).toBe(extracted.revision)
162
109
  })
163
110
 
164
- test('renders a V1-shaped but unadmitted root only as a structural preview', async () => {
111
+ test('renders a V1-shaped root as preview when SDK admission fails', async () => {
165
112
  const root = fixtureRoot('canonical-preview')
166
- linkCurrentSdk(root)
167
- const schemaIndex = join(root, 'schema', 'index.ts')
113
+ const schemaIndex = join(root, 'schema/index.ts')
168
114
  writeFileSync(
169
115
  schemaIndex,
170
116
  `export const schema = {
171
117
  format: 'astrale.dsl', version: 'v1', origin: 'not an origin',
172
- dependencies: [], types: {}, interfaces: {}, classes: {}, functions: {},
173
- policies: {}, views: {}, core: { nodes: {}, edges: [] },
118
+ dependencies: {}, classes: {}, functions: {}, policies: {}, views: {},
119
+ core: { nodes: {}, edges: [] },
174
120
  }\n`,
175
121
  )
176
-
177
- const result = await runtimeExtract(schemaIndex, root)
178
- expect(result.ok).toBe(true)
179
- expect(result.schemaMode).toBe('canonical-preview')
180
- expect(result.revision).toBeNull()
181
- expect(result.root).toMatchObject({ format: 'astrale.dsl', version: 'v1' })
122
+ expect(await runtimeExtract(schemaIndex, root)).toMatchObject({
123
+ ok: true,
124
+ schemaMode: 'canonical-preview',
125
+ revision: null,
126
+ })
182
127
  })
183
128
 
184
- test('extracts canonical core without importing implementation.ts', async () => {
129
+ test('extracts Core through the pure Schema entry without importing Application', async () => {
185
130
  const handle = currentFixture()
186
- const result = await coreExtract(handle.schemaIndex, handle.domainFile, handle.root)
187
-
188
- expect(result).toEqual({
131
+ expect(await coreExtract(handle.schemaIndex, handle.root)).toEqual({
189
132
  ok: true,
190
133
  core: {
191
134
  domain: 'documents.runtime.test',
@@ -193,83 +136,22 @@ describe('current SDK schema extractor', () => {
193
136
  {
194
137
  path: '/:documents.runtime.test:core.welcome',
195
138
  className: 'Document',
196
- data: { 'documents.runtime.test:class.Document.property.title': 'Welcome' },
197
- },
198
- ],
199
- edges: [
200
- {
201
- from: '/:documents.runtime.test:core.welcome',
202
- to: '/:documents.runtime.test',
203
- edgeName: 'owned_by',
204
139
  data: {},
205
140
  },
206
141
  ],
142
+ edges: [],
207
143
  },
208
144
  })
209
145
  })
210
146
  })
211
147
 
212
- test('runtime extractor preserves the legacy compiled-IR envelope', async () => {
213
- const root = fixtureRoot('legacy-runtime')
214
- const schemaIndex = join(root, 'schema', 'index.ts')
215
- writeFileSync(
216
- schemaIndex,
217
- `export const D = { $: { ir: {
218
- version: 'legacy', domain: 'legacy.runtime.test', types: {}, interfaces: {},
219
- classes: {}, imports: {}, functions: {},
220
- } } }\n`,
221
- )
222
-
223
- const result = await runtimeExtract(schemaIndex, root)
224
- expect(result).toMatchObject({
225
- ok: true,
226
- schemaMode: 'legacy',
148
+ test('rejects the removed compiled-IR compatibility envelope', async () => {
149
+ const root = fixtureRoot('removed-runtime')
150
+ const schemaIndex = join(root, 'schema/index.ts')
151
+ writeFileSync(schemaIndex, `export const D = { $: { ir: { domain: 'legacy' } } }\n`)
152
+ expect(await runtimeExtract(schemaIndex, root)).toMatchObject({
153
+ ok: false,
154
+ schemaMode: 'unavailable',
227
155
  revision: null,
228
- root: null,
229
- ir: { domain: 'legacy.runtime.test', functions: {} },
230
- })
231
- })
232
-
233
- test('core extractor preserves the legacy defineCore fallback', async () => {
234
- const root = fixtureRoot('legacy-core')
235
- const schemaIndex = join(root, 'schema', 'index.ts')
236
- const domainFile = join(root, 'domain.ts')
237
- writeFileSync(schemaIndex, `export const schema = { domain: 'legacy.runtime.test' }\n`)
238
- writeFileSync(
239
- domainFile,
240
- `
241
- const Item = { config: { name: 'Item' } }
242
- const linked = { config: { name: 'linked' } }
243
- const schema = { interfaces: {}, classes: { Item, linked }, imports: [] }
244
- export const core = {
245
- domain: 'legacy.runtime.test', schema,
246
- __nodes: [
247
- { path: '/legacy/left', def: Item, data: { name: 'Left' } },
248
- { path: '/legacy/right', def: Item, data: { name: 'Right' } },
249
- ],
250
- __edges: [
251
- { from: '/legacy/left', to: '/legacy/right', edge: linked, data: { order: 1 } },
252
- ],
253
- }
254
- `,
255
- )
256
-
257
- expect(await coreExtract(schemaIndex, domainFile, root)).toEqual({
258
- ok: true,
259
- core: {
260
- domain: 'legacy.runtime.test',
261
- nodes: [
262
- { path: '/legacy/left', className: 'Item', data: { name: 'Left' } },
263
- { path: '/legacy/right', className: 'Item', data: { name: 'Right' } },
264
- ],
265
- edges: [
266
- {
267
- from: '/legacy/left',
268
- to: '/legacy/right',
269
- edgeName: 'linked',
270
- data: { order: 1 },
271
- },
272
- ],
273
- },
274
156
  })
275
157
  })
@@ -5,13 +5,7 @@
5
5
  * render IR plus the raw canonical root (when present), or an error render-state
6
6
  * — never throws.
7
7
  */
8
- import type {
9
- IrInterface,
10
- SchemaIR,
11
- SchemaRevision,
12
- StudioCore,
13
- StudioSchemaBundle,
14
- } from '../../shared/types'
8
+ import type { SchemaIR, SchemaRevision, StudioCore, StudioSchemaBundle } from '../../shared/types'
15
9
 
16
10
  import { isSchemaRevision } from '../../shared/types'
17
11
 
@@ -21,13 +15,11 @@ const CORE_EXTRACTOR = new URL('./core-extractor.ts', import.meta.url).pathname
21
15
  export interface RuntimeExtractResult {
22
16
  ok: boolean
23
17
  ir: SchemaIR | null
24
- /** Portable canonical V1 document. Null for legacy domains and failures. */
18
+ /** Portable canonical V1 document. Null only for failures. */
25
19
  root: unknown | null
26
20
  schemaMode: StudioSchemaBundle['schemaMode']
27
- /** Present only when the Domain cohort SDK admitted `root`. */
21
+ /** Present only when the Domain's installed SDK admitted `root`. */
28
22
  revision: SchemaRevision | null
29
- /** member bodies of imported (kernel + cross-domain) interfaces, by name */
30
- importedInterfaces?: Record<string, IrInterface>
31
23
  error?: { message: string }
32
24
  }
33
25
 
@@ -70,16 +62,10 @@ export async function runtimeExtract(
70
62
  }
71
63
  const schemaMode = parsed.schemaMode
72
64
  const revision = parsed.revision
73
- const validMode =
74
- schemaMode === 'canonical-admitted' ||
75
- schemaMode === 'canonical-preview' ||
76
- schemaMode === 'legacy'
65
+ const validMode = schemaMode === 'canonical-admitted' || schemaMode === 'canonical-preview'
77
66
  const validRevision =
78
67
  schemaMode === 'canonical-admitted' ? isSchemaRevision(revision) : revision == null
79
- const validRoot =
80
- schemaMode === 'legacy'
81
- ? parsed.root == null
82
- : parsed.root !== null && parsed.root !== undefined
68
+ const validRoot = parsed.root !== null && parsed.root !== undefined
83
69
  if (!validMode || !validRevision || !validRoot) {
84
70
  return {
85
71
  ok: false,
@@ -96,7 +82,6 @@ export async function runtimeExtract(
96
82
  root: parsed.root ?? null,
97
83
  schemaMode,
98
84
  revision: revision ?? null,
99
- importedInterfaces: (parsed.importedInterfaces ?? {}) as Record<string, IrInterface>,
100
85
  }
101
86
  } catch (e: any) {
102
87
  return {
@@ -118,18 +103,16 @@ export interface CoreExtractResult {
118
103
  }
119
104
 
120
105
  /**
121
- * Spawn the core-extractor island over the pure schema entry first, retaining the
122
- * composition entry only as the legacy `defineCore` fallback. Returns the
123
- * resolved core graph or an error — never throws.
106
+ * Spawn the Core extractor over the pure Schema entry. Returns the projected
107
+ * graph or an error and never imports Application or Runtime modules.
124
108
  */
125
109
  export async function coreExtract(
126
110
  schemaIndexPath: string,
127
- domainFile: string,
128
111
  domainDir: string,
129
112
  timeoutMs = 20000,
130
113
  ): Promise<CoreExtractResult> {
131
114
  try {
132
- const proc = Bun.spawn(['bun', 'run', CORE_EXTRACTOR, schemaIndexPath, domainFile, domainDir], {
115
+ const proc = Bun.spawn(['bun', 'run', CORE_EXTRACTOR, schemaIndexPath, domainDir], {
133
116
  cwd: domainDir,
134
117
  stdout: 'pipe',
135
118
  stderr: 'pipe',
@@ -0,0 +1,70 @@
1
+ import { expect, test } from 'bun:test'
2
+
3
+ import { decodeSchemaIR } from './schema-ir-json'
4
+
5
+ function canonicalIr() {
6
+ return {
7
+ format: 'astrale.dsl',
8
+ version: 'v1',
9
+ domain: 'example.test',
10
+ classes: {
11
+ Document: {
12
+ type: 'node',
13
+ name: 'Document',
14
+ origin: 'example.test',
15
+ ref: { origin: 'example.test', kind: 'class', name: 'Document' },
16
+ properties: {},
17
+ methods: {
18
+ rename: {
19
+ name: 'rename',
20
+ input: { type: 'object', properties: {} },
21
+ output: { mode: 'value', schema: { type: 'boolean' } },
22
+ static: false,
23
+ inheritance: 'default',
24
+ auth: 'authorized',
25
+ },
26
+ },
27
+ },
28
+ },
29
+ importsByKey: {},
30
+ importedClassesByKey: {},
31
+ functions: {
32
+ search: {
33
+ name: 'search',
34
+ input: { type: 'object', properties: {} },
35
+ output: { mode: 'stream', item: { type: 'string' } },
36
+ auth: 'authenticated',
37
+ },
38
+ },
39
+ views: {},
40
+ policies: {},
41
+ dependencies: [],
42
+ core: {},
43
+ }
44
+ }
45
+
46
+ test('admits the complete canonical Studio projection', () => {
47
+ expect(decodeSchemaIR(canonicalIr())).toMatchObject({
48
+ format: 'astrale.dsl',
49
+ domain: 'example.test',
50
+ })
51
+ })
52
+
53
+ test('rejects stale projections that lost canonical callable contracts', () => {
54
+ const input = canonicalIr()
55
+ const rename = input.classes.Document.methods.rename as Record<string, unknown>
56
+ delete rename.input
57
+ delete rename.output
58
+ Object.assign(rename, { params: {}, returns: { type: 'boolean' } })
59
+ expect(decodeSchemaIR(input)).toBeUndefined()
60
+ })
61
+
62
+ test('rejects unversioned and incomplete projection roots', () => {
63
+ const withoutFormat = canonicalIr() as Record<string, unknown>
64
+ delete withoutFormat.format
65
+ expect(decodeSchemaIR(withoutFormat)).toBeUndefined()
66
+
67
+ const withoutViews = canonicalIr() as Record<string, unknown>
68
+ delete withoutViews.views
69
+ expect(decodeSchemaIR(withoutViews)).toBeUndefined()
70
+ })
@@ -1,8 +1,9 @@
1
1
  /** Structural admission for persisted Studio render IR. SDK admission is separate. */
2
2
 
3
3
  import type {
4
+ IrClass,
5
+ IrClassRef,
4
6
  IrFunction,
5
- IrInterface,
6
7
  IrMethod,
7
8
  IrSchemaRef,
8
9
  JsonSchema,
@@ -11,10 +12,6 @@ import type {
11
12
 
12
13
  import { asJsonRecord, asStringArray } from '../json'
13
14
 
14
- function optional<T>(value: unknown, guard: (candidate: unknown) => candidate is T): boolean {
15
- return value === undefined || guard(value)
16
- }
17
-
18
15
  function recordOf(value: unknown, guard: (candidate: unknown) => boolean): boolean {
19
16
  const record = asJsonRecord(value)
20
17
  return !!record && Object.values(record).every(guard)
@@ -29,14 +26,12 @@ function isSchemaRef(value: unknown): value is IrSchemaRef {
29
26
  return (
30
27
  typeof record?.origin === 'string' &&
31
28
  typeof record.name === 'string' &&
32
- ['type', 'interface', 'class', 'function', 'policy', 'view', 'core'].includes(
33
- String(record.kind),
34
- )
29
+ ['class', 'function', 'policy', 'view', 'core'].includes(String(record.kind))
35
30
  )
36
31
  }
37
32
 
38
- function isSchemaRefArray(value: unknown): value is IrSchemaRef[] {
39
- return Array.isArray(value) && value.every(isSchemaRef)
33
+ function isClassRef(value: unknown): value is IrClassRef {
34
+ return isSchemaRef(value) && value.kind === 'class'
40
35
  }
41
36
 
42
37
  function isOutput(value: unknown): boolean {
@@ -52,20 +47,22 @@ function isMethod(value: unknown): value is IrMethod {
52
47
  const record = asJsonRecord(value)
53
48
  return (
54
49
  typeof record?.name === 'string' &&
55
- recordOf(record.params, isJsonSchema) &&
56
- isJsonSchema(record.returns) &&
50
+ isJsonSchema(record.input) &&
51
+ isOutput(record.output) &&
57
52
  typeof record.static === 'boolean' &&
58
- ['default', 'abstract', 'sealed'].includes(String(record.inheritance)) &&
59
- optional(record.input, isJsonSchema) &&
60
- (record.requiredParams === undefined || asStringArray(record.requiredParams) !== undefined) &&
61
- (record.output === undefined || isOutput(record.output))
53
+ ['default', 'abstract', 'sealed'].includes(String(record.inheritance))
62
54
  )
63
55
  }
64
56
 
65
57
  function isEndpoint(value: unknown): boolean {
66
58
  const record = asJsonRecord(value)
67
59
  if (!record || typeof record.name !== 'string' || !asStringArray(record.types)) return false
68
- if (record.refs !== undefined && !isSchemaRefArray(record.refs)) return false
60
+ if (
61
+ record.refs !== undefined &&
62
+ (!Array.isArray(record.refs) || !record.refs.every(isSchemaRef))
63
+ ) {
64
+ return false
65
+ }
69
66
  if (record.cardinality !== undefined) {
70
67
  const cardinality = asJsonRecord(record.cardinality)
71
68
  if (
@@ -81,31 +78,19 @@ function isEndpoint(value: unknown): boolean {
81
78
  return true
82
79
  }
83
80
 
84
- function isInterface(value: unknown): value is IrInterface {
85
- const record = asJsonRecord(value)
86
- return (
87
- record?.type === 'interface' &&
88
- typeof record.name === 'string' &&
89
- recordOf(record.properties, isJsonSchema) &&
90
- recordOf(record.methods, isMethod) &&
91
- (record.required === undefined || asStringArray(record.required) !== undefined) &&
92
- (record.extends === undefined || asStringArray(record.extends) !== undefined) &&
93
- (record.extendsRefs === undefined || isSchemaRefArray(record.extendsRefs)) &&
94
- (record.endpoints === undefined ||
95
- (Array.isArray(record.endpoints) && record.endpoints.every(isEndpoint)))
96
- )
97
- }
98
-
99
- function isClass(value: unknown): boolean {
81
+ function isClass(value: unknown): value is IrClass {
100
82
  const record = asJsonRecord(value)
101
83
  return (
102
84
  (record?.type === 'node' || record?.type === 'edge') &&
103
85
  typeof record.name === 'string' &&
86
+ typeof record.origin === 'string' &&
87
+ isClassRef(record.ref) &&
104
88
  recordOf(record.properties, isJsonSchema) &&
105
89
  recordOf(record.methods, isMethod) &&
106
90
  (record.required === undefined || asStringArray(record.required) !== undefined) &&
107
- (record.implements === undefined || asStringArray(record.implements) !== undefined) &&
108
- (record.implementsRefs === undefined || isSchemaRefArray(record.implementsRefs)) &&
91
+ (record.extends === undefined || asStringArray(record.extends) !== undefined) &&
92
+ (record.extendsRefs === undefined ||
93
+ (Array.isArray(record.extendsRefs) && record.extendsRefs.every(isClassRef))) &&
109
94
  (record.endpoints === undefined ||
110
95
  (Array.isArray(record.endpoints) && record.endpoints.every(isEndpoint)))
111
96
  )
@@ -113,24 +98,16 @@ function isClass(value: unknown): boolean {
113
98
 
114
99
  function isFunction(value: unknown): value is IrFunction {
115
100
  const record = asJsonRecord(value)
116
- return (
117
- typeof record?.name === 'string' &&
118
- isJsonSchema(record.input) &&
119
- recordOf(record.params, isJsonSchema) &&
120
- (record.requiredParams === undefined || asStringArray(record.requiredParams) !== undefined) &&
121
- isOutput(record.output) &&
122
- isJsonSchema(record.returns) &&
123
- record.static === true &&
124
- record.inheritance === 'default'
125
- )
101
+ return typeof record?.name === 'string' && isJsonSchema(record.input) && isOutput(record.output)
126
102
  }
127
103
 
128
104
  function isImport(value: unknown): boolean {
129
105
  const record = asJsonRecord(value)
130
106
  return (
131
107
  typeof record?.origin === 'string' &&
132
- (record.definition === 'class' || record.definition === 'interface') &&
133
- optional(record.ref, isSchemaRef)
108
+ isClassRef(record.ref) &&
109
+ typeof record.key === 'string' &&
110
+ record.key === `${record.ref.origin}:class.${record.ref.name}`
134
111
  )
135
112
  }
136
113
 
@@ -142,7 +119,9 @@ function isView(value: unknown): boolean {
142
119
  ['required', 'optional', 'public'].includes(String(record.auth)) &&
143
120
  !!target &&
144
121
  (target.kind === 'domain' ||
145
- (target.kind === 'definition' && isSchemaRefArray(target.definitions)))
122
+ (target.kind === 'definition' &&
123
+ Array.isArray(target.definitions) &&
124
+ target.definitions.every(isSchemaRef)))
146
125
  )
147
126
  }
148
127
 
@@ -150,32 +129,23 @@ export function decodeSchemaIR(value: unknown): SchemaIR | undefined {
150
129
  const record = asJsonRecord(value)
151
130
  if (
152
131
  !record ||
132
+ record.format !== 'astrale.dsl' ||
153
133
  typeof record.version !== 'string' ||
154
134
  typeof record.domain !== 'string' ||
155
- !recordOf(record.types, isJsonSchema) ||
156
- !recordOf(record.interfaces, isInterface) ||
157
135
  !recordOf(record.classes, isClass) ||
158
- !recordOf(record.imports, isImport) ||
159
136
  !recordOf(record.functions, isFunction) ||
160
- (record.importsByKey !== undefined && !recordOf(record.importsByKey, isImport)) ||
161
- (record.importedInterfacesByKey !== undefined &&
162
- !recordOf(record.importedInterfacesByKey, isInterface)) ||
163
- (record.views !== undefined && !recordOf(record.views, isView)) ||
164
- (record.policies !== undefined && !asJsonRecord(record.policies)) ||
165
- (record.dependencies !== undefined &&
166
- (!Array.isArray(record.dependencies) ||
167
- !record.dependencies.every((dependency) => {
168
- const item = asJsonRecord(dependency)
169
- return typeof item?.origin === 'string' && typeof item.revision === 'string'
170
- })))
137
+ !recordOf(record.importsByKey, isImport) ||
138
+ !recordOf(record.importedClassesByKey, isClass) ||
139
+ !recordOf(record.views, isView) ||
140
+ !asJsonRecord(record.policies) ||
141
+ !Array.isArray(record.dependencies) ||
142
+ !record.dependencies.every((dependency) => {
143
+ const item = asJsonRecord(dependency)
144
+ return typeof item?.origin === 'string' && typeof item.revision === 'string'
145
+ }) ||
146
+ !('core' in record)
171
147
  ) {
172
148
  return undefined
173
149
  }
174
150
  return record as unknown as SchemaIR
175
151
  }
176
-
177
- export function decodeIrInterfaceRecord(value: unknown): Record<string, IrInterface> | undefined {
178
- const record = asJsonRecord(value)
179
- if (!record || !Object.values(record).every(isInterface)) return undefined
180
- return record as unknown as Record<string, IrInterface>
181
- }