@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,21 +1,18 @@
|
|
|
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
|
-
|
|
12
|
-
|
|
10
|
+
IrClassKey,
|
|
11
|
+
IrClassRef,
|
|
13
12
|
IrEndpoint,
|
|
14
13
|
IrFunction,
|
|
15
14
|
IrImportDescriptor,
|
|
16
|
-
IrInterface,
|
|
17
15
|
IrMethod,
|
|
18
|
-
IrSchemaKey,
|
|
19
16
|
IrSchemaRef,
|
|
20
17
|
IrView,
|
|
21
18
|
JsonSchema,
|
|
@@ -23,6 +20,9 @@ import type {
|
|
|
23
20
|
StudioCore,
|
|
24
21
|
} from '../../shared/types'
|
|
25
22
|
|
|
23
|
+
import { classRefKey, isIrClassRef, schemaRefKey } from '../../shared/schema/identity'
|
|
24
|
+
import { isSchemaRevision } from '../../shared/types'
|
|
25
|
+
|
|
26
26
|
type AnyRecord = Record<string, unknown>
|
|
27
27
|
|
|
28
28
|
export type CanonicalDomainSchemaV1 = AnyRecord & {
|
|
@@ -32,10 +32,23 @@ export type CanonicalDomainSchemaV1 = AnyRecord & {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export interface CanonicalSchemaProjection {
|
|
35
|
-
ir: SchemaIR
|
|
36
|
-
importedInterfaces: Record<string, IrInterface>
|
|
35
|
+
readonly ir: SchemaIR
|
|
37
36
|
}
|
|
38
37
|
|
|
38
|
+
export type CanonicalSchemaAdmission =
|
|
39
|
+
| {
|
|
40
|
+
readonly status: 'admitted'
|
|
41
|
+
readonly root: CanonicalDomainSchemaV1
|
|
42
|
+
readonly closure: CanonicalDomainSchemaV1[]
|
|
43
|
+
readonly revision: `sha256:${string}`
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
readonly status: 'preview'
|
|
47
|
+
readonly root: CanonicalDomainSchemaV1
|
|
48
|
+
readonly closure: CanonicalDomainSchemaV1[]
|
|
49
|
+
readonly revision: null
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
export function isCanonicalDomainSchemaV1(value: unknown): value is CanonicalDomainSchemaV1 {
|
|
40
53
|
const candidate = asRecord(value)
|
|
41
54
|
return (
|
|
@@ -52,89 +65,78 @@ export function findCanonicalDomainSchemaExport(
|
|
|
52
65
|
): CanonicalDomainSchemaV1 | null {
|
|
53
66
|
if (isCanonicalDomainSchemaV1(moduleExports.schema)) return moduleExports.schema
|
|
54
67
|
if (isCanonicalDomainSchemaV1(moduleExports.default)) return moduleExports.default
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
)
|
|
62
75
|
}
|
|
63
76
|
|
|
64
|
-
/**
|
|
65
|
-
* Read the exact retained dependency closure through the domain's own SDK.
|
|
66
|
-
* Calls are optional and fail closed: an unrecognised/substituted result is
|
|
67
|
-
* discarded rather than trusted as imported-schema evidence.
|
|
68
|
-
*/
|
|
77
|
+
/** Read the exact retained dependency closure through the authored SDK. */
|
|
69
78
|
export function closureFromSdk(
|
|
70
79
|
sdkModule: Record<string, unknown>,
|
|
71
80
|
root: CanonicalDomainSchemaV1,
|
|
72
81
|
): CanonicalDomainSchemaV1[] {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (closure) return closure
|
|
82
|
-
}
|
|
83
|
-
} catch {
|
|
84
|
-
// A structurally V1-looking export may lack retained SDK admission context.
|
|
85
|
-
}
|
|
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 []
|
|
86
90
|
}
|
|
91
|
+
}
|
|
87
92
|
|
|
93
|
+
/** Re-admit one portable root and prove its revision through the authored SDK. */
|
|
94
|
+
export function admitCanonicalSchemaFromSdk(
|
|
95
|
+
sdkModule: Record<string, unknown>,
|
|
96
|
+
candidate: CanonicalDomainSchemaV1,
|
|
97
|
+
): CanonicalSchemaAdmission {
|
|
98
|
+
const retainedClosure = closureFromSdk(sdkModule, candidate)
|
|
88
99
|
const schemaApi = asRecord(sdkModule.schema)
|
|
100
|
+
const accept = schemaApi?.accept
|
|
101
|
+
const revision = schemaApi?.revision
|
|
89
102
|
const resolve = schemaApi?.resolve
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (closure) return closure
|
|
97
|
-
}
|
|
98
|
-
} catch {
|
|
99
|
-
// Missing retained context is a valid degradation: local schema still renders.
|
|
100
|
-
}
|
|
103
|
+
if (
|
|
104
|
+
typeof accept !== 'function' ||
|
|
105
|
+
typeof revision !== 'function' ||
|
|
106
|
+
typeof resolve !== 'function'
|
|
107
|
+
) {
|
|
108
|
+
return { status: 'preview', root: candidate, closure: retainedClosure, revision: null }
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
: requiredNames(declaration.input)
|
|
120
|
-
functions[name] = {
|
|
121
|
-
name,
|
|
122
|
-
input: studioSchema(declaration.input ?? objectInputFromParams(params, requiredParams)),
|
|
123
|
-
params,
|
|
124
|
-
requiredParams,
|
|
125
|
-
output: callableOutput(declaration.output, returns),
|
|
126
|
-
returns,
|
|
127
|
-
static: true,
|
|
128
|
-
inheritance: 'default',
|
|
129
|
-
...(callableAuth(declaration.auth) ? { auth: callableAuth(declaration.auth) } : {}),
|
|
130
|
-
...(typeof declaration.description === 'string'
|
|
131
|
-
? { description: declaration.description }
|
|
132
|
-
: {}),
|
|
133
|
-
...(declaration.policy === undefined ? {} : { policy: jsonCopy(declaration.policy) }),
|
|
111
|
+
try {
|
|
112
|
+
const revisions = new Map<string, string>()
|
|
113
|
+
for (const dependency of retainedClosure) {
|
|
114
|
+
const value = Reflect.apply(revision, schemaApi, [dependency])
|
|
115
|
+
if (!isSchemaRevision(value)) throw new TypeError('SDK returned an invalid revision.')
|
|
116
|
+
revisions.set(dependency.origin, value)
|
|
117
|
+
}
|
|
118
|
+
const lookup = {
|
|
119
|
+
get(origin: string, expected: string): CanonicalDomainSchemaV1 | undefined {
|
|
120
|
+
const dependency = retainedClosure.find((entry) => entry.origin === origin)
|
|
121
|
+
return dependency && revisions.get(origin) === expected ? dependency : undefined
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
const accepted = Reflect.apply(accept, schemaApi, [candidate, lookup])
|
|
125
|
+
if (!isCanonicalDomainSchemaV1(accepted) || accepted.origin !== candidate.origin) {
|
|
126
|
+
throw new TypeError('SDK admission returned another root.')
|
|
134
127
|
}
|
|
128
|
+
const acceptedRevision = Reflect.apply(revision, schemaApi, [accepted])
|
|
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.')
|
|
134
|
+
}
|
|
135
|
+
const closure = closureFromSdk(sdkModule, accepted)
|
|
136
|
+
return { status: 'admitted', root: accepted, closure, revision: acceptedRevision }
|
|
137
|
+
} catch {
|
|
138
|
+
return { status: 'preview', root: candidate, closure: retainedClosure, revision: null }
|
|
135
139
|
}
|
|
136
|
-
|
|
137
|
-
return { ...(ir as unknown as SchemaIR), functions }
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
export function projectCanonicalSchema(
|
|
@@ -142,12 +144,6 @@ export function projectCanonicalSchema(
|
|
|
142
144
|
closure: readonly CanonicalDomainSchemaV1[] = [],
|
|
143
145
|
): CanonicalSchemaProjection {
|
|
144
146
|
const origin = root.origin
|
|
145
|
-
const interfaces = Object.fromEntries(
|
|
146
|
-
entriesOf(root.interfaces).map(([name, declaration]) => [
|
|
147
|
-
name,
|
|
148
|
-
projectInterface(origin, name, declaration),
|
|
149
|
-
]),
|
|
150
|
-
)
|
|
151
147
|
const classes = Object.fromEntries(
|
|
152
148
|
entriesOf(root.classes).map(([name, declaration]) => [
|
|
153
149
|
name,
|
|
@@ -163,193 +159,138 @@ export function projectCanonicalSchema(
|
|
|
163
159
|
const views = Object.fromEntries(
|
|
164
160
|
entriesOf(root.views).map(([name, declaration]) => [name, projectView(name, declaration)]),
|
|
165
161
|
)
|
|
166
|
-
|
|
167
|
-
const importsByKey: Record<IrDefinitionKey, IrImportDescriptor> = {}
|
|
168
|
-
const importedInterfacesByKey: Record<IrDefinitionKey, IrInterface> = {}
|
|
169
162
|
const schemas = new Map(
|
|
170
|
-
closure.filter(isCanonicalDomainSchemaV1).map((
|
|
163
|
+
closure.filter(isCanonicalDomainSchemaV1).map((value) => [value.origin, value]),
|
|
171
164
|
)
|
|
172
|
-
const
|
|
165
|
+
const importsByKey = {} as Record<IrClassKey, IrImportDescriptor>
|
|
166
|
+
const importedClassesByKey = {} as Record<IrClassKey, IrClass>
|
|
167
|
+
const pending = collectClassRefs(root)
|
|
173
168
|
const visited = new Set<string>()
|
|
174
169
|
|
|
175
|
-
for (let index = 0; index < pending.length; index
|
|
170
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
176
171
|
const ref = pending[index]
|
|
177
|
-
if (
|
|
178
|
-
const key =
|
|
172
|
+
if (ref.origin === origin) continue
|
|
173
|
+
const key = classRefKey(ref)
|
|
179
174
|
if (visited.has(key)) continue
|
|
180
175
|
visited.add(key)
|
|
181
|
-
|
|
182
|
-
importsByKey[key] = {
|
|
183
|
-
origin: ref.origin,
|
|
184
|
-
definition: ref.kind,
|
|
185
|
-
ref,
|
|
186
|
-
key,
|
|
187
|
-
}
|
|
188
|
-
if (ref.kind !== 'interface' || key in importedInterfacesByKey) continue
|
|
189
|
-
|
|
176
|
+
importsByKey[key] = { origin: ref.origin, ref, key }
|
|
190
177
|
const owner = schemas.get(ref.origin)
|
|
191
|
-
const declaration = asRecord(asRecord(owner?.
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
pending.push(...collectDefinitionRefsFromDeclaration(declaration))
|
|
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))
|
|
196
182
|
}
|
|
197
183
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (name) nameCounts.set(name, (nameCounts.get(name) ?? 0) + 1)
|
|
205
|
-
}
|
|
206
|
-
const localNames = new Set([...Object.keys(interfaces), ...Object.keys(classes)])
|
|
207
|
-
const isUnambiguous = (name: string): boolean =>
|
|
208
|
-
nameCounts.get(name) === 1 && !localNames.has(name)
|
|
209
|
-
const imports = Object.fromEntries(
|
|
210
|
-
Object.values(importsByKey).flatMap((descriptor) => {
|
|
211
|
-
const name = descriptor.ref?.name
|
|
212
|
-
return name && isUnambiguous(name) ? [[name, descriptor]] : []
|
|
213
|
-
}),
|
|
214
|
-
)
|
|
215
|
-
const importedInterfaces = Object.fromEntries(
|
|
216
|
-
Object.values(importedInterfacesByKey).flatMap((declaration) =>
|
|
217
|
-
isUnambiguous(declaration.name) ? [[declaration.name, declaration]] : [],
|
|
218
|
-
),
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
const dependencies = arrayOf(root.dependencies)
|
|
222
|
-
.map(asRecord)
|
|
223
|
-
.filter((dependency): dependency is AnyRecord => dependency !== null)
|
|
224
|
-
.flatMap((dependency) =>
|
|
225
|
-
typeof dependency.origin === 'string' && typeof dependency.revision === 'string'
|
|
226
|
-
? [{ origin: dependency.origin, revision: dependency.revision }]
|
|
227
|
-
: [],
|
|
228
|
-
)
|
|
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
|
+
})
|
|
229
190
|
|
|
230
191
|
return {
|
|
231
192
|
ir: {
|
|
232
193
|
format: 'astrale.dsl',
|
|
233
194
|
version: 'v1',
|
|
234
195
|
domain: origin,
|
|
235
|
-
types: schemaRecord(root.types),
|
|
236
|
-
interfaces,
|
|
237
196
|
classes,
|
|
238
|
-
imports,
|
|
239
197
|
importsByKey,
|
|
240
|
-
|
|
198
|
+
importedClassesByKey,
|
|
241
199
|
functions,
|
|
242
200
|
views,
|
|
243
201
|
policies: recordCopy(root.policies),
|
|
244
202
|
dependencies,
|
|
245
203
|
core: jsonCopy(root.core),
|
|
246
204
|
},
|
|
247
|
-
importedInterfaces,
|
|
248
205
|
}
|
|
249
206
|
}
|
|
250
207
|
|
|
251
|
-
/** Project canonical
|
|
208
|
+
/** Project canonical Core data without importing Application or Runtime modules. */
|
|
252
209
|
export function projectCanonicalCore(
|
|
253
210
|
root: CanonicalDomainSchemaV1,
|
|
254
211
|
): Pick<StudioCore, 'domain' | 'nodes' | 'edges'> {
|
|
255
212
|
const core = asRecord(root.core) ?? {}
|
|
256
213
|
const nodes = entriesOf(core.nodes).map(([slug, raw]) => {
|
|
257
214
|
const node = asRecord(raw) ?? {}
|
|
258
|
-
const
|
|
215
|
+
const selectedClass = classRef(node.class)
|
|
259
216
|
return {
|
|
260
|
-
path: corePath({ origin: root.origin,
|
|
261
|
-
className:
|
|
217
|
+
path: corePath({ origin: root.origin, name: slug }),
|
|
218
|
+
className: selectedClass?.name ?? '?',
|
|
262
219
|
data: recordCopy(node.properties),
|
|
263
220
|
}
|
|
264
221
|
})
|
|
265
222
|
const edges = arrayOf(core.edges).map((raw) => {
|
|
266
223
|
const edge = asRecord(raw) ?? {}
|
|
267
|
-
const classRef = schemaRef(edge.class)
|
|
268
224
|
return {
|
|
269
225
|
from: coreEndpoint(edge.source, root.origin),
|
|
270
226
|
to: coreEndpoint(edge.target, root.origin),
|
|
271
|
-
edgeName: classRef?.name ?? '?',
|
|
227
|
+
edgeName: classRef(edge.class)?.name ?? '?',
|
|
272
228
|
...(edge.properties === undefined ? {} : { data: recordCopy(edge.properties) }),
|
|
273
229
|
}
|
|
274
230
|
})
|
|
275
231
|
return { domain: root.origin, nodes, edges }
|
|
276
232
|
}
|
|
277
233
|
|
|
278
|
-
function projectInterface(origin: string, name: string, value: unknown): IrInterface {
|
|
279
|
-
const declaration = asRecord(value) ?? {}
|
|
280
|
-
const family = definitionFamily(declaration.family, 'both')
|
|
281
|
-
const extendsRefs = refsOf(declaration.extends, 'interface')
|
|
282
|
-
return {
|
|
283
|
-
type: 'interface',
|
|
284
|
-
name,
|
|
285
|
-
origin,
|
|
286
|
-
family,
|
|
287
|
-
ref: { origin, kind: 'interface', name },
|
|
288
|
-
extends: extendsRefs.map((ref) => ref.name),
|
|
289
|
-
extendsRefs,
|
|
290
|
-
properties: propertySchemas(declaration),
|
|
291
|
-
required: requiredNames(declaration.properties),
|
|
292
|
-
methods: projectMethods(declaration.methods),
|
|
293
|
-
...(family === 'edge' ? edgeFields(declaration) : {}),
|
|
294
|
-
...(typeof declaration.description === 'string'
|
|
295
|
-
? { description: declaration.description }
|
|
296
|
-
: {}),
|
|
297
|
-
...(asRecord(declaration.propertyMetadata)
|
|
298
|
-
? { propertyMetadata: recordCopy(declaration.propertyMetadata) }
|
|
299
|
-
: {}),
|
|
300
|
-
...(dataDeclaration(declaration.data) ? { data: dataDeclaration(declaration.data) } : {}),
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
234
|
function projectClass(origin: string, name: string, value: unknown): IrClass {
|
|
305
235
|
const declaration = asRecord(value) ?? {}
|
|
306
|
-
const
|
|
307
|
-
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
|
+
)
|
|
308
255
|
const policies = Object.fromEntries(
|
|
309
256
|
entriesOf(declaration.policies).flatMap(([policyName, raw]) => {
|
|
310
|
-
const ref =
|
|
257
|
+
const ref = definitionRefOf(raw)
|
|
311
258
|
return ref ? [[policyName, ref]] : []
|
|
312
259
|
}),
|
|
313
260
|
)
|
|
314
261
|
return {
|
|
315
|
-
type:
|
|
262
|
+
type: kind,
|
|
316
263
|
name,
|
|
317
264
|
origin,
|
|
318
265
|
ref: { origin, kind: 'class', name },
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
properties
|
|
322
|
-
required
|
|
266
|
+
extends: extendsRefs.map((ref) => ref.name),
|
|
267
|
+
extendsRefs,
|
|
268
|
+
properties,
|
|
269
|
+
required,
|
|
323
270
|
methods: projectMethods(declaration.methods),
|
|
324
|
-
...(
|
|
325
|
-
...(typeof declaration.icon === 'string' ? { icon: declaration.icon } : {}),
|
|
271
|
+
...(kind === 'edge' ? edgeFields(declaration) : {}),
|
|
326
272
|
...(typeof declaration.description === 'string'
|
|
327
273
|
? { description: declaration.description }
|
|
328
274
|
: {}),
|
|
329
|
-
...(
|
|
330
|
-
? { propertyMetadata: recordCopy(declaration.propertyMetadata) }
|
|
331
|
-
: {}),
|
|
275
|
+
...(Object.keys(propertyMetadata).length === 0 ? {} : { propertyMetadata }),
|
|
332
276
|
...(dataDeclaration(declaration.data) ? { data: dataDeclaration(declaration.data) } : {}),
|
|
333
|
-
...(Object.keys(policies).length
|
|
277
|
+
...(Object.keys(policies).length === 0 ? {} : { policies }),
|
|
334
278
|
}
|
|
335
279
|
}
|
|
336
280
|
|
|
337
281
|
function projectMethods(value: unknown): Record<string, IrMethod> {
|
|
338
282
|
return Object.fromEntries(
|
|
339
|
-
entriesOf(value).map(([name,
|
|
340
|
-
const
|
|
341
|
-
const raw = asRecord(declaration) ?? {}
|
|
283
|
+
entriesOf(value).map(([name, raw]) => {
|
|
284
|
+
const declaration = asRecord(raw) ?? {}
|
|
342
285
|
const inheritance =
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
raw.inheritance === 'default'
|
|
346
|
-
? raw.inheritance
|
|
286
|
+
declaration.inheritance === 'abstract' || declaration.inheritance === 'sealed'
|
|
287
|
+
? declaration.inheritance
|
|
347
288
|
: 'default'
|
|
348
289
|
return [
|
|
349
290
|
name,
|
|
350
291
|
{
|
|
351
|
-
...
|
|
352
|
-
static:
|
|
292
|
+
...projectCallable(name, declaration),
|
|
293
|
+
static: declaration.static === true,
|
|
353
294
|
inheritance,
|
|
354
295
|
} satisfies IrMethod,
|
|
355
296
|
]
|
|
@@ -357,50 +298,40 @@ function projectMethods(value: unknown): Record<string, IrMethod> {
|
|
|
357
298
|
)
|
|
358
299
|
}
|
|
359
300
|
|
|
360
|
-
function projectFunction(name: string,
|
|
361
|
-
return
|
|
362
|
-
...projectCallable(name, declaration),
|
|
363
|
-
static: true,
|
|
364
|
-
inheritance: 'default',
|
|
365
|
-
}
|
|
301
|
+
function projectFunction(name: string, value: unknown): IrFunction {
|
|
302
|
+
return projectCallable(name, value)
|
|
366
303
|
}
|
|
367
304
|
|
|
368
|
-
function projectCallable(name: string, value: unknown):
|
|
305
|
+
function projectCallable(name: string, value: unknown): IrFunction {
|
|
369
306
|
const declaration = asRecord(value) ?? {}
|
|
370
307
|
const input = studioSchema(declaration.input)
|
|
371
|
-
const output = callableOutput(declaration.output
|
|
308
|
+
const output = callableOutput(declaration.output)
|
|
372
309
|
const auth = callableAuth(declaration.auth)
|
|
373
310
|
return {
|
|
374
311
|
name,
|
|
375
312
|
input,
|
|
376
|
-
params: callableParams(declaration.input),
|
|
377
|
-
requiredParams: requiredNames(declaration.input),
|
|
378
313
|
output,
|
|
379
|
-
returns: outputValue(output),
|
|
380
314
|
...(typeof declaration.description === 'string'
|
|
381
315
|
? { description: declaration.description }
|
|
382
316
|
: {}),
|
|
383
|
-
...(auth ? {
|
|
317
|
+
...(auth === undefined ? {} : { auth }),
|
|
384
318
|
...(declaration.policy === undefined ? {} : { policy: jsonCopy(declaration.policy) }),
|
|
385
319
|
}
|
|
386
320
|
}
|
|
387
321
|
|
|
388
322
|
function projectView(name: string, value: unknown): IrView {
|
|
389
323
|
const declaration = asRecord(value) ?? {}
|
|
390
|
-
const
|
|
391
|
-
const target =
|
|
392
|
-
rawTarget?.kind === 'definition'
|
|
393
|
-
? ({
|
|
394
|
-
kind: 'definition',
|
|
395
|
-
definitions: refsOf(rawTarget.definitions, ['class', 'interface']),
|
|
396
|
-
} as const)
|
|
397
|
-
: ({ kind: 'domain' } as const)
|
|
398
|
-
const auth =
|
|
399
|
-
declaration.auth === 'optional' || declaration.auth === 'public' ? declaration.auth : 'required'
|
|
324
|
+
const target = asRecord(declaration.target)
|
|
400
325
|
return {
|
|
401
326
|
name,
|
|
402
|
-
target
|
|
403
|
-
|
|
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',
|
|
404
335
|
...(typeof declaration.description === 'string'
|
|
405
336
|
? { description: declaration.description }
|
|
406
337
|
: {}),
|
|
@@ -419,250 +350,186 @@ function edgeFields(
|
|
|
419
350
|
projectEndpoint(endpoints?.source, 'outgoing'),
|
|
420
351
|
projectEndpoint(endpoints?.target, 'incoming'),
|
|
421
352
|
]
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
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
|
+
},
|
|
426
361
|
}
|
|
427
|
-
return { endpoints: projected, orientation, constraints }
|
|
428
362
|
}
|
|
429
363
|
|
|
430
364
|
function projectEndpoint(
|
|
431
365
|
value: unknown,
|
|
432
|
-
|
|
366
|
+
cardinality: 'outgoing' | 'incoming' | 'incident',
|
|
433
367
|
): IrEndpoint {
|
|
434
368
|
const endpoint = asRecord(value) ?? {}
|
|
435
|
-
const refs = refsOf(endpoint.accepts
|
|
436
|
-
const
|
|
369
|
+
const refs = refsOf(endpoint.accepts)
|
|
370
|
+
const count = edgeCount(endpoint[cardinality])
|
|
437
371
|
return {
|
|
438
372
|
name: typeof endpoint.role === 'string' ? endpoint.role : '',
|
|
439
373
|
types: refs.map((ref) => ref.name),
|
|
440
374
|
refs,
|
|
441
|
-
...(
|
|
375
|
+
...(count === undefined ? {} : { cardinality: count }),
|
|
442
376
|
}
|
|
443
377
|
}
|
|
444
378
|
|
|
445
379
|
function edgeCount(value: unknown): IrEndpoint['cardinality'] | undefined {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
case '0..1':
|
|
452
|
-
return { min: 0, max: 1 }
|
|
453
|
-
case '1':
|
|
454
|
-
return { min: 1, max: 1 }
|
|
455
|
-
default:
|
|
456
|
-
return undefined
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function propertySchemas(declaration: AnyRecord): Record<string, JsonSchema> {
|
|
461
|
-
const object = asRecord(declaration.properties) ?? {}
|
|
462
|
-
return Object.fromEntries(
|
|
463
|
-
entriesOf(object.properties).map(([name, schema]) => [name, studioSchema(schema)]),
|
|
464
|
-
)
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
function callableParams(input: unknown): Record<string, JsonSchema> {
|
|
468
|
-
const object = asRecord(input)
|
|
469
|
-
if (!object) return {}
|
|
470
|
-
return Object.fromEntries(
|
|
471
|
-
entriesOf(object.properties).map(([name, schema]) => [name, studioSchema(schema)]),
|
|
472
|
-
)
|
|
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
|
|
473
385
|
}
|
|
474
386
|
|
|
475
|
-
function
|
|
476
|
-
const
|
|
477
|
-
if (output?.mode === 'stream') return { mode: 'stream', item: studioSchema(output.item) }
|
|
478
|
-
if (output?.mode === 'binary') return { mode: 'binary' }
|
|
479
|
-
if (output?.mode === 'value') return { mode: 'value', schema: studioSchema(output.schema) }
|
|
480
|
-
return { mode: 'value', schema: fallback }
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function outputValue(output: IrCallableOutput): JsonSchema {
|
|
484
|
-
if (output.mode === 'value') return output.schema
|
|
485
|
-
if (output.mode === 'stream') return output.item
|
|
486
|
-
return {}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function callableAuth(value: unknown): IrCallableAuth | undefined {
|
|
490
|
-
return value === 'anonymous' || value === 'authenticated' || value === 'authorized'
|
|
491
|
-
? value
|
|
492
|
-
: undefined
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function definitionFamily(value: unknown, fallback: 'node' | 'both'): 'node' | 'edge' | 'both' {
|
|
496
|
-
return value === 'node' || value === 'edge' || value === 'both' ? value : fallback
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function dataDeclaration(
|
|
500
|
-
value: unknown,
|
|
501
|
-
): { mediaType: string; [key: string]: unknown } | undefined {
|
|
502
|
-
const data = asRecord(value)
|
|
503
|
-
return typeof data?.mediaType === 'string'
|
|
504
|
-
? (jsonCopy(data) as { mediaType: string; [key: string]: unknown })
|
|
505
|
-
: undefined
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function refsOf(
|
|
509
|
-
value: unknown,
|
|
510
|
-
kinds: IrSchemaRef['kind'] | readonly IrSchemaRef['kind'][],
|
|
511
|
-
): IrSchemaRef[] {
|
|
512
|
-
const accepted = new Set(Array.isArray(kinds) ? kinds : [kinds])
|
|
513
|
-
return arrayOf(value)
|
|
514
|
-
.map(schemaRef)
|
|
515
|
-
.filter((ref): ref is IrSchemaRef => ref !== null && accepted.has(ref.kind))
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function schemaRef(value: unknown): IrSchemaRef | null {
|
|
519
|
-
const ref = asRecord(value)
|
|
520
|
-
if (!ref || typeof ref.origin !== 'string' || typeof ref.name !== 'string') return null
|
|
521
|
-
switch (ref.kind) {
|
|
522
|
-
case 'type':
|
|
523
|
-
case 'interface':
|
|
524
|
-
case 'class':
|
|
525
|
-
case 'function':
|
|
526
|
-
case 'policy':
|
|
527
|
-
case 'view':
|
|
528
|
-
case 'core':
|
|
529
|
-
return { origin: ref.origin, kind: ref.kind, name: ref.name }
|
|
530
|
-
default:
|
|
531
|
-
return null
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function collectDefinitionRefs(root: CanonicalDomainSchemaV1): IrSchemaRef[] {
|
|
536
|
-
const refs: IrSchemaRef[] = []
|
|
537
|
-
for (const [, declaration] of entriesOf(root.types)) {
|
|
538
|
-
collectPathSchemaRefs(declaration, refs)
|
|
539
|
-
}
|
|
540
|
-
for (const [, declaration] of entriesOf(root.interfaces)) {
|
|
541
|
-
refs.push(...collectDefinitionRefsFromDeclaration(declaration))
|
|
542
|
-
}
|
|
387
|
+
function collectClassRefs(root: CanonicalDomainSchemaV1): IrClassRef[] {
|
|
388
|
+
const refs: IrClassRef[] = []
|
|
543
389
|
for (const [, declaration] of entriesOf(root.classes)) {
|
|
544
|
-
refs.push(...
|
|
390
|
+
refs.push(...collectClassRefsFromClass(declaration))
|
|
545
391
|
}
|
|
546
392
|
for (const [, callable] of entriesOf(root.functions)) collectCallableRefs(callable, refs)
|
|
547
393
|
for (const [, view] of entriesOf(root.views)) {
|
|
548
|
-
|
|
549
|
-
refs.push(...refsOf(target?.definitions, ['class', 'interface']))
|
|
394
|
+
refs.push(...refsOf(asRecord(asRecord(view)?.target)?.definitions))
|
|
550
395
|
}
|
|
551
396
|
const core = asRecord(root.core)
|
|
552
|
-
for (const [, node] of entriesOf(core?.nodes))
|
|
397
|
+
for (const [, node] of entriesOf(core?.nodes)) addClassRef(asRecord(node)?.class, refs)
|
|
553
398
|
for (const edge of arrayOf(core?.edges)) {
|
|
554
399
|
const declaration = asRecord(edge)
|
|
555
|
-
|
|
556
|
-
addRef(declaration?.source, refs)
|
|
557
|
-
addRef(declaration?.target, refs)
|
|
400
|
+
addClassRef(declaration?.class, refs)
|
|
558
401
|
}
|
|
559
|
-
|
|
560
|
-
return
|
|
402
|
+
for (const [, policy] of entriesOf(root.policies)) collectPolicyRefs(policy, refs)
|
|
403
|
+
return uniqueClassRefs(refs)
|
|
561
404
|
}
|
|
562
405
|
|
|
563
|
-
function
|
|
406
|
+
function collectClassRefsFromClass(value: unknown): IrClassRef[] {
|
|
564
407
|
const declaration = asRecord(value) ?? {}
|
|
565
|
-
const refs = [
|
|
566
|
-
...refsOf(declaration.extends, 'interface'),
|
|
567
|
-
...refsOf(declaration.implements, 'interface'),
|
|
568
|
-
]
|
|
569
|
-
collectPathSchemaRefs(declaration.properties, refs)
|
|
408
|
+
const refs = [...refsOf(declaration.extends)]
|
|
570
409
|
const endpoints = asRecord(declaration.endpoints)
|
|
571
410
|
for (const endpoint of [
|
|
572
411
|
endpoints?.source,
|
|
573
412
|
endpoints?.target,
|
|
574
413
|
...arrayOf(declaration.endpoints),
|
|
575
414
|
]) {
|
|
576
|
-
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)
|
|
577
419
|
}
|
|
578
420
|
for (const [, callable] of entriesOf(declaration.methods)) collectCallableRefs(callable, refs)
|
|
579
|
-
for (const [, policy] of entriesOf(declaration.policies))
|
|
580
|
-
return
|
|
421
|
+
for (const [, policy] of entriesOf(declaration.policies)) collectPolicyRefs(policy, refs)
|
|
422
|
+
return uniqueClassRefs(refs)
|
|
581
423
|
}
|
|
582
424
|
|
|
583
|
-
function collectCallableRefs(value: unknown, refs:
|
|
425
|
+
function collectCallableRefs(value: unknown, refs: IrClassRef[]): void {
|
|
584
426
|
const callable = asRecord(value)
|
|
585
|
-
if (
|
|
427
|
+
if (callable === null) return
|
|
586
428
|
collectPathSchemaRefs(callable.input, refs)
|
|
587
429
|
const output = asRecord(callable.output)
|
|
588
430
|
collectPathSchemaRefs(output?.schema ?? output?.item, refs)
|
|
589
431
|
collectPolicyRefs(callable.policy, refs)
|
|
590
432
|
}
|
|
591
433
|
|
|
592
|
-
|
|
593
|
-
function collectPathSchemaRefs(value: unknown, refs: IrSchemaRef[]): void {
|
|
434
|
+
function collectPathSchemaRefs(value: unknown, refs: IrClassRef[]): void {
|
|
594
435
|
if (Array.isArray(value)) {
|
|
595
436
|
for (const item of value) collectPathSchemaRefs(item, refs)
|
|
596
437
|
return
|
|
597
438
|
}
|
|
598
439
|
const object = asRecord(value)
|
|
599
|
-
if (
|
|
440
|
+
if (object === null) return
|
|
600
441
|
const annotation = asRecord(object['x-astrale-path'])
|
|
601
|
-
|
|
442
|
+
refs.push(...refsOf(annotation?.accepts))
|
|
602
443
|
for (const [key, child] of Object.entries(object)) {
|
|
603
444
|
if (key === 'const' || key === 'enum' || key === 'default' || key === 'examples') continue
|
|
604
445
|
collectPathSchemaRefs(child, refs)
|
|
605
446
|
}
|
|
606
447
|
}
|
|
607
448
|
|
|
608
|
-
function collectPolicyRefs(value: unknown, refs:
|
|
449
|
+
function collectPolicyRefs(value: unknown, refs: IrClassRef[]): void {
|
|
609
450
|
if (Array.isArray(value)) {
|
|
610
451
|
for (const item of value) collectPolicyRefs(item, refs)
|
|
611
452
|
return
|
|
612
453
|
}
|
|
613
454
|
const object = asRecord(value)
|
|
614
|
-
if (
|
|
615
|
-
|
|
616
|
-
for (const
|
|
617
|
-
'allOf',
|
|
618
|
-
'anyOf',
|
|
619
|
-
'exists',
|
|
620
|
-
'where',
|
|
621
|
-
'match',
|
|
622
|
-
'nodes',
|
|
623
|
-
'source',
|
|
624
|
-
'target',
|
|
625
|
-
'object',
|
|
626
|
-
]) {
|
|
627
|
-
collectPolicyRefs(object[key], refs)
|
|
628
|
-
}
|
|
455
|
+
if (object === null) return
|
|
456
|
+
addClassRef(object.class, refs)
|
|
457
|
+
for (const child of Object.values(object)) collectPolicyRefs(child, refs)
|
|
629
458
|
}
|
|
630
459
|
|
|
631
|
-
function
|
|
632
|
-
const ref =
|
|
633
|
-
if (ref) refs.push(ref)
|
|
460
|
+
function addClassRef(value: unknown, refs: IrClassRef[]): void {
|
|
461
|
+
const ref = classRef(value)
|
|
462
|
+
if (ref !== null) refs.push(ref)
|
|
634
463
|
}
|
|
635
464
|
|
|
636
|
-
function
|
|
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
|
|
474
|
+
}
|
|
475
|
+
|
|
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[] {
|
|
637
492
|
const seen = new Set<string>()
|
|
638
493
|
return refs.filter((ref) => {
|
|
639
|
-
const key =
|
|
494
|
+
const key = classRefKey(ref)
|
|
640
495
|
if (seen.has(key)) return false
|
|
641
496
|
seen.add(key)
|
|
642
497
|
return true
|
|
643
498
|
})
|
|
644
499
|
}
|
|
645
500
|
|
|
646
|
-
function
|
|
647
|
-
|
|
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) }
|
|
648
506
|
}
|
|
649
507
|
|
|
650
|
-
function
|
|
651
|
-
return
|
|
508
|
+
function callableAuth(value: unknown): IrCallableAuth | undefined {
|
|
509
|
+
return value === 'anonymous' || value === 'authenticated' || value === 'authorized'
|
|
510
|
+
? value
|
|
511
|
+
: undefined
|
|
652
512
|
}
|
|
653
513
|
|
|
654
|
-
function
|
|
655
|
-
|
|
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
|
|
656
521
|
}
|
|
657
522
|
|
|
658
523
|
function coreEndpoint(value: unknown, rootOrigin: string): string {
|
|
659
524
|
const endpoint = asRecord(value)
|
|
660
|
-
if (endpoint?.kind === 'domain')
|
|
661
|
-
|
|
525
|
+
if (endpoint?.kind === 'domain' && typeof endpoint.origin === 'string') {
|
|
526
|
+
return `/:${endpoint.origin}`
|
|
527
|
+
}
|
|
528
|
+
const ref = definitionRefOf(value)
|
|
662
529
|
return ref?.kind === 'core' ? corePath(ref) : `/:${rootOrigin}`
|
|
663
530
|
}
|
|
664
531
|
|
|
665
|
-
function corePath(ref: Pick<IrSchemaRef, 'origin' | '
|
|
532
|
+
function corePath(ref: Pick<IrSchemaRef, 'origin' | 'name'>): string {
|
|
666
533
|
return `/:${ref.origin}:core.${ref.name}`
|
|
667
534
|
}
|
|
668
535
|
|
|
@@ -673,11 +540,11 @@ function admitClosure(
|
|
|
673
540
|
if (!Array.isArray(value)) return null
|
|
674
541
|
const closure: CanonicalDomainSchemaV1[] = []
|
|
675
542
|
const origins = new Set<string>()
|
|
676
|
-
for (const
|
|
677
|
-
if (!isCanonicalDomainSchemaV1(
|
|
678
|
-
if (origins.has(
|
|
679
|
-
origins.add(
|
|
680
|
-
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)
|
|
681
548
|
}
|
|
682
549
|
return closure
|
|
683
550
|
}
|
|
@@ -688,52 +555,17 @@ function studioSchema(value: unknown): JsonSchema {
|
|
|
688
555
|
return (jsonCopy(asRecord(value) ?? {}) ?? {}) as JsonSchema
|
|
689
556
|
}
|
|
690
557
|
|
|
691
|
-
function
|
|
692
|
-
return Object.fromEntries(entriesOf(value).map(([name, schema]) => [name, studioSchema(schema)]))
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
function requiredNames(value: unknown): string[] {
|
|
696
|
-
const object = asRecord(value)
|
|
697
|
-
return arrayOf(object?.required).filter((name): name is string => typeof name === 'string')
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
function legacyRequiredParams(params: Record<string, JsonSchema>): string[] {
|
|
701
|
-
return Object.entries(params).flatMap(([name, schema]) =>
|
|
702
|
-
schemaAllowsNull(schema) ? [] : [name],
|
|
703
|
-
)
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
function schemaAllowsNull(schema: JsonSchema): boolean {
|
|
707
|
-
if (schema.type === 'null') return true
|
|
708
|
-
if (Array.isArray(schema.type) && schema.type.includes('null')) return true
|
|
709
|
-
return [...arrayOf(schema.anyOf), ...arrayOf(schema.oneOf)].some(
|
|
710
|
-
(candidate) => asRecord(candidate)?.type === 'null',
|
|
711
|
-
)
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
function objectInputFromParams(
|
|
715
|
-
params: Record<string, JsonSchema>,
|
|
716
|
-
required: readonly string[],
|
|
717
|
-
): JsonSchema {
|
|
718
|
-
return {
|
|
719
|
-
type: 'object',
|
|
720
|
-
properties: params,
|
|
721
|
-
required: [...required],
|
|
722
|
-
additionalProperties: false,
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
function recordCopy(value: unknown): Record<string, any> {
|
|
558
|
+
function recordCopy(value: unknown): Record<string, unknown> {
|
|
727
559
|
const record = asRecord(value)
|
|
728
|
-
return record ? (jsonCopy(record) as Record<string,
|
|
560
|
+
return record === null ? {} : (jsonCopy(record) as Record<string, unknown>)
|
|
729
561
|
}
|
|
730
562
|
|
|
731
563
|
function jsonCopy(value: unknown): any {
|
|
732
564
|
if (Array.isArray(value)) return value.map(jsonCopy)
|
|
733
565
|
const record = asRecord(value)
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
566
|
+
return record === null
|
|
567
|
+
? value
|
|
568
|
+
: Object.fromEntries(Object.entries(record).map(([key, item]) => [key, jsonCopy(item)]))
|
|
737
569
|
}
|
|
738
570
|
|
|
739
571
|
function entriesOf(value: unknown): [string, unknown][] {
|