@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.
- package/README.md +16 -1
- package/dist/astrale.js +51105 -58057
- package/dist/public/connect-core.js +2085 -30702
- package/dist/public/keys/index.js +884 -29476
- package/dist/public/paths/index.js +13101 -41452
- package/dist/types/admin/contract.d.ts +26 -0
- package/dist/types/admin/instance/client.d.ts +20 -0
- package/dist/types/admin/instance/index.d.ts +2 -0
- package/dist/types/admin/instance/model.d.ts +6 -10
- 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 +11 -36
- package/dist/types/lib/config.d.ts +7 -59
- package/dist/types/lib/failure-debug.d.ts +1 -0
- package/dist/types/lib/idp.d.ts +25 -173
- package/dist/types/lib/instance-target.d.ts +1 -1
- package/dist/types/lib/instance.d.ts +30 -88
- package/dist/types/lib/log.d.ts +5 -3
- package/dist/types/lib/output.d.ts +1 -1
- 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 +109 -0
- package/dist/types/lib/validation.d.ts +4 -1
- package/package.json +15 -20
- package/src/__tests__/fixtures/publication.ts +20 -0
- package/src/admin/.spec/architecture.md +12 -5
- package/src/admin/__tests__/fixture.ts +25 -0
- package/src/admin/catalog/.spec/api.d.ts +1 -3
- package/src/admin/catalog/.spec/architecture.md +5 -4
- package/src/admin/catalog/__tests__/client.test.ts +143 -69
- package/src/admin/catalog/client.ts +40 -54
- package/src/admin/catalog/model.ts +3 -4
- package/src/admin/contract.ts +46 -0
- 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 +5 -9
- package/src/admin/instance/.spec/architecture.md +7 -7
- package/src/admin/instance/__tests__/client.test.ts +157 -175
- package/src/admin/instance/client.ts +39 -128
- package/src/admin/instance/index.ts +1 -2
- package/src/admin/instance/model.ts +12 -18
- package/src/commands/__tests__/admin-instance.test.ts +11 -8
- package/src/commands/__tests__/call-describe.test.ts +46 -33
- package/src/commands/__tests__/call.test.ts +34 -0
- package/src/commands/__tests__/domain-install-operation.test.ts +7 -22
- package/src/commands/__tests__/domain-install-owned.test.ts +1 -1
- package/src/commands/__tests__/domain-list.test.ts +5 -25
- package/src/commands/__tests__/install-direct.test.ts +115 -8
- package/src/commands/__tests__/install-identity-override.test.ts +3 -14
- 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__/introspect-parse.test.ts +80 -1
- package/src/commands/__tests__/logs.test.ts +250 -2
- package/src/commands/__tests__/read-commands.test.ts +62 -10
- package/src/commands/__tests__/token-ttl.test.ts +49 -4
- package/src/commands/__tests__/update.test.ts +25 -3
- package/src/commands/__tests__/view.test.ts +49 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +54 -35
- package/src/commands/domain/list.ts +1 -1
- 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 +48 -35
- package/src/commands/logs.ts +123 -33
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +35 -32
- package/src/commands/token.ts +42 -27
- package/src/commands/ui/__tests__/commands.test.ts +152 -0
- package/src/commands/ui/add.ts +51 -0
- package/src/commands/ui/doctor.ts +13 -0
- package/src/commands/ui/init.ts +38 -0
- package/src/commands/ui/list.ts +26 -0
- package/src/commands/ui/preset-apply.ts +19 -0
- package/src/commands/ui/preset-list.ts +12 -0
- package/src/commands/ui/shared.ts +25 -0
- package/src/commands/update.ts +31 -11
- package/src/commands/view.ts +82 -37
- 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 +27 -20
- package/src/connection/__tests__/exchange.test.ts +30 -1
- 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 +47 -23
- 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__/binary.test.ts +16 -1
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/domain-publication.test.ts +10 -16
- package/src/lib/__tests__/idp.test.ts +34 -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__/log-errors.test.ts +64 -0
- package/src/lib/__tests__/studio-server-deps.test.ts +1 -1
- package/src/lib/__tests__/update.test.ts +264 -11
- package/src/lib/__tests__/view-assets.test.ts +14 -0
- package/src/lib/__tests__/view-external-open-intent.test.ts +150 -0
- package/src/lib/__tests__/view-external-open-origins.test.ts +27 -0
- package/src/lib/__tests__/view-open-intent.test.ts +0 -2
- package/src/lib/__tests__/view-server.test.ts +26 -3
- package/src/lib/__tests__/view-session.test.ts +2 -1
- package/src/lib/admin-instance.ts +14 -8
- package/src/lib/admin-target.ts +21 -2
- package/src/lib/binary.ts +22 -5
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/domain-publication.ts +9 -7
- package/src/lib/failure-debug.ts +26 -0
- package/src/lib/idp.ts +34 -3
- package/src/lib/instance-candidates.ts +2 -2
- package/src/lib/instance-target.ts +3 -14
- package/src/lib/instance.ts +16 -11
- package/src/lib/log.ts +16 -9
- package/src/lib/output.ts +1 -1
- package/src/lib/proc.ts +7 -2
- package/src/lib/provision-instance.ts +34 -57
- package/src/lib/update.ts +298 -45
- package/src/lib/validation.ts +2 -2
- package/src/lib/view/assets.ts +11 -2
- package/src/lib/view/external-open-intent.ts +41 -0
- package/src/lib/view/external-open-origins.ts +37 -0
- package/src/lib/view/host-capabilities.ts +17 -0
- package/src/lib/view/resolve.ts +1 -1
- package/src/lib/view/server.ts +18 -3
- package/src/lib/view/session.ts +2 -0
- package/src/program/.spec/api.d.ts +1 -0
- package/src/program/__tests__/program.test.ts +163 -6
- package/src/program/argv.ts +14 -0
- package/src/program/build.ts +26 -4
- package/src/program/command.ts +1 -0
- package/src/program/index.ts +1 -0
- package/src/program/registry.ts +2 -1
- package/src/setup/__tests__/instance-step.test.ts +27 -10
- package/src/setup/steps/instance.ts +22 -15
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/recorder.ts +9 -1
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +10 -0
- package/src/ui/.spec/laws.ts +36 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +981 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +87 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +710 -0
- package/src/ui/project.ts +219 -0
- package/src/ui/release.ts +321 -0
- package/src/ui/runner.ts +18 -0
- package/studio/client/dist/assets/elk-api-DjmKsHXc.js +1 -0
- package/studio/client/dist/assets/index-7YHdeCxp.js +81 -0
- package/studio/client/dist/assets/index-C0FAnwNw.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio-BilUNb6Z.css +1 -0
- package/studio/client/dist/assets/schema-studio-CABpTfvH.js +8 -0
- package/studio/client/dist/index.html +3 -2
- package/studio/package.json +7 -13
- 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/harness/claude/adapter.test.ts +15 -5
- package/studio/server/agent/harness/gateway/token.test.ts +1 -1
- package/studio/server/agent/harness/gateway/token.ts +3 -3
- 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 +3 -3
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api-agent-loadout.test.ts +1 -1
- package/studio/server/api.test.ts +1 -1
- package/studio/server/cache.test.ts +23 -12
- package/studio/server/cache.ts +12 -15
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +1 -1
- package/studio/server/index.ts +1 -1
- package/studio/server/instances/probe.test.ts +1 -1
- package/studio/server/introspect/anatomy/views/routes.ts +46 -37
- package/studio/server/introspect/anatomy/views.ts +9 -14
- package/studio/server/introspect/anatomy-extras.test.ts +26 -42
- package/studio/server/introspect/anatomy.test.ts +22 -14
- package/studio/server/introspect/anatomy.ts +6 -9
- package/studio/server/introspect/bundle.ts +0 -3
- package/studio/server/introspect/canonical-schema.test.ts +94 -389
- package/studio/server/introspect/canonical-schema.ts +227 -457
- package/studio/server/introspect/core-extractor.ts +18 -127
- package/studio/server/introspect/core.ts +1 -2
- package/studio/server/introspect/diff.test.ts +11 -7
- package/studio/server/introspect/diff.ts +18 -55
- package/studio/server/introspect/extractor.ts +22 -117
- package/studio/server/introspect/overlay-tsmorph.test.ts +119 -240
- package/studio/server/introspect/overlay.test.ts +26 -54
- package/studio/server/introspect/overlay.ts +10 -37
- package/studio/server/introspect/revision.test.ts +1 -1
- package/studio/server/introspect/revision.ts +1 -1
- package/studio/server/introspect/runtime.test.ts +65 -183
- package/studio/server/introspect/runtime.ts +8 -25
- package/studio/server/introspect/schema-ir-json.test.ts +70 -0
- package/studio/server/introspect/schema-ir-json.ts +38 -68
- 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/handlers.ts +116 -636
- package/studio/server/introspect/source-overlay/kernel-calls.ts +0 -3
- package/studio/server/introspect/source-overlay/spans.ts +15 -45
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +17 -74
- package/studio/server/views/target.ts +24 -50
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/schema.ts +38 -107
- package/studio/shared/contracts/surface.test.ts +13 -15
- package/studio/shared/contracts/workspace.ts +2 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -23
- package/viewer/dist/main.js +30 -51
- package/.check-workspace.cjs +0 -40
- package/src/admin/binding.ts +0 -168
- package/src/commands/__tests__/instance-create-hosts.test.ts +0 -29
- package/studio/client/dist/assets/elk-api-lwhFo7vB.js +0 -1
- package/studio/client/dist/assets/index-B-c-smo9.js +0 -8
- package/studio/client/dist/assets/index-BckHuAWk.js +0 -81
- package/studio/client/dist/assets/index-C4aNQ6dz.css +0 -1
- package/studio/client/dist/assets/index-ChOr3yP0.css +0 -1
- package/studio/server/introspect/anatomy/views/legacy.ts +0 -232
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pure projection
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* the domain's own SDK cohort, not a second copy installed beside Studio.
|
|
2
|
+
* Pure projection from an SDK-admitted portable DomainSchema into Studio's
|
|
3
|
+
* intentionally lossy render model. The extractor calls the authored Domain's
|
|
4
|
+
* own installed SDK for admission before this module interprets the document.
|
|
6
5
|
*/
|
|
7
6
|
import type {
|
|
8
7
|
IrCallableAuth,
|
|
9
8
|
IrCallableOutput,
|
|
10
9
|
IrClass,
|
|
11
|
-
|
|
10
|
+
IrClassKey,
|
|
11
|
+
IrClassRef,
|
|
12
12
|
IrEndpoint,
|
|
13
13
|
IrFunction,
|
|
14
14
|
IrImportDescriptor,
|
|
15
|
-
IrInterface,
|
|
16
15
|
IrMethod,
|
|
17
16
|
IrSchemaRef,
|
|
18
17
|
IrView,
|
|
@@ -21,7 +20,7 @@ import type {
|
|
|
21
20
|
StudioCore,
|
|
22
21
|
} from '../../shared/types'
|
|
23
22
|
|
|
24
|
-
import {
|
|
23
|
+
import { classRefKey, isIrClassRef, schemaRefKey } from '../../shared/schema/identity'
|
|
25
24
|
import { isSchemaRevision } from '../../shared/types'
|
|
26
25
|
|
|
27
26
|
type AnyRecord = Record<string, unknown>
|
|
@@ -33,22 +32,21 @@ export type CanonicalDomainSchemaV1 = AnyRecord & {
|
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
export interface CanonicalSchemaProjection {
|
|
36
|
-
ir: SchemaIR
|
|
37
|
-
importedInterfaces: Record<string, IrInterface>
|
|
35
|
+
readonly ir: SchemaIR
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
export type CanonicalSchemaAdmission =
|
|
41
39
|
| {
|
|
42
|
-
status: 'admitted'
|
|
43
|
-
root: CanonicalDomainSchemaV1
|
|
44
|
-
closure: CanonicalDomainSchemaV1[]
|
|
45
|
-
revision: `sha256:${string}`
|
|
40
|
+
readonly status: 'admitted'
|
|
41
|
+
readonly root: CanonicalDomainSchemaV1
|
|
42
|
+
readonly closure: CanonicalDomainSchemaV1[]
|
|
43
|
+
readonly revision: `sha256:${string}`
|
|
46
44
|
}
|
|
47
45
|
| {
|
|
48
|
-
status: 'preview'
|
|
49
|
-
root: CanonicalDomainSchemaV1
|
|
50
|
-
closure: CanonicalDomainSchemaV1[]
|
|
51
|
-
revision: null
|
|
46
|
+
readonly status: 'preview'
|
|
47
|
+
readonly root: CanonicalDomainSchemaV1
|
|
48
|
+
readonly closure: CanonicalDomainSchemaV1[]
|
|
49
|
+
readonly revision: null
|
|
52
50
|
}
|
|
53
51
|
|
|
54
52
|
export function isCanonicalDomainSchemaV1(value: unknown): value is CanonicalDomainSchemaV1 {
|
|
@@ -67,63 +65,32 @@ export function findCanonicalDomainSchemaExport(
|
|
|
67
65
|
): CanonicalDomainSchemaV1 | null {
|
|
68
66
|
if (isCanonicalDomainSchemaV1(moduleExports.schema)) return moduleExports.schema
|
|
69
67
|
if (isCanonicalDomainSchemaV1(moduleExports.default)) return moduleExports.default
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
68
|
+
return (
|
|
69
|
+
Object.entries(moduleExports)
|
|
70
|
+
.filter((entry): entry is [string, CanonicalDomainSchemaV1] =>
|
|
71
|
+
isCanonicalDomainSchemaV1(entry[1]),
|
|
72
|
+
)
|
|
73
|
+
.sort(([left], [right]) => left.localeCompare(right))[0]?.[1] ?? null
|
|
74
|
+
)
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
/**
|
|
80
|
-
* Read the exact retained dependency closure through the domain's own SDK.
|
|
81
|
-
* Calls are optional and fail closed: an unrecognised/substituted result is
|
|
82
|
-
* discarded rather than trusted as imported-schema evidence.
|
|
83
|
-
*/
|
|
77
|
+
/** Read the exact retained dependency closure through the authored SDK. */
|
|
84
78
|
export function closureFromSdk(
|
|
85
79
|
sdkModule: Record<string, unknown>,
|
|
86
80
|
root: CanonicalDomainSchemaV1,
|
|
87
81
|
): CanonicalDomainSchemaV1[] {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (closure) return closure
|
|
97
|
-
}
|
|
98
|
-
} catch {
|
|
99
|
-
// A structurally V1-looking export may lack retained SDK admission context.
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const schemaApi = asRecord(sdkModule.schema)
|
|
104
|
-
const resolve = schemaApi?.resolve
|
|
105
|
-
if (typeof resolve === 'function') {
|
|
106
|
-
try {
|
|
107
|
-
const candidate = asRecord(Reflect.apply(resolve, schemaApi, [root]))
|
|
108
|
-
const resolution = asRecord(candidate?.$)
|
|
109
|
-
if (resolution?.schema === root) {
|
|
110
|
-
const closure = admitClosure(resolution.closure, root)
|
|
111
|
-
if (closure) return closure
|
|
112
|
-
}
|
|
113
|
-
} catch {
|
|
114
|
-
// Missing retained context is a valid degradation: local schema still renders.
|
|
115
|
-
}
|
|
82
|
+
const create = asRecord(sdkModule.bundle)?.create
|
|
83
|
+
if (typeof create !== 'function') return []
|
|
84
|
+
try {
|
|
85
|
+
const candidate = asRecord(Reflect.apply(create, undefined, [root]))
|
|
86
|
+
if (candidate?.root !== root) return []
|
|
87
|
+
return admitClosure(candidate.closure, root) ?? []
|
|
88
|
+
} catch {
|
|
89
|
+
return []
|
|
116
90
|
}
|
|
117
|
-
|
|
118
|
-
return []
|
|
119
91
|
}
|
|
120
92
|
|
|
121
|
-
/**
|
|
122
|
-
* Re-admit a structurally V1-looking export through the Domain's own SDK cohort.
|
|
123
|
-
* Retained Builder context supplies the exact dependency closure. If that proof
|
|
124
|
-
* is unavailable or admission fails, Studio may still render the document as a
|
|
125
|
-
* structural preview, but it must not assign a canonical revision to it.
|
|
126
|
-
*/
|
|
93
|
+
/** Re-admit one portable root and prove its revision through the authored SDK. */
|
|
127
94
|
export function admitCanonicalSchemaFromSdk(
|
|
128
95
|
sdkModule: Record<string, unknown>,
|
|
129
96
|
candidate: CanonicalDomainSchemaV1,
|
|
@@ -132,7 +99,12 @@ export function admitCanonicalSchemaFromSdk(
|
|
|
132
99
|
const schemaApi = asRecord(sdkModule.schema)
|
|
133
100
|
const accept = schemaApi?.accept
|
|
134
101
|
const revision = schemaApi?.revision
|
|
135
|
-
|
|
102
|
+
const resolve = schemaApi?.resolve
|
|
103
|
+
if (
|
|
104
|
+
typeof accept !== 'function' ||
|
|
105
|
+
typeof revision !== 'function' ||
|
|
106
|
+
typeof resolve !== 'function'
|
|
107
|
+
) {
|
|
136
108
|
return { status: 'preview', root: candidate, closure: retainedClosure, revision: null }
|
|
137
109
|
}
|
|
138
110
|
|
|
@@ -140,7 +112,7 @@ export function admitCanonicalSchemaFromSdk(
|
|
|
140
112
|
const revisions = new Map<string, string>()
|
|
141
113
|
for (const dependency of retainedClosure) {
|
|
142
114
|
const value = Reflect.apply(revision, schemaApi, [dependency])
|
|
143
|
-
if (!isSchemaRevision(value)) throw new TypeError('SDK returned an invalid
|
|
115
|
+
if (!isSchemaRevision(value)) throw new TypeError('SDK returned an invalid revision.')
|
|
144
116
|
revisions.set(dependency.origin, value)
|
|
145
117
|
}
|
|
146
118
|
const lookup = {
|
|
@@ -151,77 +123,27 @@ export function admitCanonicalSchemaFromSdk(
|
|
|
151
123
|
}
|
|
152
124
|
const accepted = Reflect.apply(accept, schemaApi, [candidate, lookup])
|
|
153
125
|
if (!isCanonicalDomainSchemaV1(accepted) || accepted.origin !== candidate.origin) {
|
|
154
|
-
throw new TypeError('SDK admission returned
|
|
126
|
+
throw new TypeError('SDK admission returned another root.')
|
|
155
127
|
}
|
|
156
128
|
const acceptedRevision = Reflect.apply(revision, schemaApi, [accepted])
|
|
157
|
-
if (!isSchemaRevision(acceptedRevision))
|
|
158
|
-
throw new TypeError('SDK returned an invalid
|
|
129
|
+
if (!isSchemaRevision(acceptedRevision))
|
|
130
|
+
throw new TypeError('SDK returned an invalid revision.')
|
|
131
|
+
const domain = asRecord(Reflect.apply(resolve, schemaApi, [accepted]))
|
|
132
|
+
if (domain?.source !== accepted || domain.origin !== accepted.origin) {
|
|
133
|
+
throw new TypeError('SDK resolution did not retain the admitted root.')
|
|
159
134
|
}
|
|
160
|
-
|
|
161
|
-
// Resolve the newly accepted value once more so its retained closure, rather
|
|
162
|
-
// than Studio's structural candidate, is what feeds exact-ref projection.
|
|
163
|
-
const resolve = schemaApi.resolve
|
|
164
|
-
let closure = retainedClosure
|
|
165
|
-
if (typeof resolve === 'function') {
|
|
166
|
-
const resolved = asRecord(Reflect.apply(resolve, schemaApi, [accepted]))
|
|
167
|
-
const proof = asRecord(resolved?.$)
|
|
168
|
-
if (proof?.schema !== accepted) throw new TypeError('SDK resolution did not retain the root')
|
|
169
|
-
const admitted = admitClosure(proof.closure, accepted)
|
|
170
|
-
if (!admitted) throw new TypeError('SDK resolution returned an invalid dependency closure')
|
|
171
|
-
closure = admitted
|
|
172
|
-
}
|
|
173
|
-
|
|
135
|
+
const closure = closureFromSdk(sdkModule, accepted)
|
|
174
136
|
return { status: 'admitted', root: accepted, closure, revision: acceptedRevision }
|
|
175
137
|
} catch {
|
|
176
138
|
return { status: 'preview', root: candidate, closure: retainedClosure, revision: null }
|
|
177
139
|
}
|
|
178
140
|
}
|
|
179
141
|
|
|
180
|
-
/** Add fields required by the current shared contract to a legacy serializer IR. */
|
|
181
|
-
export function normalizeLegacySchemaIR(value: unknown): SchemaIR {
|
|
182
|
-
const ir = asRecord(value)
|
|
183
|
-
if (!ir) throw new TypeError('legacy schema IR must be an object')
|
|
184
|
-
|
|
185
|
-
const functions: Record<string, IrFunction> = {}
|
|
186
|
-
for (const [name, raw] of entriesOf(ir.functions)) {
|
|
187
|
-
const declaration = asRecord(raw) ?? {}
|
|
188
|
-
const params = schemaRecord(declaration.params)
|
|
189
|
-
const returns = studioSchema(declaration.returns)
|
|
190
|
-
const requiredParams =
|
|
191
|
-
declaration.input === undefined
|
|
192
|
-
? legacyRequiredParams(params)
|
|
193
|
-
: requiredNames(declaration.input)
|
|
194
|
-
functions[name] = {
|
|
195
|
-
name,
|
|
196
|
-
input: studioSchema(declaration.input ?? objectInputFromParams(params, requiredParams)),
|
|
197
|
-
params,
|
|
198
|
-
requiredParams,
|
|
199
|
-
output: callableOutput(declaration.output, returns),
|
|
200
|
-
returns,
|
|
201
|
-
static: true,
|
|
202
|
-
inheritance: 'default',
|
|
203
|
-
...(callableAuth(declaration.auth) ? { auth: callableAuth(declaration.auth) } : {}),
|
|
204
|
-
...(typeof declaration.description === 'string'
|
|
205
|
-
? { description: declaration.description }
|
|
206
|
-
: {}),
|
|
207
|
-
...(declaration.policy === undefined ? {} : { policy: jsonCopy(declaration.policy) }),
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return { ...(ir as unknown as SchemaIR), functions }
|
|
212
|
-
}
|
|
213
|
-
|
|
214
142
|
export function projectCanonicalSchema(
|
|
215
143
|
root: CanonicalDomainSchemaV1,
|
|
216
144
|
closure: readonly CanonicalDomainSchemaV1[] = [],
|
|
217
145
|
): CanonicalSchemaProjection {
|
|
218
146
|
const origin = root.origin
|
|
219
|
-
const interfaces = Object.fromEntries(
|
|
220
|
-
entriesOf(root.interfaces).map(([name, declaration]) => [
|
|
221
|
-
name,
|
|
222
|
-
projectInterface(origin, name, declaration),
|
|
223
|
-
]),
|
|
224
|
-
)
|
|
225
147
|
const classes = Object.fromEntries(
|
|
226
148
|
entriesOf(root.classes).map(([name, declaration]) => [
|
|
227
149
|
name,
|
|
@@ -237,193 +159,138 @@ export function projectCanonicalSchema(
|
|
|
237
159
|
const views = Object.fromEntries(
|
|
238
160
|
entriesOf(root.views).map(([name, declaration]) => [name, projectView(name, declaration)]),
|
|
239
161
|
)
|
|
240
|
-
|
|
241
|
-
const importsByKey: Record<IrDefinitionKey, IrImportDescriptor> = {}
|
|
242
|
-
const importedInterfacesByKey: Record<IrDefinitionKey, IrInterface> = {}
|
|
243
162
|
const schemas = new Map(
|
|
244
|
-
closure.filter(isCanonicalDomainSchemaV1).map((
|
|
163
|
+
closure.filter(isCanonicalDomainSchemaV1).map((value) => [value.origin, value]),
|
|
245
164
|
)
|
|
246
|
-
const
|
|
165
|
+
const importsByKey = {} as Record<IrClassKey, IrImportDescriptor>
|
|
166
|
+
const importedClassesByKey = {} as Record<IrClassKey, IrClass>
|
|
167
|
+
const pending = collectClassRefs(root)
|
|
247
168
|
const visited = new Set<string>()
|
|
248
169
|
|
|
249
|
-
for (let index = 0; index < pending.length; index
|
|
170
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
250
171
|
const ref = pending[index]
|
|
251
|
-
if (
|
|
252
|
-
const key =
|
|
172
|
+
if (ref.origin === origin) continue
|
|
173
|
+
const key = classRefKey(ref)
|
|
253
174
|
if (visited.has(key)) continue
|
|
254
175
|
visited.add(key)
|
|
255
|
-
|
|
256
|
-
importsByKey[key] = {
|
|
257
|
-
origin: ref.origin,
|
|
258
|
-
definition: ref.kind,
|
|
259
|
-
ref,
|
|
260
|
-
key,
|
|
261
|
-
}
|
|
262
|
-
if (ref.kind !== 'interface' || key in importedInterfacesByKey) continue
|
|
263
|
-
|
|
176
|
+
importsByKey[key] = { origin: ref.origin, ref, key }
|
|
264
177
|
const owner = schemas.get(ref.origin)
|
|
265
|
-
const declaration = asRecord(asRecord(owner?.
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
pending.push(...collectDefinitionRefsFromDeclaration(declaration))
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Bare member names are retained solely as an unambiguous compatibility
|
|
273
|
-
// lookup. The canonical Key maps above remain complete when origins or kinds
|
|
274
|
-
// reuse a name, and prevent a traversal-order-dependent "first one wins".
|
|
275
|
-
const nameCounts = new Map<string, number>()
|
|
276
|
-
for (const descriptor of Object.values(importsByKey)) {
|
|
277
|
-
const name = descriptor.ref?.name
|
|
278
|
-
if (name) nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1)
|
|
178
|
+
const declaration = asRecord(asRecord(owner?.classes)?.[ref.name])
|
|
179
|
+
if (owner === undefined || declaration === null) continue
|
|
180
|
+
importedClassesByKey[key] = projectClass(owner.origin, ref.name, declaration)
|
|
181
|
+
pending.push(...collectClassRefsFromClass(declaration))
|
|
279
182
|
}
|
|
280
|
-
const localNames = new Set([...Object.keys(interfaces), ...Object.keys(classes)])
|
|
281
|
-
const isUnambiguous = (name: string): boolean =>
|
|
282
|
-
nameCounts.get(name) === 1 && !localNames.has(name)
|
|
283
|
-
const imports = Object.fromEntries(
|
|
284
|
-
Object.values(importsByKey).flatMap((descriptor) => {
|
|
285
|
-
const name = descriptor.ref?.name
|
|
286
|
-
return name && isUnambiguous(name) ? [[name, descriptor]] : []
|
|
287
|
-
}),
|
|
288
|
-
)
|
|
289
|
-
const importedInterfaces = Object.fromEntries(
|
|
290
|
-
Object.values(importedInterfacesByKey).flatMap((declaration) =>
|
|
291
|
-
isUnambiguous(declaration.name) ? [[declaration.name, declaration]] : [],
|
|
292
|
-
),
|
|
293
|
-
)
|
|
294
183
|
|
|
295
|
-
const dependencies = arrayOf(root.dependencies)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
: [],
|
|
302
|
-
)
|
|
184
|
+
const dependencies = arrayOf(root.dependencies).flatMap((value) => {
|
|
185
|
+
const dependency = asRecord(value)
|
|
186
|
+
return typeof dependency?.origin === 'string' && typeof dependency.revision === 'string'
|
|
187
|
+
? [{ origin: dependency.origin, revision: dependency.revision }]
|
|
188
|
+
: []
|
|
189
|
+
})
|
|
303
190
|
|
|
304
191
|
return {
|
|
305
192
|
ir: {
|
|
306
193
|
format: 'astrale.dsl',
|
|
307
194
|
version: 'v1',
|
|
308
195
|
domain: origin,
|
|
309
|
-
types: schemaRecord(root.types),
|
|
310
|
-
interfaces,
|
|
311
196
|
classes,
|
|
312
|
-
imports,
|
|
313
197
|
importsByKey,
|
|
314
|
-
|
|
198
|
+
importedClassesByKey,
|
|
315
199
|
functions,
|
|
316
200
|
views,
|
|
317
201
|
policies: recordCopy(root.policies),
|
|
318
202
|
dependencies,
|
|
319
203
|
core: jsonCopy(root.core),
|
|
320
204
|
},
|
|
321
|
-
importedInterfaces,
|
|
322
205
|
}
|
|
323
206
|
}
|
|
324
207
|
|
|
325
|
-
/** Project canonical
|
|
208
|
+
/** Project canonical Core data without importing Application or Runtime modules. */
|
|
326
209
|
export function projectCanonicalCore(
|
|
327
210
|
root: CanonicalDomainSchemaV1,
|
|
328
211
|
): Pick<StudioCore, 'domain' | 'nodes' | 'edges'> {
|
|
329
212
|
const core = asRecord(root.core) ?? {}
|
|
330
213
|
const nodes = entriesOf(core.nodes).map(([slug, raw]) => {
|
|
331
214
|
const node = asRecord(raw) ?? {}
|
|
332
|
-
const
|
|
215
|
+
const selectedClass = classRef(node.class)
|
|
333
216
|
return {
|
|
334
|
-
path: corePath({ origin: root.origin,
|
|
335
|
-
className:
|
|
217
|
+
path: corePath({ origin: root.origin, name: slug }),
|
|
218
|
+
className: selectedClass?.name ?? '?',
|
|
336
219
|
data: recordCopy(node.properties),
|
|
337
220
|
}
|
|
338
221
|
})
|
|
339
222
|
const edges = arrayOf(core.edges).map((raw) => {
|
|
340
223
|
const edge = asRecord(raw) ?? {}
|
|
341
|
-
const classRef = schemaRef(edge.class)
|
|
342
224
|
return {
|
|
343
225
|
from: coreEndpoint(edge.source, root.origin),
|
|
344
226
|
to: coreEndpoint(edge.target, root.origin),
|
|
345
|
-
edgeName: classRef?.name ?? '?',
|
|
227
|
+
edgeName: classRef(edge.class)?.name ?? '?',
|
|
346
228
|
...(edge.properties === undefined ? {} : { data: recordCopy(edge.properties) }),
|
|
347
229
|
}
|
|
348
230
|
})
|
|
349
231
|
return { domain: root.origin, nodes, edges }
|
|
350
232
|
}
|
|
351
233
|
|
|
352
|
-
function projectInterface(origin: string, name: string, value: unknown): IrInterface {
|
|
353
|
-
const declaration = asRecord(value) ?? {}
|
|
354
|
-
const family = definitionFamily(declaration.family, 'both')
|
|
355
|
-
const extendsRefs = refsOf(declaration.extends, 'interface')
|
|
356
|
-
return {
|
|
357
|
-
type: 'interface',
|
|
358
|
-
name,
|
|
359
|
-
origin,
|
|
360
|
-
family,
|
|
361
|
-
ref: { origin, kind: 'interface', name },
|
|
362
|
-
extends: extendsRefs.map((ref) => ref.name),
|
|
363
|
-
extendsRefs,
|
|
364
|
-
properties: propertySchemas(declaration),
|
|
365
|
-
required: requiredNames(declaration.properties),
|
|
366
|
-
methods: projectMethods(declaration.methods),
|
|
367
|
-
...(family === 'edge' ? edgeFields(declaration) : {}),
|
|
368
|
-
...(typeof declaration.description === 'string'
|
|
369
|
-
? { description: declaration.description }
|
|
370
|
-
: {}),
|
|
371
|
-
...(asRecord(declaration.propertyMetadata)
|
|
372
|
-
? { propertyMetadata: recordCopy(declaration.propertyMetadata) }
|
|
373
|
-
: {}),
|
|
374
|
-
...(dataDeclaration(declaration.data) ? { data: dataDeclaration(declaration.data) } : {}),
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
234
|
function projectClass(origin: string, name: string, value: unknown): IrClass {
|
|
379
235
|
const declaration = asRecord(value) ?? {}
|
|
380
|
-
const
|
|
381
|
-
const
|
|
236
|
+
const kind = declaration.kind === 'edge' ? 'edge' : 'node'
|
|
237
|
+
const extendsRefs = refsOf(declaration.extends)
|
|
238
|
+
const propertyEntries = entriesOf(declaration.properties)
|
|
239
|
+
const properties = Object.fromEntries(
|
|
240
|
+
propertyEntries.map(([propertyName, raw]) => [
|
|
241
|
+
propertyName,
|
|
242
|
+
studioSchema(asRecord(raw)?.schema),
|
|
243
|
+
]),
|
|
244
|
+
)
|
|
245
|
+
const required = propertyEntries.flatMap(([propertyName, raw]) =>
|
|
246
|
+
asRecord(raw)?.required === true ? [propertyName] : [],
|
|
247
|
+
)
|
|
248
|
+
const propertyMetadata = Object.fromEntries(
|
|
249
|
+
propertyEntries.map(([propertyName, raw]) => {
|
|
250
|
+
const member = { ...(asRecord(raw) ?? {}) }
|
|
251
|
+
delete member.schema
|
|
252
|
+
return [propertyName, jsonCopy(member)]
|
|
253
|
+
}),
|
|
254
|
+
)
|
|
382
255
|
const policies = Object.fromEntries(
|
|
383
256
|
entriesOf(declaration.policies).flatMap(([policyName, raw]) => {
|
|
384
|
-
const ref =
|
|
257
|
+
const ref = definitionRefOf(raw)
|
|
385
258
|
return ref ? [[policyName, ref]] : []
|
|
386
259
|
}),
|
|
387
260
|
)
|
|
388
261
|
return {
|
|
389
|
-
type:
|
|
262
|
+
type: kind,
|
|
390
263
|
name,
|
|
391
264
|
origin,
|
|
392
265
|
ref: { origin, kind: 'class', name },
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
properties
|
|
396
|
-
required
|
|
266
|
+
extends: extendsRefs.map((ref) => ref.name),
|
|
267
|
+
extendsRefs,
|
|
268
|
+
properties,
|
|
269
|
+
required,
|
|
397
270
|
methods: projectMethods(declaration.methods),
|
|
398
|
-
...(
|
|
399
|
-
...(typeof declaration.icon === 'string' ? { icon: declaration.icon } : {}),
|
|
271
|
+
...(kind === 'edge' ? edgeFields(declaration) : {}),
|
|
400
272
|
...(typeof declaration.description === 'string'
|
|
401
273
|
? { description: declaration.description }
|
|
402
274
|
: {}),
|
|
403
|
-
...(
|
|
404
|
-
? { propertyMetadata: recordCopy(declaration.propertyMetadata) }
|
|
405
|
-
: {}),
|
|
275
|
+
...(Object.keys(propertyMetadata).length === 0 ? {} : { propertyMetadata }),
|
|
406
276
|
...(dataDeclaration(declaration.data) ? { data: dataDeclaration(declaration.data) } : {}),
|
|
407
|
-
...(Object.keys(policies).length
|
|
277
|
+
...(Object.keys(policies).length === 0 ? {} : { policies }),
|
|
408
278
|
}
|
|
409
279
|
}
|
|
410
280
|
|
|
411
281
|
function projectMethods(value: unknown): Record<string, IrMethod> {
|
|
412
282
|
return Object.fromEntries(
|
|
413
|
-
entriesOf(value).map(([name,
|
|
414
|
-
const
|
|
415
|
-
const raw = asRecord(declaration) ?? {}
|
|
283
|
+
entriesOf(value).map(([name, raw]) => {
|
|
284
|
+
const declaration = asRecord(raw) ?? {}
|
|
416
285
|
const inheritance =
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
raw.inheritance === 'default'
|
|
420
|
-
? raw.inheritance
|
|
286
|
+
declaration.inheritance === 'abstract' || declaration.inheritance === 'sealed'
|
|
287
|
+
? declaration.inheritance
|
|
421
288
|
: 'default'
|
|
422
289
|
return [
|
|
423
290
|
name,
|
|
424
291
|
{
|
|
425
|
-
...
|
|
426
|
-
static:
|
|
292
|
+
...projectCallable(name, declaration),
|
|
293
|
+
static: declaration.static === true,
|
|
427
294
|
inheritance,
|
|
428
295
|
} satisfies IrMethod,
|
|
429
296
|
]
|
|
@@ -431,50 +298,40 @@ function projectMethods(value: unknown): Record<string, IrMethod> {
|
|
|
431
298
|
)
|
|
432
299
|
}
|
|
433
300
|
|
|
434
|
-
function projectFunction(name: string,
|
|
435
|
-
return
|
|
436
|
-
...projectCallable(name, declaration),
|
|
437
|
-
static: true,
|
|
438
|
-
inheritance: 'default',
|
|
439
|
-
}
|
|
301
|
+
function projectFunction(name: string, value: unknown): IrFunction {
|
|
302
|
+
return projectCallable(name, value)
|
|
440
303
|
}
|
|
441
304
|
|
|
442
|
-
function projectCallable(name: string, value: unknown):
|
|
305
|
+
function projectCallable(name: string, value: unknown): IrFunction {
|
|
443
306
|
const declaration = asRecord(value) ?? {}
|
|
444
307
|
const input = studioSchema(declaration.input)
|
|
445
|
-
const output = callableOutput(declaration.output
|
|
308
|
+
const output = callableOutput(declaration.output)
|
|
446
309
|
const auth = callableAuth(declaration.auth)
|
|
447
310
|
return {
|
|
448
311
|
name,
|
|
449
312
|
input,
|
|
450
|
-
params: callableParams(declaration.input),
|
|
451
|
-
requiredParams: requiredNames(declaration.input),
|
|
452
313
|
output,
|
|
453
|
-
returns: outputValue(output),
|
|
454
314
|
...(typeof declaration.description === 'string'
|
|
455
315
|
? { description: declaration.description }
|
|
456
316
|
: {}),
|
|
457
|
-
...(auth ? {
|
|
317
|
+
...(auth === undefined ? {} : { auth }),
|
|
458
318
|
...(declaration.policy === undefined ? {} : { policy: jsonCopy(declaration.policy) }),
|
|
459
319
|
}
|
|
460
320
|
}
|
|
461
321
|
|
|
462
322
|
function projectView(name: string, value: unknown): IrView {
|
|
463
323
|
const declaration = asRecord(value) ?? {}
|
|
464
|
-
const
|
|
465
|
-
const target =
|
|
466
|
-
rawTarget?.kind === 'definition'
|
|
467
|
-
? ({
|
|
468
|
-
kind: 'definition',
|
|
469
|
-
definitions: refsOf(rawTarget.definitions, ['class', 'interface']),
|
|
470
|
-
} as const)
|
|
471
|
-
: ({ kind: 'domain' } as const)
|
|
472
|
-
const auth =
|
|
473
|
-
declaration.auth === 'optional' || declaration.auth === 'public' ? declaration.auth : 'required'
|
|
324
|
+
const target = asRecord(declaration.target)
|
|
474
325
|
return {
|
|
475
326
|
name,
|
|
476
|
-
target
|
|
477
|
-
|
|
327
|
+
target:
|
|
328
|
+
target?.kind === 'definition'
|
|
329
|
+
? { kind: 'definition', definitions: refsOf(target.definitions) }
|
|
330
|
+
: { kind: 'domain' },
|
|
331
|
+
auth:
|
|
332
|
+
declaration.auth === 'optional' || declaration.auth === 'public'
|
|
333
|
+
? declaration.auth
|
|
334
|
+
: 'required',
|
|
478
335
|
...(typeof declaration.description === 'string'
|
|
479
336
|
? { description: declaration.description }
|
|
480
337
|
: {}),
|
|
@@ -493,238 +350,186 @@ function edgeFields(
|
|
|
493
350
|
projectEndpoint(endpoints?.source, 'outgoing'),
|
|
494
351
|
projectEndpoint(endpoints?.target, 'incoming'),
|
|
495
352
|
]
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
353
|
+
const constraints = asRecord(declaration.constraints)
|
|
354
|
+
return {
|
|
355
|
+
endpoints: projected,
|
|
356
|
+
orientation,
|
|
357
|
+
constraints: {
|
|
358
|
+
...(constraints?.noSelf === true ? { noSelf: true } : {}),
|
|
359
|
+
...(constraints?.acyclic === true ? { acyclic: true } : {}),
|
|
360
|
+
},
|
|
500
361
|
}
|
|
501
|
-
return { endpoints: projected, orientation, constraints }
|
|
502
362
|
}
|
|
503
363
|
|
|
504
364
|
function projectEndpoint(
|
|
505
365
|
value: unknown,
|
|
506
|
-
|
|
366
|
+
cardinality: 'outgoing' | 'incoming' | 'incident',
|
|
507
367
|
): IrEndpoint {
|
|
508
368
|
const endpoint = asRecord(value) ?? {}
|
|
509
|
-
const refs = refsOf(endpoint.accepts
|
|
510
|
-
const
|
|
369
|
+
const refs = refsOf(endpoint.accepts)
|
|
370
|
+
const count = edgeCount(endpoint[cardinality])
|
|
511
371
|
return {
|
|
512
372
|
name: typeof endpoint.role === 'string' ? endpoint.role : '',
|
|
513
373
|
types: refs.map((ref) => ref.name),
|
|
514
374
|
refs,
|
|
515
|
-
...(
|
|
375
|
+
...(count === undefined ? {} : { cardinality: count }),
|
|
516
376
|
}
|
|
517
377
|
}
|
|
518
378
|
|
|
519
379
|
function edgeCount(value: unknown): IrEndpoint['cardinality'] | undefined {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
case '0..1':
|
|
526
|
-
return { min: 0, max: 1 }
|
|
527
|
-
case '1':
|
|
528
|
-
return { min: 1, max: 1 }
|
|
529
|
-
default:
|
|
530
|
-
return undefined
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function propertySchemas(declaration: AnyRecord): Record<string, JsonSchema> {
|
|
535
|
-
const object = asRecord(declaration.properties) ?? {}
|
|
536
|
-
return Object.fromEntries(
|
|
537
|
-
entriesOf(object.properties).map(([name, schema]) => [name, studioSchema(schema)]),
|
|
538
|
-
)
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function callableParams(input: unknown): Record<string, JsonSchema> {
|
|
542
|
-
const object = asRecord(input)
|
|
543
|
-
if (!object) return {}
|
|
544
|
-
return Object.fromEntries(
|
|
545
|
-
entriesOf(object.properties).map(([name, schema]) => [name, studioSchema(schema)]),
|
|
546
|
-
)
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function callableOutput(value: unknown, fallback: JsonSchema): IrCallableOutput {
|
|
550
|
-
const output = asRecord(value)
|
|
551
|
-
if (output?.mode === 'stream') return { mode: 'stream', item: studioSchema(output.item) }
|
|
552
|
-
if (output?.mode === 'binary') return { mode: 'binary' }
|
|
553
|
-
if (output?.mode === 'value') return { mode: 'value', schema: studioSchema(output.schema) }
|
|
554
|
-
return { mode: 'value', schema: fallback }
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function outputValue(output: IrCallableOutput): JsonSchema {
|
|
558
|
-
if (output.mode === 'value') return output.schema
|
|
559
|
-
if (output.mode === 'stream') return output.item
|
|
560
|
-
return {}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
function callableAuth(value: unknown): IrCallableAuth | undefined {
|
|
564
|
-
return value === 'anonymous' || value === 'authenticated' || value === 'authorized'
|
|
565
|
-
? value
|
|
566
|
-
: undefined
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function definitionFamily(value: unknown, fallback: 'node' | 'both'): 'node' | 'edge' | 'both' {
|
|
570
|
-
return value === 'node' || value === 'edge' || value === 'both' ? value : fallback
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function dataDeclaration(
|
|
574
|
-
value: unknown,
|
|
575
|
-
): { mediaType: string; [key: string]: unknown } | undefined {
|
|
576
|
-
const data = asRecord(value)
|
|
577
|
-
return typeof data?.mediaType === 'string'
|
|
578
|
-
? (jsonCopy(data) as { mediaType: string; [key: string]: unknown })
|
|
579
|
-
: undefined
|
|
380
|
+
if (value === '0..*') return { min: 0, max: null }
|
|
381
|
+
if (value === '1..*') return { min: 1, max: null }
|
|
382
|
+
if (value === '0..1') return { min: 0, max: 1 }
|
|
383
|
+
if (value === '1') return { min: 1, max: 1 }
|
|
384
|
+
return undefined
|
|
580
385
|
}
|
|
581
386
|
|
|
582
|
-
function
|
|
583
|
-
|
|
584
|
-
kinds: IrSchemaRef['kind'] | readonly IrSchemaRef['kind'][],
|
|
585
|
-
): IrSchemaRef[] {
|
|
586
|
-
const accepted = new Set(Array.isArray(kinds) ? kinds : [kinds])
|
|
587
|
-
return arrayOf(value)
|
|
588
|
-
.map(schemaRef)
|
|
589
|
-
.filter((ref): ref is IrSchemaRef => ref !== null && accepted.has(ref.kind))
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function schemaRef(value: unknown): IrSchemaRef | null {
|
|
593
|
-
const ref = asRecord(value)
|
|
594
|
-
if (!ref || typeof ref.origin !== 'string' || typeof ref.name !== 'string') return null
|
|
595
|
-
switch (ref.kind) {
|
|
596
|
-
case 'type':
|
|
597
|
-
case 'interface':
|
|
598
|
-
case 'class':
|
|
599
|
-
case 'function':
|
|
600
|
-
case 'policy':
|
|
601
|
-
case 'view':
|
|
602
|
-
case 'core':
|
|
603
|
-
return { origin: ref.origin, kind: ref.kind, name: ref.name }
|
|
604
|
-
default:
|
|
605
|
-
return null
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
function collectDefinitionRefs(root: CanonicalDomainSchemaV1): IrSchemaRef[] {
|
|
610
|
-
const refs: IrSchemaRef[] = []
|
|
611
|
-
for (const [, declaration] of entriesOf(root.types)) {
|
|
612
|
-
collectPathSchemaRefs(declaration, refs)
|
|
613
|
-
}
|
|
614
|
-
for (const [, declaration] of entriesOf(root.interfaces)) {
|
|
615
|
-
refs.push(...collectDefinitionRefsFromDeclaration(declaration))
|
|
616
|
-
}
|
|
387
|
+
function collectClassRefs(root: CanonicalDomainSchemaV1): IrClassRef[] {
|
|
388
|
+
const refs: IrClassRef[] = []
|
|
617
389
|
for (const [, declaration] of entriesOf(root.classes)) {
|
|
618
|
-
refs.push(...
|
|
390
|
+
refs.push(...collectClassRefsFromClass(declaration))
|
|
619
391
|
}
|
|
620
392
|
for (const [, callable] of entriesOf(root.functions)) collectCallableRefs(callable, refs)
|
|
621
393
|
for (const [, view] of entriesOf(root.views)) {
|
|
622
|
-
|
|
623
|
-
refs.push(...refsOf(target?.definitions, ['class', 'interface']))
|
|
394
|
+
refs.push(...refsOf(asRecord(asRecord(view)?.target)?.definitions))
|
|
624
395
|
}
|
|
625
396
|
const core = asRecord(root.core)
|
|
626
|
-
for (const [, node] of entriesOf(core?.nodes))
|
|
397
|
+
for (const [, node] of entriesOf(core?.nodes)) addClassRef(asRecord(node)?.class, refs)
|
|
627
398
|
for (const edge of arrayOf(core?.edges)) {
|
|
628
399
|
const declaration = asRecord(edge)
|
|
629
|
-
|
|
630
|
-
addRef(declaration?.source, refs)
|
|
631
|
-
addRef(declaration?.target, refs)
|
|
400
|
+
addClassRef(declaration?.class, refs)
|
|
632
401
|
}
|
|
633
402
|
for (const [, policy] of entriesOf(root.policies)) collectPolicyRefs(policy, refs)
|
|
634
|
-
return
|
|
403
|
+
return uniqueClassRefs(refs)
|
|
635
404
|
}
|
|
636
405
|
|
|
637
|
-
function
|
|
406
|
+
function collectClassRefsFromClass(value: unknown): IrClassRef[] {
|
|
638
407
|
const declaration = asRecord(value) ?? {}
|
|
639
|
-
const refs = [
|
|
640
|
-
...refsOf(declaration.extends, 'interface'),
|
|
641
|
-
...refsOf(declaration.implements, 'interface'),
|
|
642
|
-
]
|
|
643
|
-
collectPathSchemaRefs(declaration.properties, refs)
|
|
408
|
+
const refs = [...refsOf(declaration.extends)]
|
|
644
409
|
const endpoints = asRecord(declaration.endpoints)
|
|
645
410
|
for (const endpoint of [
|
|
646
411
|
endpoints?.source,
|
|
647
412
|
endpoints?.target,
|
|
648
413
|
...arrayOf(declaration.endpoints),
|
|
649
414
|
]) {
|
|
650
|
-
refs.push(...refsOf(asRecord(endpoint)?.accepts
|
|
415
|
+
refs.push(...refsOf(asRecord(endpoint)?.accepts))
|
|
416
|
+
}
|
|
417
|
+
for (const [, property] of entriesOf(declaration.properties)) {
|
|
418
|
+
collectPathSchemaRefs(asRecord(property)?.schema, refs)
|
|
651
419
|
}
|
|
652
420
|
for (const [, callable] of entriesOf(declaration.methods)) collectCallableRefs(callable, refs)
|
|
653
|
-
for (const [, policy] of entriesOf(declaration.policies))
|
|
654
|
-
return
|
|
421
|
+
for (const [, policy] of entriesOf(declaration.policies)) collectPolicyRefs(policy, refs)
|
|
422
|
+
return uniqueClassRefs(refs)
|
|
655
423
|
}
|
|
656
424
|
|
|
657
|
-
function collectCallableRefs(value: unknown, refs:
|
|
425
|
+
function collectCallableRefs(value: unknown, refs: IrClassRef[]): void {
|
|
658
426
|
const callable = asRecord(value)
|
|
659
|
-
if (
|
|
427
|
+
if (callable === null) return
|
|
660
428
|
collectPathSchemaRefs(callable.input, refs)
|
|
661
429
|
const output = asRecord(callable.output)
|
|
662
430
|
collectPathSchemaRefs(output?.schema ?? output?.item, refs)
|
|
663
431
|
collectPolicyRefs(callable.policy, refs)
|
|
664
432
|
}
|
|
665
433
|
|
|
666
|
-
|
|
667
|
-
function collectPathSchemaRefs(value: unknown, refs: IrSchemaRef[]): void {
|
|
434
|
+
function collectPathSchemaRefs(value: unknown, refs: IrClassRef[]): void {
|
|
668
435
|
if (Array.isArray(value)) {
|
|
669
436
|
for (const item of value) collectPathSchemaRefs(item, refs)
|
|
670
437
|
return
|
|
671
438
|
}
|
|
672
439
|
const object = asRecord(value)
|
|
673
|
-
if (
|
|
440
|
+
if (object === null) return
|
|
674
441
|
const annotation = asRecord(object['x-astrale-path'])
|
|
675
|
-
|
|
442
|
+
refs.push(...refsOf(annotation?.accepts))
|
|
676
443
|
for (const [key, child] of Object.entries(object)) {
|
|
677
444
|
if (key === 'const' || key === 'enum' || key === 'default' || key === 'examples') continue
|
|
678
445
|
collectPathSchemaRefs(child, refs)
|
|
679
446
|
}
|
|
680
447
|
}
|
|
681
448
|
|
|
682
|
-
function collectPolicyRefs(value: unknown, refs:
|
|
449
|
+
function collectPolicyRefs(value: unknown, refs: IrClassRef[]): void {
|
|
683
450
|
if (Array.isArray(value)) {
|
|
684
451
|
for (const item of value) collectPolicyRefs(item, refs)
|
|
685
452
|
return
|
|
686
453
|
}
|
|
687
454
|
const object = asRecord(value)
|
|
688
|
-
if (
|
|
689
|
-
|
|
690
|
-
for (const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
'nodes',
|
|
697
|
-
'source',
|
|
698
|
-
'target',
|
|
699
|
-
'object',
|
|
700
|
-
]) {
|
|
701
|
-
collectPolicyRefs(object[key], refs)
|
|
702
|
-
}
|
|
455
|
+
if (object === null) return
|
|
456
|
+
addClassRef(object.class, refs)
|
|
457
|
+
for (const child of Object.values(object)) collectPolicyRefs(child, refs)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function addClassRef(value: unknown, refs: IrClassRef[]): void {
|
|
461
|
+
const ref = classRef(value)
|
|
462
|
+
if (ref !== null) refs.push(ref)
|
|
703
463
|
}
|
|
704
464
|
|
|
705
|
-
function
|
|
706
|
-
|
|
707
|
-
|
|
465
|
+
function refsOf(value: unknown): IrClassRef[] {
|
|
466
|
+
return arrayOf(value)
|
|
467
|
+
.map(classRef)
|
|
468
|
+
.filter((ref): ref is IrClassRef => ref !== null)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function classRef(value: unknown): IrClassRef | null {
|
|
472
|
+
const ref = definitionRefOf(value)
|
|
473
|
+
return ref !== null && isIrClassRef(ref) ? ref : null
|
|
708
474
|
}
|
|
709
475
|
|
|
710
|
-
function
|
|
476
|
+
function definitionRefOf(value: unknown): IrSchemaRef | null {
|
|
477
|
+
const ref = asRecord(value)
|
|
478
|
+
if (typeof ref?.origin !== 'string' || typeof ref.name !== 'string') return null
|
|
479
|
+
if (
|
|
480
|
+
ref.kind !== 'class' &&
|
|
481
|
+
ref.kind !== 'function' &&
|
|
482
|
+
ref.kind !== 'policy' &&
|
|
483
|
+
ref.kind !== 'view' &&
|
|
484
|
+
ref.kind !== 'core'
|
|
485
|
+
) {
|
|
486
|
+
return null
|
|
487
|
+
}
|
|
488
|
+
return { origin: ref.origin, kind: ref.kind, name: ref.name }
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function uniqueClassRefs(refs: readonly IrClassRef[]): IrClassRef[] {
|
|
711
492
|
const seen = new Set<string>()
|
|
712
493
|
return refs.filter((ref) => {
|
|
713
|
-
const key =
|
|
494
|
+
const key = classRefKey(ref)
|
|
714
495
|
if (seen.has(key)) return false
|
|
715
496
|
seen.add(key)
|
|
716
497
|
return true
|
|
717
498
|
})
|
|
718
499
|
}
|
|
719
500
|
|
|
501
|
+
function callableOutput(value: unknown): IrCallableOutput {
|
|
502
|
+
const output = asRecord(value)
|
|
503
|
+
if (output?.mode === 'stream') return { mode: 'stream', item: studioSchema(output.item) }
|
|
504
|
+
if (output?.mode === 'binary') return { mode: 'binary' }
|
|
505
|
+
return { mode: 'value', schema: studioSchema(output?.schema) }
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function callableAuth(value: unknown): IrCallableAuth | undefined {
|
|
509
|
+
return value === 'anonymous' || value === 'authenticated' || value === 'authorized'
|
|
510
|
+
? value
|
|
511
|
+
: undefined
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function dataDeclaration(
|
|
515
|
+
value: unknown,
|
|
516
|
+
): { mediaType: string; [key: string]: unknown } | undefined {
|
|
517
|
+
const data = asRecord(value)
|
|
518
|
+
return typeof data?.mediaType === 'string'
|
|
519
|
+
? (jsonCopy(data) as { mediaType: string; [key: string]: unknown })
|
|
520
|
+
: undefined
|
|
521
|
+
}
|
|
522
|
+
|
|
720
523
|
function coreEndpoint(value: unknown, rootOrigin: string): string {
|
|
721
524
|
const endpoint = asRecord(value)
|
|
722
|
-
if (endpoint?.kind === 'domain')
|
|
723
|
-
|
|
525
|
+
if (endpoint?.kind === 'domain' && typeof endpoint.origin === 'string') {
|
|
526
|
+
return `/:${endpoint.origin}`
|
|
527
|
+
}
|
|
528
|
+
const ref = definitionRefOf(value)
|
|
724
529
|
return ref?.kind === 'core' ? corePath(ref) : `/:${rootOrigin}`
|
|
725
530
|
}
|
|
726
531
|
|
|
727
|
-
function corePath(ref: Pick<IrSchemaRef, 'origin' | '
|
|
532
|
+
function corePath(ref: Pick<IrSchemaRef, 'origin' | 'name'>): string {
|
|
728
533
|
return `/:${ref.origin}:core.${ref.name}`
|
|
729
534
|
}
|
|
730
535
|
|
|
@@ -735,11 +540,11 @@ function admitClosure(
|
|
|
735
540
|
if (!Array.isArray(value)) return null
|
|
736
541
|
const closure: CanonicalDomainSchemaV1[] = []
|
|
737
542
|
const origins = new Set<string>()
|
|
738
|
-
for (const
|
|
739
|
-
if (!isCanonicalDomainSchemaV1(
|
|
740
|
-
if (origins.has(
|
|
741
|
-
origins.add(
|
|
742
|
-
closure.push(
|
|
543
|
+
for (const candidate of value) {
|
|
544
|
+
if (!isCanonicalDomainSchemaV1(candidate) || candidate.origin === root.origin) return null
|
|
545
|
+
if (origins.has(candidate.origin)) return null
|
|
546
|
+
origins.add(candidate.origin)
|
|
547
|
+
closure.push(candidate)
|
|
743
548
|
}
|
|
744
549
|
return closure
|
|
745
550
|
}
|
|
@@ -750,52 +555,17 @@ function studioSchema(value: unknown): JsonSchema {
|
|
|
750
555
|
return (jsonCopy(asRecord(value) ?? {}) ?? {}) as JsonSchema
|
|
751
556
|
}
|
|
752
557
|
|
|
753
|
-
function
|
|
754
|
-
return Object.fromEntries(entriesOf(value).map(([name, schema]) => [name, studioSchema(schema)]))
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function requiredNames(value: unknown): string[] {
|
|
758
|
-
const object = asRecord(value)
|
|
759
|
-
return arrayOf(object?.required).filter((name): name is string => typeof name === 'string')
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
function legacyRequiredParams(params: Record<string, JsonSchema>): string[] {
|
|
763
|
-
return Object.entries(params).flatMap(([name, schema]) =>
|
|
764
|
-
schemaAllowsNull(schema) ? [] : [name],
|
|
765
|
-
)
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
function schemaAllowsNull(schema: JsonSchema): boolean {
|
|
769
|
-
if (schema.type === 'null') return true
|
|
770
|
-
if (Array.isArray(schema.type) && schema.type.includes('null')) return true
|
|
771
|
-
return [...arrayOf(schema.anyOf), ...arrayOf(schema.oneOf)].some(
|
|
772
|
-
(candidate) => asRecord(candidate)?.type === 'null',
|
|
773
|
-
)
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
function objectInputFromParams(
|
|
777
|
-
params: Record<string, JsonSchema>,
|
|
778
|
-
required: readonly string[],
|
|
779
|
-
): JsonSchema {
|
|
780
|
-
return {
|
|
781
|
-
type: 'object',
|
|
782
|
-
properties: params,
|
|
783
|
-
required: [...required],
|
|
784
|
-
additionalProperties: false,
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
function recordCopy(value: unknown): Record<string, any> {
|
|
558
|
+
function recordCopy(value: unknown): Record<string, unknown> {
|
|
789
559
|
const record = asRecord(value)
|
|
790
|
-
return record ? (jsonCopy(record) as Record<string,
|
|
560
|
+
return record === null ? {} : (jsonCopy(record) as Record<string, unknown>)
|
|
791
561
|
}
|
|
792
562
|
|
|
793
563
|
function jsonCopy(value: unknown): any {
|
|
794
564
|
if (Array.isArray(value)) return value.map(jsonCopy)
|
|
795
565
|
const record = asRecord(value)
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
566
|
+
return record === null
|
|
567
|
+
? value
|
|
568
|
+
: Object.fromEntries(Object.entries(record).map(([key, item]) => [key, jsonCopy(item)]))
|
|
799
569
|
}
|
|
800
570
|
|
|
801
571
|
function entriesOf(value: unknown): [string, unknown][] {
|