@astrale-os/cli 1.0.0-beta.0 → 1.0.0-beta.10
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.
- package/README.md +23 -12
- package/dist/astrale.js +51494 -56307
- package/dist/public/connect-core.js +2028 -26846
- package/dist/public/keys/index.js +1755 -40694
- package/dist/public/paths/index.js +859 -39518
- package/dist/types/admin/binding.d.ts +19 -0
- package/dist/types/admin/instance/client.d.ts +22 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +5 -12
- package/dist/types/connection/auth.d.ts +3 -0
- package/dist/types/connection/call.d.ts +3 -0
- package/dist/types/connection/command.d.ts +27 -0
- package/dist/types/connection/credential.d.ts +14 -0
- package/dist/types/connection/errors.d.ts +1 -0
- package/dist/types/connection/exchange.d.ts +9 -0
- package/dist/types/connection/failure/classify.d.ts +2 -0
- package/dist/types/connection/failure/index.d.ts +5 -0
- package/dist/types/connection/failure/model.d.ts +25 -0
- package/dist/types/connection/failure/render.d.ts +3 -0
- package/dist/types/connection/index.d.ts +8 -0
- package/dist/types/connection/reasons.d.ts +37 -0
- package/dist/types/connection/self.d.ts +37 -0
- package/dist/types/connection/session.d.ts +31 -0
- package/dist/types/connection/target.d.ts +38 -0
- package/dist/types/errors.d.ts +1 -1
- package/dist/types/lib/admin-instance.d.ts +17 -0
- package/dist/types/lib/admin-target.d.ts +6 -2
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +7 -0
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +10 -2
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/meta.d.ts +2 -2
- package/dist/types/lib/proc.d.ts +43 -0
- package/dist/types/lib/self.d.ts +9 -0
- package/dist/types/lib/update.d.ts +131 -0
- package/package.json +8 -10
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/__tests__/binding.test.ts +31 -0
- package/src/admin/__tests__/fixture.ts +101 -0
- package/src/admin/binding.ts +98 -0
- package/src/admin/catalog/.spec/api.d.ts +2 -2
- package/src/admin/catalog/__tests__/client.test.ts +20 -49
- package/src/admin/catalog/client.ts +36 -32
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/graph/.spec/api.d.ts +4 -10
- package/src/admin/graph/nodes.ts +1 -1
- package/src/admin/instance/.spec/api.d.ts +2 -2
- package/src/admin/instance/__tests__/client.test.ts +40 -163
- package/src/admin/instance/client.ts +35 -137
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +11 -20
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/auth-login.test.ts +54 -1
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +5 -1
- package/src/commands/__tests__/domain-install-operation.test.ts +12 -7
- package/src/commands/__tests__/domain-install-owned.test.ts +6 -2
- package/src/commands/__tests__/domain-list.test.ts +63 -4
- package/src/commands/__tests__/install-identity-override.test.ts +19 -19
- package/src/commands/__tests__/instance-list-rows.test.ts +1 -1
- package/src/commands/__tests__/instance-status.test.ts +139 -0
- package/src/commands/__tests__/read-commands.test.ts +35 -10
- package/src/commands/__tests__/studio-descriptor.test.ts +22 -0
- package/src/commands/__tests__/update.test.ts +95 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +43 -56
- package/src/commands/domain/install.ts +13 -17
- package/src/commands/domain/list.ts +18 -13
- package/src/commands/domain/publish.ts +14 -11
- package/src/commands/domain/uninstall.ts +2 -2
- package/src/commands/get.ts +14 -15
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +5 -4
- package/src/commands/instance/create.ts +8 -16
- package/src/commands/instance/delete.ts +12 -9
- package/src/commands/instance/list.ts +2 -2
- package/src/commands/instance/status.ts +66 -20
- package/src/commands/instance/use.ts +16 -12
- package/src/commands/introspect.ts +9 -16
- package/src/commands/logs.ts +24 -25
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +27 -30
- package/src/commands/studio.ts +20 -0
- package/src/commands/token.ts +8 -7
- package/src/commands/update.ts +68 -22
- package/src/commands/view.ts +7 -6
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +5 -4
- package/src/connection/.spec/flows/session.ts +2 -2
- package/src/connection/.spec/laws/connection.ts +6 -2
- package/src/connection/.spec/layout.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +20 -1
- package/src/connection/__tests__/errors.test.ts +43 -32
- package/src/connection/__tests__/exchange.test.ts +36 -6
- package/src/connection/__tests__/failure-fixtures.ts +39 -0
- package/src/connection/__tests__/failure-safety.test.ts +75 -0
- package/src/connection/__tests__/self.test.ts +31 -11
- package/src/connection/__tests__/session.test.ts +3 -3
- package/src/connection/__tests__/target.test.ts +1 -0
- package/src/connection/auth.ts +13 -0
- package/src/connection/call.ts +1 -1
- package/src/connection/credential.ts +2 -2
- package/src/connection/errors.ts +1 -355
- package/src/connection/exchange.ts +52 -27
- package/src/connection/failure/classify.ts +110 -0
- package/src/connection/failure/index.ts +18 -0
- package/src/connection/failure/model.ts +28 -0
- package/src/connection/failure/render.ts +116 -0
- package/src/connection/reasons.ts +16 -0
- package/src/connection/self.ts +39 -21
- package/src/connection/session.ts +19 -8
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +12 -5
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +25 -6
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +21 -32
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/registration.ts +3 -3
- package/src/keys/__tests__/keys.test.ts +4 -0
- package/src/keys/credential.ts +1 -0
- package/src/lib/__tests__/admin-target.test.ts +12 -0
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +138 -0
- package/src/lib/__tests__/idp-session.test.ts +22 -0
- package/src/lib/__tests__/idp.test.ts +36 -4
- package/src/lib/__tests__/instance-candidates.test.ts +1 -3
- package/src/lib/__tests__/instance-target.test.ts +7 -39
- package/src/lib/__tests__/instance.test.ts +6 -0
- package/src/lib/__tests__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +53 -16
- package/src/lib/__tests__/update.test.ts +113 -8
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +1 -1
- package/src/lib/__tests__/view-session.test.ts +1 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +104 -0
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +39 -4
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +22 -12
- package/src/lib/log.ts +16 -9
- package/src/lib/login-flow.ts +13 -3
- package/src/lib/meta.ts +2 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +124 -30
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/resolve.ts +1 -1
- package/src/program/__tests__/program.test.ts +20 -4
- package/src/program/build.ts +2 -2
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/studio/client/dist/assets/{elk-api-D2xgMJvi.js → elk-api-ByzoDZWH.js} +1 -1
- package/studio/client/dist/assets/index-B0RCgUOT.js +8 -0
- package/studio/client/dist/assets/index-BiGR8Oo9.css +1 -0
- package/studio/client/dist/assets/index-N8J8EKlB.js +81 -0
- package/studio/client/dist/index.html +2 -2
- package/studio/package.json +5 -10
- package/studio/server/agent/ask.test.ts +1 -1
- package/studio/server/agent/bridge/grant.test.ts +1 -1
- package/studio/server/agent/bridge/routes.test.ts +1 -1
- package/studio/server/agent/bridge/stdio.ts +8 -1
- package/studio/server/agent/conversation.test.ts +10 -5
- package/studio/server/agent/conversation.ts +56 -8
- package/studio/server/agent/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/codex/loadout.ts +18 -21
- package/studio/server/agent/harness/gateway/config.ts +20 -18
- package/studio/server/agent/harness/selection.ts +9 -1
- package/studio/server/agent/prompts/anchors.test.ts +32 -36
- package/studio/server/agent/prompts/anchors.ts +20 -49
- package/studio/server/agent/prompts/system.test.ts +3 -2
- package/studio/server/agent/prompts/system.ts +6 -5
- package/studio/server/agent/prompts/turn.ts +13 -4
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/completion.ts +2 -2
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/agent/run/preparation.ts +6 -5
- package/studio/server/agent/run/transcript.test.ts +35 -1
- package/studio/server/agent/run/transcript.ts +169 -3
- package/studio/server/agent/run/usage.test.ts +24 -0
- package/studio/server/agent/run/usage.ts +26 -1
- package/studio/server/api/canvas.ts +43 -0
- package/studio/server/api/comments.ts +85 -0
- package/studio/server/api/context.ts +61 -0
- package/studio/server/api/deployment.ts +39 -0
- package/studio/server/api/documents.ts +55 -0
- package/studio/server/api/domain.ts +59 -0
- package/studio/server/api/http.ts +45 -0
- package/studio/server/api/project.ts +57 -0
- package/studio/server/api/schema.ts +12 -0
- package/studio/server/api/views.ts +57 -0
- package/studio/server/api/workspace.ts +60 -0
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +147 -0
- package/studio/server/api.ts +20 -422
- package/studio/server/cache.test.ts +122 -0
- package/studio/server/cache.ts +162 -11
- package/studio/server/cli-consumers.test.ts +494 -0
- package/studio/server/cli.test.ts +88 -0
- package/studio/server/cli.ts +240 -0
- package/studio/server/client-package.ts +25 -1
- package/studio/server/domain.test.ts +50 -40
- package/studio/server/domain.ts +83 -52
- package/studio/server/environment/dotenv-preview.test.ts +39 -0
- package/studio/server/environment/dotenv-preview.ts +27 -0
- package/studio/server/environment/files.test.ts +53 -0
- package/studio/server/{state/env.ts → environment/files.ts} +7 -48
- package/studio/server/{state → handoff}/copy.ts +21 -6
- package/studio/server/handoff/service.test.ts +22 -0
- package/studio/server/{state/handoff.ts → handoff/service.ts} +21 -14
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/active.ts +72 -0
- package/studio/server/instances/deploy-record.test.ts +31 -0
- package/studio/server/instances/deploy-record.ts +37 -0
- package/studio/server/instances/deploy.ts +56 -0
- package/studio/server/{state/instance.test.ts → instances/probe.test.ts} +25 -21
- package/studio/server/instances/probe.ts +67 -0
- package/studio/server/instances/status.test.ts +12 -0
- package/studio/server/instances/status.ts +49 -0
- package/studio/server/introspect/anatomy/client-tree.ts +57 -0
- package/studio/server/introspect/anatomy/env-fields.ts +66 -0
- package/studio/server/introspect/anatomy/schema-definition.ts +53 -0
- package/studio/server/introspect/anatomy/source.ts +181 -0
- package/studio/server/introspect/anatomy/views/routes.ts +114 -0
- package/studio/server/introspect/anatomy/views.ts +53 -0
- package/studio/server/introspect/anatomy-extras.test.ts +106 -35
- package/studio/server/introspect/anatomy-extras.ts +7 -726
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +52 -28
- package/studio/server/introspect/bundle.ts +15 -9
- package/studio/server/introspect/canonical-schema.test.ts +93 -309
- package/studio/server/introspect/canonical-schema.ts +257 -425
- package/studio/server/introspect/config-preview.test.ts +32 -0
- package/studio/server/introspect/config-preview.ts +119 -0
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +18 -18
- package/studio/server/introspect/diff.ts +44 -90
- package/studio/server/introspect/extractor.ts +23 -116
- package/studio/server/introspect/hash.ts +5 -2
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay-tsmorph.ts +7 -1305
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +54 -0
- package/studio/server/introspect/revision.ts +49 -0
- package/studio/server/introspect/runtime.test.ts +83 -143
- package/studio/server/introspect/runtime.ts +40 -11
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +151 -0
- package/studio/server/introspect/schema-refs.test.ts +43 -88
- package/studio/server/introspect/schema-refs.ts +14 -49
- package/studio/server/introspect/source-overlay/annotations.ts +14 -0
- package/studio/server/introspect/source-overlay/handlers.ts +161 -0
- package/studio/server/introspect/source-overlay/kernel-calls.ts +46 -0
- package/studio/server/introspect/source-overlay/project.ts +248 -0
- package/studio/server/introspect/source-overlay/spans.ts +330 -0
- package/studio/server/json.ts +46 -0
- package/studio/server/lifecycle.ts +5 -1
- package/studio/server/sse.test.ts +26 -0
- package/studio/server/sse.ts +4 -1
- package/studio/server/state/baseline.test.ts +223 -7
- package/studio/server/state/baseline.ts +148 -34
- package/studio/server/state/comments.test.ts +66 -1
- package/studio/server/state/comments.ts +173 -10
- package/studio/server/state/context.ts +34 -2
- package/studio/server/state/documents.ts +42 -1
- package/studio/server/state/integrations.ts +27 -1
- package/studio/server/state/layout.test.ts +34 -0
- package/studio/server/state/layout.ts +44 -17
- package/studio/server/state/settings.ts +29 -22
- package/studio/server/state/store.test.ts +122 -0
- package/studio/server/state/store.ts +66 -17
- package/studio/server/state/visibility.ts +22 -11
- package/studio/server/views/model.test.ts +57 -0
- package/studio/server/views/model.ts +66 -0
- package/studio/server/views/runtime.ts +40 -0
- package/studio/server/views/selection-repository.test.ts +31 -0
- package/studio/server/views/selection-repository.ts +60 -0
- package/studio/server/views/session.test.ts +73 -0
- package/studio/server/views/session.ts +166 -0
- package/studio/server/views/target.test.ts +78 -0
- package/studio/server/views/target.ts +222 -0
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +9 -5
- package/studio/server/workspace/catalog.test.ts +17 -0
- package/studio/server/workspace/catalog.ts +43 -0
- package/studio/server/{state → workspace}/create.test.ts +1 -1
- package/studio/server/{state → workspace}/create.ts +1 -1
- package/studio/server/{state → workspace}/git.ts +1 -1
- package/studio/server/workspace/updates.ts +83 -0
- package/studio/server/workspace-state.ts +1 -1
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/README.md +16 -0
- package/studio/shared/contracts/agent.ts +270 -0
- package/studio/shared/contracts/runtime.ts +50 -0
- package/studio/shared/contracts/schema.ts +393 -0
- package/studio/shared/contracts/surface.test.ts +317 -0
- package/studio/shared/contracts/workspace.ts +265 -0
- package/studio/shared/layout.test.ts +116 -0
- package/studio/shared/schema/identity.test.ts +34 -0
- package/studio/shared/schema/identity.ts +68 -0
- package/studio/shared/types.ts +11 -1099
- package/studio/tsconfig.json +1 -1
- package/viewer/dist/main.js +40 -61
- package/.check-workspace.cjs +0 -40
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/index-BMdnsIJA.css +0 -1
- package/studio/client/dist/assets/index-D-vRV8w7.js +0 -8
- package/studio/client/dist/assets/index-LGSWRrk8.js +0 -81
- package/studio/server/state/catalog.ts +0 -117
- package/studio/server/state/instance.ts +0 -280
- package/studio/server/state/updates.ts +0 -63
- package/studio/server/state/views.test.ts +0 -286
- package/studio/server/state/views.ts +0 -535
|
@@ -1,72 +1,44 @@
|
|
|
1
1
|
import { expect, test } from 'bun:test'
|
|
2
2
|
|
|
3
|
-
import type { SchemaIR } from '../../shared/types'
|
|
3
|
+
import type { IrClassKey, SchemaIR } from '../../shared/types'
|
|
4
4
|
|
|
5
5
|
import { buildOverlay } from './overlay'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
function descriptor(origin: string, name: string) {
|
|
8
|
+
const key = `${origin}:class.${name}` as IrClassKey
|
|
9
|
+
return { origin, ref: { origin, kind: 'class' as const, name }, key }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
test('preserves homonymous Classes through exact import identity', () => {
|
|
13
|
+
const first = descriptor('a.example', 'Shared')
|
|
14
|
+
const second = descriptor('b.example', 'Shared')
|
|
15
|
+
const kernel = descriptor('kernel.astrale.ai', 'Identity')
|
|
8
16
|
const ir = {
|
|
9
17
|
version: 'v1',
|
|
18
|
+
format: 'astrale.dsl',
|
|
10
19
|
domain: 'app.example',
|
|
11
|
-
types: {},
|
|
12
|
-
interfaces: {},
|
|
13
20
|
classes: {},
|
|
14
21
|
functions: {},
|
|
15
|
-
imports: {},
|
|
16
22
|
importsByKey: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
ref: { origin: 'a.example', kind: 'interface', name: 'Shared' },
|
|
21
|
-
key: 'a.example:interface.Shared',
|
|
22
|
-
},
|
|
23
|
-
'b.example:interface.Shared': {
|
|
24
|
-
origin: 'b.example',
|
|
25
|
-
definition: 'interface',
|
|
26
|
-
ref: { origin: 'b.example', kind: 'interface', name: 'Shared' },
|
|
27
|
-
key: 'b.example:interface.Shared',
|
|
28
|
-
},
|
|
29
|
-
'a.example:class.Shared': {
|
|
30
|
-
origin: 'a.example',
|
|
31
|
-
definition: 'class',
|
|
32
|
-
ref: { origin: 'a.example', kind: 'class', name: 'Shared' },
|
|
33
|
-
key: 'a.example:class.Shared',
|
|
34
|
-
},
|
|
35
|
-
'kernel.astrale.ai:interface.Identity': {
|
|
36
|
-
origin: 'kernel.astrale.ai',
|
|
37
|
-
definition: 'interface',
|
|
38
|
-
ref: { origin: 'kernel.astrale.ai', kind: 'interface', name: 'Identity' },
|
|
39
|
-
key: 'kernel.astrale.ai:interface.Identity',
|
|
40
|
-
},
|
|
23
|
+
[first.key]: first,
|
|
24
|
+
[second.key]: second,
|
|
25
|
+
[kernel.key]: kernel,
|
|
41
26
|
},
|
|
27
|
+
importedClassesByKey: {},
|
|
28
|
+
views: {},
|
|
29
|
+
policies: {},
|
|
30
|
+
dependencies: [
|
|
31
|
+
{ origin: first.origin, revision: `sha256:${'a'.repeat(64)}` },
|
|
32
|
+
{ origin: second.origin, revision: `sha256:${'b'.repeat(64)}` },
|
|
33
|
+
],
|
|
34
|
+
core: {},
|
|
42
35
|
} satisfies SchemaIR
|
|
43
36
|
|
|
44
37
|
const overlay = buildOverlay({ ir, domainRoot: '', schemaDir: '' })
|
|
45
|
-
|
|
46
38
|
expect(overlay.crossDomainImports).toEqual([
|
|
47
|
-
{ name: 'Shared', origin:
|
|
48
|
-
{ name: 'Shared', origin:
|
|
49
|
-
{ name: 'Shared', origin: 'a.example', definition: 'class' },
|
|
50
|
-
])
|
|
51
|
-
expect(overlay.mixins).toEqual([
|
|
52
|
-
{ name: 'Identity', origin: 'kernel.astrale.ai', definition: 'interface' },
|
|
53
|
-
])
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test('retains the legacy short-name import fallback', () => {
|
|
57
|
-
const ir = {
|
|
58
|
-
version: 'legacy',
|
|
59
|
-
domain: 'app.example',
|
|
60
|
-
types: {},
|
|
61
|
-
interfaces: {},
|
|
62
|
-
classes: {},
|
|
63
|
-
functions: {},
|
|
64
|
-
imports: {
|
|
65
|
-
Identity: { origin: 'kernel.astrale.ai', definition: 'interface' },
|
|
66
|
-
},
|
|
67
|
-
} satisfies SchemaIR
|
|
68
|
-
|
|
69
|
-
expect(buildOverlay({ ir, domainRoot: '', schemaDir: '' }).mixins).toEqual([
|
|
70
|
-
{ name: 'Identity', origin: 'kernel.astrale.ai', definition: 'interface' },
|
|
39
|
+
{ name: 'Shared', origin: first.origin, ref: first.ref },
|
|
40
|
+
{ name: 'Shared', origin: second.origin, ref: second.ref },
|
|
71
41
|
])
|
|
42
|
+
expect(overlay.mixins).toEqual([{ name: 'Identity', origin: kernel.origin, ref: kernel.ref }])
|
|
43
|
+
expect(overlay.requires).toEqual(['a.example', 'b.example'])
|
|
72
44
|
})
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* overlay.ts — the Studio overlay over the DSL IR. Computes what the IR cannot
|
|
3
|
-
* carry. The
|
|
3
|
+
* carry. The import split and dependency list are computed here; handlerLinks,
|
|
4
4
|
* static parse). handlerLinks, sourceSpans (+JSDoc) and annotations are filled
|
|
5
|
-
* by a ts-morph pass over
|
|
6
|
-
* domain.ts and runtime/index.ts support retained.
|
|
5
|
+
* by a ts-morph pass over current Schema, Runtime, Action, and Workflow files.
|
|
7
6
|
*/
|
|
8
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
9
|
-
import { join } from 'node:path'
|
|
10
|
-
|
|
11
7
|
import type { CrossDomainImport, SchemaIR, SchemaOverlay } from '../../shared/types'
|
|
12
8
|
|
|
13
9
|
import { buildHandlerLinks, buildSchemaAnnotations, buildSourceSpans } from './overlay-tsmorph'
|
|
@@ -22,46 +18,23 @@ export function buildOverlay({ ir, domainRoot, schemaDir }: OverlayArgs): Schema
|
|
|
22
18
|
const origin = ir?.domain ?? ''
|
|
23
19
|
const mixins: CrossDomainImport[] = []
|
|
24
20
|
const crossDomainImports: CrossDomainImport[] = []
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const entry: CrossDomainImport = { name, origin: d.origin, definition: d.definition }
|
|
33
|
-
if (d.origin === 'kernel.astrale.ai') mixins.push(entry)
|
|
21
|
+
for (const descriptor of Object.values(ir?.importsByKey ?? {})) {
|
|
22
|
+
const entry: CrossDomainImport = {
|
|
23
|
+
name: descriptor.ref.name,
|
|
24
|
+
origin: descriptor.origin,
|
|
25
|
+
ref: descriptor.ref,
|
|
26
|
+
}
|
|
27
|
+
if (descriptor.origin === 'kernel.astrale.ai') mixins.push(entry)
|
|
34
28
|
else crossDomainImports.push(entry)
|
|
35
29
|
}
|
|
36
30
|
|
|
37
|
-
const { requires, postInstall } = parseCompositionEntry(domainRoot, origin)
|
|
38
|
-
|
|
39
31
|
return {
|
|
40
32
|
origin,
|
|
41
|
-
requires,
|
|
33
|
+
requires: (ir?.dependencies ?? []).map(({ origin }) => origin),
|
|
42
34
|
crossDomainImports,
|
|
43
35
|
mixins,
|
|
44
|
-
postInstall,
|
|
45
36
|
handlerLinks: buildHandlerLinks({ ir, domainRoot }),
|
|
46
37
|
sourceSpans: buildSourceSpans({ ir, schemaDir }),
|
|
47
38
|
annotations: buildSchemaAnnotations({ ir }),
|
|
48
39
|
}
|
|
49
40
|
}
|
|
50
|
-
|
|
51
|
-
function parseCompositionEntry(
|
|
52
|
-
root: string,
|
|
53
|
-
origin: string,
|
|
54
|
-
): { requires: string[]; postInstall?: string } {
|
|
55
|
-
const f = ['implementation.ts', 'domain.ts'].map((file) => join(root, file)).find(existsSync)
|
|
56
|
-
let requires: string[] = []
|
|
57
|
-
let postInstall: string | undefined
|
|
58
|
-
if (f) {
|
|
59
|
-
const src = readFileSync(f, 'utf8')
|
|
60
|
-
const rm = src.match(/requires\s*:\s*\[([^\]]*)\]/)
|
|
61
|
-
if (rm) requires = [...rm[1].matchAll(/['"`]([^'"`]+)['"`]/g)].map((x) => x[1])
|
|
62
|
-
const pm = src.match(/postInstall\s*:\s*[`'"]([^`'"]+)[`'"]/)
|
|
63
|
-
if (pm)
|
|
64
|
-
postInstall = pm[1].replace(/\$\{schema\.domain\}/g, origin).replace(/\$\{[^}]+\}/g, origin)
|
|
65
|
-
}
|
|
66
|
-
return { requires, postInstall }
|
|
67
|
-
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { admittedBundleRevisionFromSdk } from './revision'
|
|
4
|
+
|
|
5
|
+
const REVISION = `sha256:${'a'.repeat(64)}` as const
|
|
6
|
+
|
|
7
|
+
describe('installed Bundle revision', () => {
|
|
8
|
+
test('uses bundle admission before asking the installed schema owner for its revision', () => {
|
|
9
|
+
const calls: string[] = []
|
|
10
|
+
const root = { format: 'astrale.dsl', version: 'v1', origin: 'notes.example.dev' }
|
|
11
|
+
const input = { wire: true }
|
|
12
|
+
const sdk = {
|
|
13
|
+
bundle: {
|
|
14
|
+
accept(value: unknown) {
|
|
15
|
+
calls.push('bundle.accept')
|
|
16
|
+
expect(value).toBe(input)
|
|
17
|
+
return { root, closure: [] }
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
schema: {
|
|
21
|
+
revision(value: unknown) {
|
|
22
|
+
calls.push('schema.revision')
|
|
23
|
+
expect(value).toBe(root)
|
|
24
|
+
return REVISION
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
expect(admittedBundleRevisionFromSdk(sdk, input)).toBe(REVISION)
|
|
30
|
+
expect(calls).toEqual(['bundle.accept', 'schema.revision'])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('fails closed when bundle admission or the returned revision is invalid', () => {
|
|
34
|
+
expect(
|
|
35
|
+
admittedBundleRevisionFromSdk(
|
|
36
|
+
{ bundle: { accept: () => ({ root: {} }) }, schema: { revision: () => 'sha-short' } },
|
|
37
|
+
{},
|
|
38
|
+
),
|
|
39
|
+
).toBeNull()
|
|
40
|
+
expect(
|
|
41
|
+
admittedBundleRevisionFromSdk(
|
|
42
|
+
{
|
|
43
|
+
bundle: {
|
|
44
|
+
accept: () => {
|
|
45
|
+
throw new Error('invalid bundle')
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
schema: { revision: () => REVISION },
|
|
49
|
+
},
|
|
50
|
+
{},
|
|
51
|
+
),
|
|
52
|
+
).toBeNull()
|
|
53
|
+
})
|
|
54
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { SchemaRevision } from '../../shared/types'
|
|
2
|
+
|
|
3
|
+
import { isSchemaRevision } from '../../shared/types'
|
|
4
|
+
|
|
5
|
+
type AnyRecord = Record<string, unknown>
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Cross the installed-bundle JSON boundary through the Domain's installed SDK, then
|
|
9
|
+
* ask the DSL owner for the root revision. Invalid/unavailable bundles return
|
|
10
|
+
* null so Studio reports unknown drift instead of inventing an identity.
|
|
11
|
+
*/
|
|
12
|
+
export function admittedBundleRevisionFromSdk(
|
|
13
|
+
sdkModule: Record<string, unknown>,
|
|
14
|
+
input: unknown,
|
|
15
|
+
): SchemaRevision | null {
|
|
16
|
+
const bundleApi = asRecord(sdkModule.bundle)
|
|
17
|
+
const schemaApi = asRecord(sdkModule.schema)
|
|
18
|
+
if (typeof bundleApi?.accept !== 'function' || typeof schemaApi?.revision !== 'function') {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const accepted = asRecord(Reflect.apply(bundleApi.accept, bundleApi, [input]))
|
|
23
|
+
if (!accepted || !Object.prototype.hasOwnProperty.call(accepted, 'root')) return null
|
|
24
|
+
const revision = Reflect.apply(schemaApi.revision, schemaApi, [accepted.root])
|
|
25
|
+
return isSchemaRevision(revision) ? revision : null
|
|
26
|
+
} catch {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function admittedBundleRevision(
|
|
32
|
+
domainRoot: string,
|
|
33
|
+
input: unknown,
|
|
34
|
+
): Promise<SchemaRevision | null> {
|
|
35
|
+
try {
|
|
36
|
+
const sdkModule: Record<string, unknown> = await import(
|
|
37
|
+
Bun.resolveSync('@astrale-os/sdk/schema', domainRoot)
|
|
38
|
+
)
|
|
39
|
+
return admittedBundleRevisionFromSdk(sdkModule, input)
|
|
40
|
+
} catch {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function asRecord(value: unknown): AnyRecord | null {
|
|
46
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
47
|
+
? (value as AnyRecord)
|
|
48
|
+
: null
|
|
49
|
+
}
|
|
@@ -6,11 +6,11 @@ import { join } from 'node:path'
|
|
|
6
6
|
import type { DomainHandle } from '../domain'
|
|
7
7
|
|
|
8
8
|
import { buildBundle } from './bundle'
|
|
9
|
-
import {
|
|
9
|
+
import { renderFingerprintOf } from './hash'
|
|
10
|
+
import { admittedBundleRevision } from './revision'
|
|
10
11
|
import { coreExtract, runtimeExtract } from './runtime'
|
|
11
12
|
|
|
12
13
|
const roots: string[] = []
|
|
13
|
-
|
|
14
14
|
afterEach(() => {
|
|
15
15
|
while (roots.length > 0) rmSync(roots.pop()!, { recursive: true, force: true })
|
|
16
16
|
})
|
|
@@ -21,134 +21,114 @@ function fixtureRoot(label: string): string {
|
|
|
21
21
|
mkdirSync(join(root, 'schema'), { recursive: true })
|
|
22
22
|
writeFileSync(join(root, 'package.json'), '{"type":"module"}\n')
|
|
23
23
|
writeFileSync(join(root, 'astrale.config.ts'), 'export default {}\n')
|
|
24
|
-
return root
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function linkCurrentSdk(root: string): void {
|
|
28
24
|
const sdk = realpathSync(join(import.meta.dir, '../../../node_modules/@astrale-os/sdk'))
|
|
29
25
|
const scope = join(root, 'node_modules', '@astrale-os')
|
|
30
26
|
mkdirSync(scope, { recursive: true })
|
|
31
27
|
symlinkSync(sdk, join(scope, 'sdk'), 'dir')
|
|
28
|
+
return root
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
function currentFixture(): DomainHandle {
|
|
35
32
|
const root = fixtureRoot('canonical-runtime')
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const domainFile = join(root, 'implementation.ts')
|
|
33
|
+
const schemaIndex = join(root, 'schema/index.ts')
|
|
34
|
+
const applicationFile = join(root, 'application.ts')
|
|
39
35
|
writeFileSync(
|
|
40
36
|
schemaIndex,
|
|
41
37
|
`
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
output: { type: 'boolean' },
|
|
58
|
-
auth: 'authenticated',
|
|
59
|
-
})
|
|
60
|
-
const Document = nodeClass({
|
|
61
|
-
implements: [Named],
|
|
62
|
-
properties: { title: property({ type: 'string' }, { required: true }) },
|
|
63
|
-
methods: { rename },
|
|
64
|
-
})
|
|
65
|
-
const owned_by = edgeClass.directed({
|
|
66
|
-
source: { as: 'document', accepts: [Document], outgoing: '0..*' },
|
|
67
|
-
target: { as: 'owner', accepts: [Document], incoming: '0..*' },
|
|
68
|
-
})
|
|
69
|
-
const welcome = node(Document, { title: 'Welcome' })
|
|
70
|
-
|
|
71
|
-
export const schema = defineSchema('documents.runtime.test', {
|
|
72
|
-
dependencies: [Directory],
|
|
73
|
-
classes: { Document, owned_by },
|
|
74
|
-
functions: {
|
|
75
|
-
exportAll: fn({
|
|
76
|
-
input: { type: 'object', properties: {}, required: [], additionalProperties: false },
|
|
77
|
-
output: output.binary(),
|
|
78
|
-
auth: 'authenticated',
|
|
79
|
-
}),
|
|
80
|
-
},
|
|
81
|
-
views: { editor: view({ target: Document, auth: 'optional' }) },
|
|
82
|
-
core: {
|
|
83
|
-
nodes: { welcome },
|
|
84
|
-
edges: [edge(welcome, owned_by, domain(), {})],
|
|
85
|
-
},
|
|
86
|
-
})
|
|
87
|
-
`,
|
|
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
|
+
`,
|
|
88
53
|
)
|
|
89
|
-
|
|
90
|
-
writeFileSync(domainFile, `throw new Error('composition entry was imported')\n`)
|
|
54
|
+
writeFileSync(applicationFile, `throw new Error('Application entry was imported')\n`)
|
|
91
55
|
return {
|
|
92
56
|
id: 'documents-runtime-test',
|
|
93
57
|
root,
|
|
94
58
|
configFile: join(root, 'astrale.config.ts'),
|
|
95
|
-
|
|
59
|
+
applicationFile,
|
|
96
60
|
schemaDirName: 'schema',
|
|
97
61
|
schemaDir: join(root, 'schema'),
|
|
98
62
|
schemaIndex,
|
|
99
63
|
}
|
|
100
64
|
}
|
|
101
65
|
|
|
102
|
-
describe('
|
|
103
|
-
test('
|
|
66
|
+
describe('SDK V1 schema extractor', () => {
|
|
67
|
+
test('uses the authored SDK for admission and projects exact imported Classes', async () => {
|
|
104
68
|
const handle = currentFixture()
|
|
105
69
|
const result = await runtimeExtract(handle.schemaIndex, handle.root)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
expect(result.ir).toMatchObject({
|
|
114
|
-
format: 'astrale.dsl',
|
|
115
|
-
domain: 'documents.runtime.test',
|
|
116
|
-
functions: {
|
|
117
|
-
exportAll: {
|
|
118
|
-
static: true,
|
|
119
|
-
inheritance: 'default',
|
|
120
|
-
output: { mode: 'binary' },
|
|
121
|
-
},
|
|
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',
|
|
122
77
|
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
+
},
|
|
127
85
|
},
|
|
128
86
|
},
|
|
129
87
|
})
|
|
130
|
-
expect(result.
|
|
131
|
-
name: 'Named',
|
|
132
|
-
origin: 'directory.runtime.test',
|
|
133
|
-
})
|
|
88
|
+
expect(result.revision).toMatch(/^sha256:[0-9a-f]{64}$/)
|
|
134
89
|
})
|
|
135
90
|
|
|
136
|
-
test('
|
|
91
|
+
test('transports the DSL revision while keeping render identity separate', async () => {
|
|
137
92
|
const handle = currentFixture()
|
|
138
93
|
const extracted = await runtimeExtract(handle.schemaIndex, handle.root)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
expect(
|
|
143
|
-
expect(
|
|
144
|
-
expect(
|
|
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))
|
|
145
100
|
})
|
|
146
101
|
|
|
147
|
-
test('
|
|
102
|
+
test('re-admits installed Bundle JSON before comparing its revision', async () => {
|
|
148
103
|
const handle = currentFixture()
|
|
149
|
-
const
|
|
104
|
+
const extracted = await runtimeExtract(handle.schemaIndex, handle.root)
|
|
105
|
+
if (extracted.revision === null) throw new Error('expected an admitted Schema revision')
|
|
106
|
+
const fixture = (await import(handle.schemaIndex)) as { installedBundle: unknown }
|
|
107
|
+
const installedWire = JSON.parse(JSON.stringify(fixture.installedBundle))
|
|
108
|
+
expect(await admittedBundleRevision(handle.root, installedWire)).toBe(extracted.revision)
|
|
109
|
+
})
|
|
150
110
|
|
|
151
|
-
|
|
111
|
+
test('renders a V1-shaped root as preview when SDK admission fails', async () => {
|
|
112
|
+
const root = fixtureRoot('canonical-preview')
|
|
113
|
+
const schemaIndex = join(root, 'schema/index.ts')
|
|
114
|
+
writeFileSync(
|
|
115
|
+
schemaIndex,
|
|
116
|
+
`export const schema = {
|
|
117
|
+
format: 'astrale.dsl', version: 'v1', origin: 'not an origin',
|
|
118
|
+
dependencies: {}, classes: {}, functions: {}, policies: {}, views: {},
|
|
119
|
+
core: { nodes: {}, edges: [] },
|
|
120
|
+
}\n`,
|
|
121
|
+
)
|
|
122
|
+
expect(await runtimeExtract(schemaIndex, root)).toMatchObject({
|
|
123
|
+
ok: true,
|
|
124
|
+
schemaMode: 'canonical-preview',
|
|
125
|
+
revision: null,
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('extracts Core through the pure Schema entry without importing Application', async () => {
|
|
130
|
+
const handle = currentFixture()
|
|
131
|
+
expect(await coreExtract(handle.schemaIndex, handle.root)).toEqual({
|
|
152
132
|
ok: true,
|
|
153
133
|
core: {
|
|
154
134
|
domain: 'documents.runtime.test',
|
|
@@ -156,62 +136,22 @@ describe('current SDK schema extractor', () => {
|
|
|
156
136
|
{
|
|
157
137
|
path: '/:documents.runtime.test:core.welcome',
|
|
158
138
|
className: 'Document',
|
|
159
|
-
data: { 'documents.runtime.test:class.Document.property.title': 'Welcome' },
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
edges: [
|
|
163
|
-
{
|
|
164
|
-
from: '/:documents.runtime.test:core.welcome',
|
|
165
|
-
to: '/:documents.runtime.test',
|
|
166
|
-
edgeName: 'owned_by',
|
|
167
139
|
data: {},
|
|
168
140
|
},
|
|
169
141
|
],
|
|
142
|
+
edges: [],
|
|
170
143
|
},
|
|
171
144
|
})
|
|
172
145
|
})
|
|
173
146
|
})
|
|
174
147
|
|
|
175
|
-
test('
|
|
176
|
-
const root = fixtureRoot('
|
|
177
|
-
const schemaIndex = join(root, 'schema
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const Item = { config: { name: 'Item' } }
|
|
184
|
-
const linked = { config: { name: 'linked' } }
|
|
185
|
-
const schema = { interfaces: {}, classes: { Item, linked }, imports: [] }
|
|
186
|
-
export const core = {
|
|
187
|
-
domain: 'legacy.runtime.test', schema,
|
|
188
|
-
__nodes: [
|
|
189
|
-
{ path: '/legacy/left', def: Item, data: { name: 'Left' } },
|
|
190
|
-
{ path: '/legacy/right', def: Item, data: { name: 'Right' } },
|
|
191
|
-
],
|
|
192
|
-
__edges: [
|
|
193
|
-
{ from: '/legacy/left', to: '/legacy/right', edge: linked, data: { order: 1 } },
|
|
194
|
-
],
|
|
195
|
-
}
|
|
196
|
-
`,
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
expect(await coreExtract(schemaIndex, domainFile, root)).toEqual({
|
|
200
|
-
ok: true,
|
|
201
|
-
core: {
|
|
202
|
-
domain: 'legacy.runtime.test',
|
|
203
|
-
nodes: [
|
|
204
|
-
{ path: '/legacy/left', className: 'Item', data: { name: 'Left' } },
|
|
205
|
-
{ path: '/legacy/right', className: 'Item', data: { name: 'Right' } },
|
|
206
|
-
],
|
|
207
|
-
edges: [
|
|
208
|
-
{
|
|
209
|
-
from: '/legacy/left',
|
|
210
|
-
to: '/legacy/right',
|
|
211
|
-
edgeName: 'linked',
|
|
212
|
-
data: { order: 1 },
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
},
|
|
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',
|
|
155
|
+
revision: null,
|
|
216
156
|
})
|
|
217
157
|
})
|
|
@@ -5,7 +5,9 @@
|
|
|
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 {
|
|
8
|
+
import type { SchemaIR, SchemaRevision, StudioCore, StudioSchemaBundle } from '../../shared/types'
|
|
9
|
+
|
|
10
|
+
import { isSchemaRevision } from '../../shared/types'
|
|
9
11
|
|
|
10
12
|
const EXTRACTOR = new URL('./extractor.ts', import.meta.url).pathname
|
|
11
13
|
const CORE_EXTRACTOR = new URL('./core-extractor.ts', import.meta.url).pathname
|
|
@@ -13,10 +15,11 @@ const CORE_EXTRACTOR = new URL('./core-extractor.ts', import.meta.url).pathname
|
|
|
13
15
|
export interface RuntimeExtractResult {
|
|
14
16
|
ok: boolean
|
|
15
17
|
ir: SchemaIR | null
|
|
16
|
-
/**
|
|
18
|
+
/** Portable canonical V1 document. Null only for failures. */
|
|
17
19
|
root: unknown | null
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
schemaMode: StudioSchemaBundle['schemaMode']
|
|
21
|
+
/** Present only when the Domain's installed SDK admitted `root`. */
|
|
22
|
+
revision: SchemaRevision | null
|
|
20
23
|
error?: { message: string }
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -42,6 +45,8 @@ export async function runtimeExtract(
|
|
|
42
45
|
ok: false,
|
|
43
46
|
ir: null,
|
|
44
47
|
root: null,
|
|
48
|
+
schemaMode: 'unavailable',
|
|
49
|
+
revision: null,
|
|
45
50
|
error: { message: err.trim() || 'extractor produced no output' },
|
|
46
51
|
}
|
|
47
52
|
}
|
|
@@ -51,16 +56,42 @@ export async function runtimeExtract(
|
|
|
51
56
|
ok: false,
|
|
52
57
|
ir: null,
|
|
53
58
|
root: null,
|
|
59
|
+
schemaMode: 'unavailable',
|
|
60
|
+
revision: null,
|
|
54
61
|
error: parsed.error ?? { message: 'extraction failed' },
|
|
55
62
|
}
|
|
63
|
+
const schemaMode = parsed.schemaMode
|
|
64
|
+
const revision = parsed.revision
|
|
65
|
+
const validMode = schemaMode === 'canonical-admitted' || schemaMode === 'canonical-preview'
|
|
66
|
+
const validRevision =
|
|
67
|
+
schemaMode === 'canonical-admitted' ? isSchemaRevision(revision) : revision == null
|
|
68
|
+
const validRoot = parsed.root !== null && parsed.root !== undefined
|
|
69
|
+
if (!validMode || !validRevision || !validRoot) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
ir: null,
|
|
73
|
+
root: null,
|
|
74
|
+
schemaMode: 'unavailable',
|
|
75
|
+
revision: null,
|
|
76
|
+
error: { message: 'extractor produced an invalid schema admission envelope' },
|
|
77
|
+
}
|
|
78
|
+
}
|
|
56
79
|
return {
|
|
57
80
|
ok: true,
|
|
58
81
|
ir: parsed.ir as SchemaIR,
|
|
59
82
|
root: parsed.root ?? null,
|
|
60
|
-
|
|
83
|
+
schemaMode,
|
|
84
|
+
revision: revision ?? null,
|
|
61
85
|
}
|
|
62
86
|
} catch (e: any) {
|
|
63
|
-
return {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
ir: null,
|
|
90
|
+
root: null,
|
|
91
|
+
schemaMode: 'unavailable',
|
|
92
|
+
revision: null,
|
|
93
|
+
error: { message: String(e?.message ?? e) },
|
|
94
|
+
}
|
|
64
95
|
}
|
|
65
96
|
}
|
|
66
97
|
|
|
@@ -72,18 +103,16 @@ export interface CoreExtractResult {
|
|
|
72
103
|
}
|
|
73
104
|
|
|
74
105
|
/**
|
|
75
|
-
* Spawn the
|
|
76
|
-
*
|
|
77
|
-
* 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.
|
|
78
108
|
*/
|
|
79
109
|
export async function coreExtract(
|
|
80
110
|
schemaIndexPath: string,
|
|
81
|
-
domainFile: string,
|
|
82
111
|
domainDir: string,
|
|
83
112
|
timeoutMs = 20000,
|
|
84
113
|
): Promise<CoreExtractResult> {
|
|
85
114
|
try {
|
|
86
|
-
const proc = Bun.spawn(['bun', 'run', CORE_EXTRACTOR, schemaIndexPath,
|
|
115
|
+
const proc = Bun.spawn(['bun', 'run', CORE_EXTRACTOR, schemaIndexPath, domainDir], {
|
|
87
116
|
cwd: domainDir,
|
|
88
117
|
stdout: 'pipe',
|
|
89
118
|
stderr: 'pipe',
|