@astrale-os/cli 1.0.0-beta.3 → 1.0.0-beta.31
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 +84 -6
- package/dist/astrale.js +57586 -60753
- package/dist/public/connect-core.js +2158 -30706
- package/dist/public/keys/index.js +1016 -29541
- package/dist/public/paths/index.js +370 -28654
- 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 +13 -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/dist/types/state/exchange-credentials.d.ts +6 -2
- package/dist/types/state/files.d.ts +2 -0
- package/dist/types/state/index.d.ts +3 -2
- package/dist/types/state/paths.d.ts +2 -0
- package/dist/types/state/session-routes.d.ts +10 -0
- package/package.json +16 -19
- 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 +47 -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 +30 -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 +6 -17
- 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 +119 -11
- 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 +50 -2
- package/src/commands/auth/logout.ts +2 -1
- package/src/commands/browser.ts +29 -0
- package/src/commands/call-describe.ts +60 -68
- package/src/commands/call.ts +71 -59
- package/src/commands/domain/install.ts +73 -46
- 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 +15 -16
- package/src/commands/identity/__tests__/register.test.ts +3 -3
- package/src/commands/identity/register.ts +8 -8
- 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 +53 -41
- package/src/commands/logs.ts +123 -36
- package/src/commands/mutate.ts +6 -5
- package/src/commands/query.ts +39 -36
- package/src/commands/session/analyze.ts +26 -4
- 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 +21 -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 +90 -48
- package/src/commands/view.ts +82 -37
- package/src/connection/.spec/api.d.ts +7 -8
- package/src/connection/.spec/architecture.md +14 -5
- 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__/auth.test.ts +1 -0
- package/src/connection/__tests__/credential.test.ts +21 -1
- package/src/connection/__tests__/errors.test.ts +27 -20
- package/src/connection/__tests__/exchange.test.ts +64 -11
- 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 +8 -4
- package/src/connection/__tests__/target.test.ts +2 -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 +110 -61
- 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 +27 -9
- package/src/errors.ts +3 -2
- package/src/graph/.spec/api.d.ts +13 -6
- package/src/graph/.spec/laws/documents.ts +7 -7
- package/src/graph/.spec/layout.ts +9 -1
- package/src/graph/__tests__/mutation.test.ts +2 -2
- package/src/graph/__tests__/query.test.ts +35 -9
- package/src/graph/class.ts +30 -0
- package/src/graph/index.ts +1 -0
- package/src/graph/query.ts +27 -40
- package/src/identity/.spec/api.d.ts +2 -2
- package/src/identity/__tests__/fixtures/registry-journey.ts +13 -1
- package/src/identity/__tests__/registration.test.ts +1 -1
- package/src/identity/__tests__/registry.test.ts +4 -0
- package/src/identity/registration.ts +3 -3
- package/src/identity/registry.ts +2 -0
- 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__/browser-retention.test.ts +212 -0
- package/src/lib/__tests__/command-dx.test.ts +1 -1
- package/src/lib/__tests__/config.test.ts +27 -0
- 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__/skills.test.ts +834 -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 +25 -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/browser-retention.ts +210 -0
- package/src/lib/command-dx.ts +9 -9
- package/src/lib/config.ts +12 -1
- 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/skills/lock.ts +117 -0
- package/src/lib/skills/sync.ts +878 -0
- package/src/lib/skills.ts +1 -52
- 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 +201 -7
- 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/setup/steps/skills.ts +40 -38
- package/src/state/.spec/api.d.ts +21 -2
- package/src/state/.spec/architecture.md +18 -4
- package/src/state/.spec/layout.ts +1 -0
- package/src/state/__tests__/exchange-credentials.test.ts +88 -42
- package/src/state/__tests__/files.test.ts +24 -1
- package/src/state/__tests__/fixtures/session-route-process.ts +93 -0
- package/src/state/__tests__/paths.test.ts +1 -0
- package/src/state/__tests__/session-routes.test.ts +138 -0
- package/src/state/exchange-credentials.ts +22 -9
- package/src/state/files.ts +41 -0
- package/src/state/index.ts +3 -1
- package/src/state/paths.ts +3 -0
- package/src/state/session-routes.ts +34 -0
- package/src/telemetry/__tests__/analyze-log.test.ts +38 -0
- package/src/telemetry/__tests__/recorder.test.ts +1 -0
- package/src/telemetry/__tests__/retention.test.ts +267 -0
- package/src/telemetry/__tests__/settings.test.ts +102 -0
- package/src/telemetry/__tests__/store-scan.test.ts +128 -0
- package/src/telemetry/__tests__/trigger.test.ts +33 -4
- package/src/telemetry/analyze.ts +24 -2
- package/src/telemetry/recorder.ts +15 -4
- package/src/telemetry/retention.ts +176 -0
- package/src/telemetry/session.ts +18 -12
- package/src/telemetry/settings.ts +64 -11
- package/src/telemetry/store.ts +92 -9
- package/src/telemetry/trigger.ts +16 -28
- package/src/ui/.spec/api.d.ts +14 -0
- package/src/ui/.spec/architecture.md +14 -0
- package/src/ui/.spec/laws.ts +50 -0
- package/src/ui/.spec/layout.ts +16 -0
- package/src/ui/__tests__/ui.test.ts +1742 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/lock.ts +89 -0
- package/src/ui/model.ts +83 -0
- package/src/ui/operations.ts +1044 -0
- package/src/ui/project.ts +333 -0
- package/src/ui/release.ts +398 -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-BFVFs_8x.js +81 -0
- package/studio/client/dist/assets/index-DuB9iUdu.css +2 -0
- package/studio/client/dist/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
- package/studio/client/dist/assets/schema-studio--a0kX3QU.css +1 -0
- package/studio/client/dist/assets/schema-studio-DH6oEWME.js +8 -0
- package/studio/client/dist/index.html +4 -3
- package/studio/package.json +8 -14
- 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 +41 -32
- package/studio/server/agent/prompts/anchors.ts +23 -51
- package/studio/server/agent/prompts/system.test.ts +4 -4
- package/studio/server/agent/prompts/system.ts +5 -5
- package/studio/server/agent/routes.test.ts +1 -1
- package/studio/server/agent/run/coordinator.test.ts +1 -1
- package/studio/server/agent/run/preparation.ts +1 -1
- package/studio/server/api/canvas.ts +0 -1
- package/studio/server/api/context.ts +1 -1
- package/studio/server/api/deployment.ts +1 -1
- package/studio/server/api/views.ts +2 -2
- package/studio/server/api/workspace.ts +1 -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 +24 -18
- package/studio/server/cache.ts +10 -54
- package/studio/server/cli-consumers.test.ts +32 -4
- package/studio/server/cli.test.ts +2 -2
- package/studio/server/domain.test.ts +50 -68
- package/studio/server/domain.ts +82 -63
- package/studio/server/handoff/copy.ts +2 -2
- 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 +10 -16
- package/studio/server/introspect/anatomy-extras.test.ts +29 -56
- 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 +155 -424
- package/studio/server/introspect/canonical-schema.ts +248 -601
- package/studio/server/introspect/core.ts +14 -22
- package/studio/server/introspect/diff.test.ts +21 -9
- package/studio/server/introspect/diff.ts +19 -64
- package/studio/server/introspect/extractor.ts +23 -122
- package/studio/server/introspect/overlay-tsmorph.test.ts +116 -241
- package/studio/server/introspect/overlay-tsmorph.ts +0 -1
- package/studio/server/introspect/overlay.ts +3 -51
- package/studio/server/introspect/revision.test.ts +24 -28
- package/studio/server/introspect/revision.ts +11 -22
- package/studio/server/introspect/runtime.test.ts +77 -195
- package/studio/server/introspect/runtime.ts +5 -67
- package/studio/server/introspect/schema-ir-json.test.ts +81 -0
- package/studio/server/introspect/schema-ir-json.ts +39 -69
- 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/lifecycle.ts +4 -1
- package/studio/server/state/baseline.test.ts +14 -9
- package/studio/server/state/baseline.ts +3 -5
- package/studio/server/state/documents.test.ts +69 -0
- package/studio/server/state/documents.ts +57 -10
- package/studio/server/state/visibility.ts +1 -5
- package/studio/server/views/model.ts +3 -3
- package/studio/server/views/target.test.ts +18 -76
- package/studio/server/views/target.ts +25 -51
- package/studio/server/watch.test.ts +11 -11
- package/studio/server/watch.ts +8 -4
- package/studio/server/workspace/updates.ts +23 -1
- package/studio/server/workspace-watch.ts +2 -2
- package/studio/shared/contracts/runtime.ts +4 -0
- package/studio/shared/contracts/schema.ts +44 -131
- package/studio/shared/contracts/surface.test.ts +15 -17
- package/studio/shared/contracts/workspace.ts +5 -11
- package/studio/shared/schema/identity.test.ts +20 -44
- package/studio/shared/schema/identity.ts +9 -24
- package/studio/tsconfig.json +2 -1
- package/tsconfig.json +1 -1
- package/viewer/dist/index.html +21 -0
- package/viewer/dist/main.js +41 -62
- 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
- package/studio/server/introspect/core-extractor.ts +0 -139
- package/studio/server/introspect/overlay.test.ts +0 -72
- package/studio/server/introspect/source-overlay/annotations.ts +0 -14
|
@@ -1,55 +1,94 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* the domain's own SDK cohort, not a second copy installed beside Studio.
|
|
2
|
+
* Project the DSL's portable Schema into Studio's deliberately smaller render
|
|
3
|
+
* model. Admission, dependency reachability, and reference resolution remain
|
|
4
|
+
* owned by the Domain's installed DSL.
|
|
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,
|
|
19
18
|
JsonSchema,
|
|
20
19
|
SchemaIR,
|
|
20
|
+
SchemaRevision,
|
|
21
21
|
StudioCore,
|
|
22
22
|
} from '../../shared/types'
|
|
23
23
|
|
|
24
|
-
import { definitionRefKey, isIrDefinitionRef, schemaRefKey } from '../../shared/schema/identity'
|
|
25
24
|
import { isSchemaRevision } from '../../shared/types'
|
|
26
25
|
|
|
27
26
|
type AnyRecord = Record<string, unknown>
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
/** The portable V1 value transported across the extractor process boundary. */
|
|
29
|
+
export interface CanonicalDomainSchemaV1 {
|
|
30
30
|
readonly format: 'astrale.dsl'
|
|
31
31
|
readonly version: 'v1'
|
|
32
32
|
readonly origin: string
|
|
33
|
+
readonly dependencies: Readonly<
|
|
34
|
+
Record<string, { readonly origin: string; readonly revision: string }>
|
|
35
|
+
>
|
|
36
|
+
readonly classes: Readonly<Record<string, unknown>>
|
|
37
|
+
readonly functions: Readonly<Record<string, unknown>>
|
|
38
|
+
readonly policies: Readonly<Record<string, unknown>>
|
|
39
|
+
readonly views: Readonly<Record<string, unknown>>
|
|
40
|
+
readonly core: unknown
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface ResolvedSchemaDomain {
|
|
44
|
+
readonly source: CanonicalDomainSchemaV1
|
|
45
|
+
readonly revision: unknown
|
|
46
|
+
definition(ref: IrClassRef): { readonly definition: unknown } | undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Runtime facade supplied by the inspected Domain, not by Studio's package. */
|
|
50
|
+
export interface SchemaSdk {
|
|
51
|
+
readonly bundle: {
|
|
52
|
+
create(input: any): unknown
|
|
53
|
+
accept(input: any): {
|
|
54
|
+
readonly root: CanonicalDomainSchemaV1
|
|
55
|
+
readonly closure: readonly CanonicalDomainSchemaV1[]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
readonly schema: {
|
|
59
|
+
resolve(input: any): ResolvedSchemaDomain
|
|
60
|
+
compareDependencyMeaning(
|
|
61
|
+
source: CanonicalDomainSchemaV1,
|
|
62
|
+
target: CanonicalDomainSchemaV1,
|
|
63
|
+
): { readonly footprint: readonly unknown[] }
|
|
64
|
+
}
|
|
65
|
+
readonly ClassKey: {
|
|
66
|
+
is(input: unknown): input is IrClassKey
|
|
67
|
+
ref(input: IrClassKey): IrClassRef
|
|
68
|
+
}
|
|
33
69
|
}
|
|
34
70
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
71
|
+
/** Smaller facade used when re-admitting an installed Bundle for drift checks. */
|
|
72
|
+
export interface SchemaAdmissionSdk {
|
|
73
|
+
readonly bundle: {
|
|
74
|
+
accept(input: any): { readonly root: CanonicalDomainSchemaV1 }
|
|
75
|
+
}
|
|
76
|
+
readonly schema: {
|
|
77
|
+
resolve(input: any): { readonly source: unknown; readonly revision: unknown }
|
|
78
|
+
}
|
|
38
79
|
}
|
|
39
80
|
|
|
40
|
-
export
|
|
41
|
-
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
revision: null
|
|
52
|
-
}
|
|
81
|
+
export interface CanonicalSchemaExtraction {
|
|
82
|
+
readonly status: 'admitted' | 'preview'
|
|
83
|
+
readonly root: CanonicalDomainSchemaV1
|
|
84
|
+
readonly revision: SchemaRevision | null
|
|
85
|
+
readonly ir: SchemaIR
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface ImportProjection {
|
|
89
|
+
importsByKey: Record<IrClassKey, IrImportDescriptor>
|
|
90
|
+
importedClassesByKey: Record<IrClassKey, IrClass>
|
|
91
|
+
}
|
|
53
92
|
|
|
54
93
|
export function isCanonicalDomainSchemaV1(value: unknown): value is CanonicalDomainSchemaV1 {
|
|
55
94
|
const candidate = asRecord(value)
|
|
@@ -67,414 +106,212 @@ export function findCanonicalDomainSchemaExport(
|
|
|
67
106
|
): CanonicalDomainSchemaV1 | null {
|
|
68
107
|
if (isCanonicalDomainSchemaV1(moduleExports.schema)) return moduleExports.schema
|
|
69
108
|
if (isCanonicalDomainSchemaV1(moduleExports.default)) return moduleExports.default
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
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
|
-
*/
|
|
84
|
-
export function closureFromSdk(
|
|
85
|
-
sdkModule: Record<string, unknown>,
|
|
86
|
-
root: CanonicalDomainSchemaV1,
|
|
87
|
-
): CanonicalDomainSchemaV1[] {
|
|
88
|
-
const bundleApi = asRecord(sdkModule.bundle)
|
|
89
|
-
const create = bundleApi?.create
|
|
90
|
-
if (typeof create === 'function') {
|
|
91
|
-
try {
|
|
92
|
-
const candidate = Reflect.apply(create, bundleApi, [root])
|
|
93
|
-
const bundle = asRecord(candidate)
|
|
94
|
-
if (bundle?.root === root) {
|
|
95
|
-
const closure = admitClosure(bundle.closure, root)
|
|
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
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return []
|
|
109
|
+
return (
|
|
110
|
+
Object.entries(moduleExports)
|
|
111
|
+
.filter((entry): entry is [string, CanonicalDomainSchemaV1] =>
|
|
112
|
+
isCanonicalDomainSchemaV1(entry[1]),
|
|
113
|
+
)
|
|
114
|
+
.sort(([left], [right]) => left.localeCompare(right))[0]?.[1] ?? null
|
|
115
|
+
)
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
/**
|
|
122
|
-
* Re-admit
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* structural preview, but it must not assign a canonical revision to it.
|
|
119
|
+
* Re-admit the authored Schema as one exact DSL Bundle and resolve its Domain.
|
|
120
|
+
* A V1-shaped value rejected by the installed DSL remains a best-effort local
|
|
121
|
+
* preview while the author is in the middle of an edit.
|
|
126
122
|
*/
|
|
127
|
-
export function
|
|
128
|
-
|
|
123
|
+
export function extractCanonicalSchemaFromSdk(
|
|
124
|
+
sdk: SchemaSdk,
|
|
129
125
|
candidate: CanonicalDomainSchemaV1,
|
|
130
|
-
):
|
|
131
|
-
const retainedClosure = closureFromSdk(sdkModule, candidate)
|
|
132
|
-
const schemaApi = asRecord(sdkModule.schema)
|
|
133
|
-
const accept = schemaApi?.accept
|
|
134
|
-
const revision = schemaApi?.revision
|
|
135
|
-
if (!schemaApi || typeof accept !== 'function' || typeof revision !== 'function') {
|
|
136
|
-
return { status: 'preview', root: candidate, closure: retainedClosure, revision: null }
|
|
137
|
-
}
|
|
138
|
-
|
|
126
|
+
): CanonicalSchemaExtraction {
|
|
139
127
|
try {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (!isSchemaRevision(value)) throw new TypeError('SDK returned an invalid schema revision')
|
|
144
|
-
revisions.set(dependency.origin, value)
|
|
128
|
+
const admitted = sdk.bundle.accept(sdk.bundle.create(candidate))
|
|
129
|
+
if (admitted.root.origin !== candidate.origin) {
|
|
130
|
+
throw new TypeError('DSL Bundle admission returned another Schema root.')
|
|
145
131
|
}
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return dependency && revisions.get(origin) === expected ? dependency : undefined
|
|
150
|
-
},
|
|
132
|
+
const domain = sdk.schema.resolve(admitted.root)
|
|
133
|
+
if (domain.source !== admitted.root || !isSchemaRevision(domain.revision)) {
|
|
134
|
+
throw new TypeError('DSL resolution did not retain the admitted Schema root.')
|
|
151
135
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (!isSchemaRevision(acceptedRevision)) {
|
|
158
|
-
throw new TypeError('SDK returned an invalid schema revision')
|
|
159
|
-
}
|
|
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
|
|
136
|
+
return {
|
|
137
|
+
status: 'admitted',
|
|
138
|
+
root: admitted.root,
|
|
139
|
+
revision: domain.revision,
|
|
140
|
+
ir: projectSchema(admitted.root, projectImports(sdk, domain, admitted.closure)),
|
|
172
141
|
}
|
|
173
|
-
|
|
174
|
-
return { status: 'admitted', root: accepted, closure, revision: acceptedRevision }
|
|
175
142
|
} catch {
|
|
176
|
-
return { status: 'preview', root: candidate,
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
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
|
-
export function projectCanonicalSchema(
|
|
215
|
-
root: CanonicalDomainSchemaV1,
|
|
216
|
-
closure: readonly CanonicalDomainSchemaV1[] = [],
|
|
217
|
-
): CanonicalSchemaProjection {
|
|
218
|
-
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
|
-
const classes = Object.fromEntries(
|
|
226
|
-
entriesOf(root.classes).map(([name, declaration]) => [
|
|
227
|
-
name,
|
|
228
|
-
projectClass(origin, name, declaration),
|
|
229
|
-
]),
|
|
230
|
-
)
|
|
231
|
-
const functions = Object.fromEntries(
|
|
232
|
-
entriesOf(root.functions).map(([name, declaration]) => [
|
|
233
|
-
name,
|
|
234
|
-
projectFunction(name, declaration),
|
|
235
|
-
]),
|
|
236
|
-
)
|
|
237
|
-
const views = Object.fromEntries(
|
|
238
|
-
entriesOf(root.views).map(([name, declaration]) => [name, projectView(name, declaration)]),
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
const importsByKey: Record<IrDefinitionKey, IrImportDescriptor> = {}
|
|
242
|
-
const importedInterfacesByKey: Record<IrDefinitionKey, IrInterface> = {}
|
|
243
|
-
const schemas = new Map(
|
|
244
|
-
closure.filter(isCanonicalDomainSchemaV1).map((schema) => [schema.origin, schema]),
|
|
245
|
-
)
|
|
246
|
-
const pending = collectDefinitionRefs(root)
|
|
247
|
-
const visited = new Set<string>()
|
|
248
|
-
|
|
249
|
-
for (let index = 0; index < pending.length; index++) {
|
|
250
|
-
const ref = pending[index]
|
|
251
|
-
if (!isIrDefinitionRef(ref) || ref.origin === origin) continue
|
|
252
|
-
const key = definitionRefKey(ref)
|
|
253
|
-
if (visited.has(key)) continue
|
|
254
|
-
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
|
-
|
|
264
|
-
const owner = schemas.get(ref.origin)
|
|
265
|
-
const declaration = asRecord(asRecord(owner?.interfaces)?.[ref.name])
|
|
266
|
-
if (!owner || !declaration) continue
|
|
267
|
-
const projected = projectInterface(owner.origin, ref.name, declaration)
|
|
268
|
-
importedInterfacesByKey[key] = projected
|
|
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)
|
|
279
|
-
}
|
|
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
|
-
|
|
295
|
-
const dependencies = arrayOf(root.dependencies)
|
|
296
|
-
.map(asRecord)
|
|
297
|
-
.filter((dependency): dependency is AnyRecord => dependency !== null)
|
|
298
|
-
.flatMap((dependency) =>
|
|
299
|
-
typeof dependency.origin === 'string' && typeof dependency.revision === 'string'
|
|
300
|
-
? [{ origin: dependency.origin, revision: dependency.revision }]
|
|
301
|
-
: [],
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
return {
|
|
305
|
-
ir: {
|
|
306
|
-
format: 'astrale.dsl',
|
|
307
|
-
version: 'v1',
|
|
308
|
-
domain: origin,
|
|
309
|
-
types: schemaRecord(root.types),
|
|
310
|
-
interfaces,
|
|
311
|
-
classes,
|
|
312
|
-
imports,
|
|
313
|
-
importsByKey,
|
|
314
|
-
importedInterfacesByKey,
|
|
315
|
-
functions,
|
|
316
|
-
views,
|
|
317
|
-
policies: recordCopy(root.policies),
|
|
318
|
-
dependencies,
|
|
319
|
-
core: jsonCopy(root.core),
|
|
320
|
-
},
|
|
321
|
-
importedInterfaces,
|
|
143
|
+
return { status: 'preview', root: candidate, revision: null, ir: projectSchema(candidate) }
|
|
322
144
|
}
|
|
323
145
|
}
|
|
324
146
|
|
|
325
|
-
/** Project canonical
|
|
147
|
+
/** Project canonical Core data without importing Application or Runtime modules. */
|
|
326
148
|
export function projectCanonicalCore(
|
|
327
149
|
root: CanonicalDomainSchemaV1,
|
|
328
150
|
): Pick<StudioCore, 'domain' | 'nodes' | 'edges'> {
|
|
329
151
|
const core = asRecord(root.core) ?? {}
|
|
330
152
|
const nodes = entriesOf(core.nodes).map(([slug, raw]) => {
|
|
331
153
|
const node = asRecord(raw) ?? {}
|
|
332
|
-
const classRef = schemaRef(node.class)
|
|
333
154
|
return {
|
|
334
|
-
path: corePath({ origin: root.origin,
|
|
335
|
-
className: classRef?.name ?? '?',
|
|
155
|
+
path: corePath({ origin: root.origin, name: slug }),
|
|
156
|
+
className: classRef(node.class)?.name ?? '?',
|
|
336
157
|
data: recordCopy(node.properties),
|
|
337
158
|
}
|
|
338
159
|
})
|
|
339
160
|
const edges = arrayOf(core.edges).map((raw) => {
|
|
340
161
|
const edge = asRecord(raw) ?? {}
|
|
341
|
-
const classRef = schemaRef(edge.class)
|
|
342
162
|
return {
|
|
343
163
|
from: coreEndpoint(edge.source, root.origin),
|
|
344
164
|
to: coreEndpoint(edge.target, root.origin),
|
|
345
|
-
edgeName: classRef?.name ?? '?',
|
|
165
|
+
edgeName: classRef(edge.class)?.name ?? '?',
|
|
346
166
|
...(edge.properties === undefined ? {} : { data: recordCopy(edge.properties) }),
|
|
347
167
|
}
|
|
348
168
|
})
|
|
349
169
|
return { domain: root.origin, nodes, edges }
|
|
350
170
|
}
|
|
351
171
|
|
|
352
|
-
function
|
|
353
|
-
const declaration = asRecord(value) ?? {}
|
|
354
|
-
const family = definitionFamily(declaration.family, 'both')
|
|
355
|
-
const extendsRefs = refsOf(declaration.extends, 'interface')
|
|
172
|
+
function projectSchema(root: CanonicalDomainSchemaV1, imports?: ImportProjection): SchemaIR {
|
|
356
173
|
return {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
origin,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
174
|
+
format: 'astrale.dsl',
|
|
175
|
+
version: 'v1',
|
|
176
|
+
domain: root.origin,
|
|
177
|
+
classes: Object.fromEntries(
|
|
178
|
+
entriesOf(root.classes).map(([name, value]) => [
|
|
179
|
+
name,
|
|
180
|
+
projectClass(root.origin, name, value),
|
|
181
|
+
]),
|
|
182
|
+
),
|
|
183
|
+
importsByKey: imports?.importsByKey ?? {},
|
|
184
|
+
importedClassesByKey: imports?.importedClassesByKey ?? {},
|
|
185
|
+
functions: Object.fromEntries(
|
|
186
|
+
entriesOf(root.functions).map(([name, value]) => [name, projectFunction(name, value)]),
|
|
187
|
+
),
|
|
188
|
+
views: Object.fromEntries(
|
|
189
|
+
entriesOf(root.views).map(([name, value]) => [name, projectView(name, value)]),
|
|
190
|
+
),
|
|
191
|
+
policies: recordCopy(root.policies),
|
|
192
|
+
dependencies: Object.values(root.dependencies ?? {}).map(({ origin, revision }) => ({
|
|
193
|
+
origin,
|
|
194
|
+
revision,
|
|
195
|
+
})),
|
|
196
|
+
core: jsonCopy(root.core),
|
|
375
197
|
}
|
|
376
198
|
}
|
|
377
199
|
|
|
200
|
+
function projectImports(
|
|
201
|
+
sdk: SchemaSdk,
|
|
202
|
+
domain: ReturnType<SchemaSdk['schema']['resolve']>,
|
|
203
|
+
closure: readonly CanonicalDomainSchemaV1[],
|
|
204
|
+
): ImportProjection {
|
|
205
|
+
const importsByKey = {} as Record<IrClassKey, IrImportDescriptor>
|
|
206
|
+
const importedClassesByKey = {} as Record<IrClassKey, IrClass>
|
|
207
|
+
|
|
208
|
+
// The DSL computes the exact reachable footprint. Studio no longer recurses
|
|
209
|
+
// through JSON Schemas, policies, Views, and Core declarations to rediscover it.
|
|
210
|
+
for (const dependency of closure) {
|
|
211
|
+
const footprint = sdk.schema.compareDependencyMeaning(domain.source, dependency).footprint
|
|
212
|
+
for (const candidate of footprint) {
|
|
213
|
+
if (!sdk.ClassKey.is(candidate)) continue
|
|
214
|
+
const ref = sdk.ClassKey.ref(candidate)
|
|
215
|
+
const definition = domain.definition(ref)
|
|
216
|
+
if (!definition) continue
|
|
217
|
+
const key = candidate as IrClassKey
|
|
218
|
+
importsByKey[key] = { origin: ref.origin, ref, key }
|
|
219
|
+
importedClassesByKey[key] = projectClass(ref.origin, ref.name, definition.definition)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { importsByKey, importedClassesByKey }
|
|
223
|
+
}
|
|
224
|
+
|
|
378
225
|
function projectClass(origin: string, name: string, value: unknown): IrClass {
|
|
379
226
|
const declaration = asRecord(value) ?? {}
|
|
380
|
-
const
|
|
381
|
-
const
|
|
227
|
+
const kind = declaration.kind === 'edge' ? 'edge' : 'node'
|
|
228
|
+
const extendsRefs = refsOf(declaration.extends)
|
|
229
|
+
const propertyEntries = entriesOf(declaration.properties)
|
|
230
|
+
const propertyMetadata = Object.fromEntries(
|
|
231
|
+
propertyEntries.map(([propertyName, raw]) => {
|
|
232
|
+
const member = { ...(asRecord(raw) ?? {}) }
|
|
233
|
+
delete member.schema
|
|
234
|
+
return [propertyName, jsonCopy(member)]
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
382
237
|
const policies = Object.fromEntries(
|
|
383
238
|
entriesOf(declaration.policies).flatMap(([policyName, raw]) => {
|
|
384
|
-
const ref =
|
|
239
|
+
const ref = definitionRefOf(raw)
|
|
385
240
|
return ref ? [[policyName, ref]] : []
|
|
386
241
|
}),
|
|
387
242
|
)
|
|
388
243
|
return {
|
|
389
|
-
type:
|
|
244
|
+
type: kind,
|
|
390
245
|
name,
|
|
391
246
|
origin,
|
|
392
247
|
ref: { origin, kind: 'class', name },
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
properties:
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
248
|
+
extends: extendsRefs.map((ref) => ref.name),
|
|
249
|
+
extendsRefs,
|
|
250
|
+
properties: Object.fromEntries(
|
|
251
|
+
propertyEntries.map(([propertyName, raw]) => [
|
|
252
|
+
propertyName,
|
|
253
|
+
studioSchema(asRecord(raw)?.schema),
|
|
254
|
+
]),
|
|
255
|
+
),
|
|
256
|
+
required: propertyEntries.flatMap(([propertyName, raw]) =>
|
|
257
|
+
asRecord(raw)?.required === true ? [propertyName] : [],
|
|
258
|
+
),
|
|
259
|
+
methods: Object.fromEntries(
|
|
260
|
+
entriesOf(declaration.methods).map(([methodName, raw]) => [
|
|
261
|
+
methodName,
|
|
262
|
+
projectMethod(methodName, raw),
|
|
263
|
+
]),
|
|
264
|
+
),
|
|
265
|
+
...(kind === 'edge' ? edgeFields(declaration) : {}),
|
|
399
266
|
...(typeof declaration.icon === 'string' ? { icon: declaration.icon } : {}),
|
|
400
267
|
...(typeof declaration.description === 'string'
|
|
401
268
|
? { description: declaration.description }
|
|
402
269
|
: {}),
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
270
|
+
// the SDK lets a Class carry its own glyph; without this the schema declared one
|
|
271
|
+
// and every surface still drew the generic box
|
|
272
|
+
...(typeof declaration.icon === 'string' ? { icon: declaration.icon } : {}),
|
|
273
|
+
...(Object.keys(propertyMetadata).length === 0 ? {} : { propertyMetadata }),
|
|
406
274
|
...(dataDeclaration(declaration.data) ? { data: dataDeclaration(declaration.data) } : {}),
|
|
407
|
-
...(Object.keys(policies).length
|
|
275
|
+
...(Object.keys(policies).length === 0 ? {} : { policies }),
|
|
408
276
|
}
|
|
409
277
|
}
|
|
410
278
|
|
|
411
|
-
function
|
|
412
|
-
|
|
413
|
-
entriesOf(value).map(([name, declaration]) => {
|
|
414
|
-
const callable = projectCallable(name, declaration)
|
|
415
|
-
const raw = asRecord(declaration) ?? {}
|
|
416
|
-
const inheritance =
|
|
417
|
-
raw.inheritance === 'abstract' ||
|
|
418
|
-
raw.inheritance === 'sealed' ||
|
|
419
|
-
raw.inheritance === 'default'
|
|
420
|
-
? raw.inheritance
|
|
421
|
-
: 'default'
|
|
422
|
-
return [
|
|
423
|
-
name,
|
|
424
|
-
{
|
|
425
|
-
...callable,
|
|
426
|
-
static: raw.static === true,
|
|
427
|
-
inheritance,
|
|
428
|
-
} satisfies IrMethod,
|
|
429
|
-
]
|
|
430
|
-
}),
|
|
431
|
-
)
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function projectFunction(name: string, declaration: unknown): IrFunction {
|
|
279
|
+
function projectMethod(name: string, value: unknown): IrMethod {
|
|
280
|
+
const declaration = asRecord(value) ?? {}
|
|
435
281
|
return {
|
|
436
|
-
...
|
|
437
|
-
static: true,
|
|
438
|
-
inheritance:
|
|
282
|
+
...projectFunction(name, declaration),
|
|
283
|
+
static: declaration.static === true,
|
|
284
|
+
inheritance:
|
|
285
|
+
declaration.inheritance === 'abstract' || declaration.inheritance === 'sealed'
|
|
286
|
+
? declaration.inheritance
|
|
287
|
+
: 'default',
|
|
439
288
|
}
|
|
440
289
|
}
|
|
441
290
|
|
|
442
|
-
function
|
|
291
|
+
function projectFunction(name: string, value: unknown): IrFunction {
|
|
443
292
|
const declaration = asRecord(value) ?? {}
|
|
444
|
-
const input = studioSchema(declaration.input)
|
|
445
|
-
const output = callableOutput(declaration.output, {})
|
|
446
293
|
const auth = callableAuth(declaration.auth)
|
|
447
294
|
return {
|
|
448
295
|
name,
|
|
449
|
-
input,
|
|
450
|
-
|
|
451
|
-
requiredParams: requiredNames(declaration.input),
|
|
452
|
-
output,
|
|
453
|
-
returns: outputValue(output),
|
|
296
|
+
input: studioSchema(declaration.input),
|
|
297
|
+
output: callableOutput(declaration.output),
|
|
454
298
|
...(typeof declaration.description === 'string'
|
|
455
299
|
? { description: declaration.description }
|
|
456
300
|
: {}),
|
|
457
|
-
...(auth ? {
|
|
301
|
+
...(auth === undefined ? {} : { auth }),
|
|
458
302
|
...(declaration.policy === undefined ? {} : { policy: jsonCopy(declaration.policy) }),
|
|
459
303
|
}
|
|
460
304
|
}
|
|
461
305
|
|
|
462
306
|
function projectView(name: string, value: unknown): IrView {
|
|
463
307
|
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'
|
|
308
|
+
const target = asRecord(declaration.target)
|
|
474
309
|
return {
|
|
475
310
|
name,
|
|
476
|
-
target
|
|
477
|
-
|
|
311
|
+
target:
|
|
312
|
+
target?.kind === 'definition'
|
|
313
|
+
? { kind: 'definition', definitions: refsOf(target.definitions) }
|
|
314
|
+
: { kind: 'domain' },
|
|
478
315
|
...(typeof declaration.description === 'string'
|
|
479
316
|
? { description: declaration.description }
|
|
480
317
|
: {}),
|
|
@@ -493,71 +330,46 @@ function edgeFields(
|
|
|
493
330
|
projectEndpoint(endpoints?.source, 'outgoing'),
|
|
494
331
|
projectEndpoint(endpoints?.target, 'incoming'),
|
|
495
332
|
]
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
333
|
+
const constraints = asRecord(declaration.constraints)
|
|
334
|
+
return {
|
|
335
|
+
endpoints: projected,
|
|
336
|
+
orientation,
|
|
337
|
+
constraints: {
|
|
338
|
+
...(constraints?.noSelf === true ? { noSelf: true } : {}),
|
|
339
|
+
...(constraints?.acyclic === true ? { acyclic: true } : {}),
|
|
340
|
+
},
|
|
500
341
|
}
|
|
501
|
-
return { endpoints: projected, orientation, constraints }
|
|
502
342
|
}
|
|
503
343
|
|
|
504
344
|
function projectEndpoint(
|
|
505
345
|
value: unknown,
|
|
506
|
-
|
|
346
|
+
cardinality: 'outgoing' | 'incoming' | 'incident',
|
|
507
347
|
): IrEndpoint {
|
|
508
348
|
const endpoint = asRecord(value) ?? {}
|
|
509
|
-
const refs = refsOf(endpoint.accepts
|
|
510
|
-
const
|
|
349
|
+
const refs = refsOf(endpoint.accepts)
|
|
350
|
+
const count = endpoint[cardinality]
|
|
511
351
|
return {
|
|
512
352
|
name: typeof endpoint.role === 'string' ? endpoint.role : '',
|
|
513
353
|
types: refs.map((ref) => ref.name),
|
|
514
354
|
refs,
|
|
515
|
-
...(
|
|
516
|
-
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function edgeCount(value: unknown): IrEndpoint['cardinality'] | undefined {
|
|
520
|
-
switch (value) {
|
|
521
|
-
case '0..*':
|
|
522
|
-
return { min: 0, max: null }
|
|
523
|
-
case '1..*':
|
|
524
|
-
return { min: 1, max: null }
|
|
525
|
-
case '0..1':
|
|
526
|
-
return { min: 0, max: 1 }
|
|
527
|
-
case '1':
|
|
528
|
-
return { min: 1, max: 1 }
|
|
529
|
-
default:
|
|
530
|
-
return undefined
|
|
355
|
+
...(count === '0..*' || count === '1..*' || count === '0..1' || count === '1'
|
|
356
|
+
? { cardinality: edgeCount(count) }
|
|
357
|
+
: {}),
|
|
531
358
|
}
|
|
532
359
|
}
|
|
533
360
|
|
|
534
|
-
function
|
|
535
|
-
|
|
536
|
-
return
|
|
537
|
-
|
|
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
|
-
)
|
|
361
|
+
function edgeCount(value: '0..*' | '1..*' | '0..1' | '1'): IrEndpoint['cardinality'] {
|
|
362
|
+
if (value === '0..*') return { min: 0, max: null }
|
|
363
|
+
if (value === '1..*') return { min: 1, max: null }
|
|
364
|
+
if (value === '0..1') return { min: 0, max: 1 }
|
|
365
|
+
return { min: 1, max: 1 }
|
|
547
366
|
}
|
|
548
367
|
|
|
549
|
-
function callableOutput(value: unknown
|
|
368
|
+
function callableOutput(value: unknown): IrCallableOutput {
|
|
550
369
|
const output = asRecord(value)
|
|
551
370
|
if (output?.mode === 'stream') return { mode: 'stream', item: studioSchema(output.item) }
|
|
552
371
|
if (output?.mode === 'binary') return { mode: 'binary' }
|
|
553
|
-
|
|
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 {}
|
|
372
|
+
return { mode: 'value', schema: studioSchema(output?.schema) }
|
|
561
373
|
}
|
|
562
374
|
|
|
563
375
|
function callableAuth(value: unknown): IrCallableAuth | undefined {
|
|
@@ -566,8 +378,30 @@ function callableAuth(value: unknown): IrCallableAuth | undefined {
|
|
|
566
378
|
: undefined
|
|
567
379
|
}
|
|
568
380
|
|
|
569
|
-
function
|
|
570
|
-
return value
|
|
381
|
+
function refsOf(value: unknown): IrClassRef[] {
|
|
382
|
+
return arrayOf(value)
|
|
383
|
+
.map(classRef)
|
|
384
|
+
.filter((ref): ref is IrClassRef => ref !== null)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function classRef(value: unknown): IrClassRef | null {
|
|
388
|
+
const ref = definitionRefOf(value)
|
|
389
|
+
return ref?.kind === 'class' ? { origin: ref.origin, kind: 'class', name: ref.name } : null
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function definitionRefOf(value: unknown): IrSchemaRef | null {
|
|
393
|
+
const ref = asRecord(value)
|
|
394
|
+
if (typeof ref?.origin !== 'string' || typeof ref.name !== 'string') return null
|
|
395
|
+
if (
|
|
396
|
+
ref.kind !== 'class' &&
|
|
397
|
+
ref.kind !== 'function' &&
|
|
398
|
+
ref.kind !== 'policy' &&
|
|
399
|
+
ref.kind !== 'view' &&
|
|
400
|
+
ref.kind !== 'core'
|
|
401
|
+
) {
|
|
402
|
+
return null
|
|
403
|
+
}
|
|
404
|
+
return { origin: ref.origin, kind: ref.kind, name: ref.name }
|
|
571
405
|
}
|
|
572
406
|
|
|
573
407
|
function dataDeclaration(
|
|
@@ -579,223 +413,36 @@ function dataDeclaration(
|
|
|
579
413
|
: undefined
|
|
580
414
|
}
|
|
581
415
|
|
|
582
|
-
function refsOf(
|
|
583
|
-
value: unknown,
|
|
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
|
-
}
|
|
617
|
-
for (const [, declaration] of entriesOf(root.classes)) {
|
|
618
|
-
refs.push(...collectDefinitionRefsFromDeclaration(declaration))
|
|
619
|
-
}
|
|
620
|
-
for (const [, callable] of entriesOf(root.functions)) collectCallableRefs(callable, refs)
|
|
621
|
-
for (const [, view] of entriesOf(root.views)) {
|
|
622
|
-
const target = asRecord(asRecord(view)?.target)
|
|
623
|
-
refs.push(...refsOf(target?.definitions, ['class', 'interface']))
|
|
624
|
-
}
|
|
625
|
-
const core = asRecord(root.core)
|
|
626
|
-
for (const [, node] of entriesOf(core?.nodes)) addRef(asRecord(node)?.class, refs)
|
|
627
|
-
for (const edge of arrayOf(core?.edges)) {
|
|
628
|
-
const declaration = asRecord(edge)
|
|
629
|
-
addRef(declaration?.class, refs)
|
|
630
|
-
addRef(declaration?.source, refs)
|
|
631
|
-
addRef(declaration?.target, refs)
|
|
632
|
-
}
|
|
633
|
-
for (const [, policy] of entriesOf(root.policies)) collectPolicyRefs(policy, refs)
|
|
634
|
-
return uniqueRefs(refs)
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
function collectDefinitionRefsFromDeclaration(value: unknown): IrSchemaRef[] {
|
|
638
|
-
const declaration = asRecord(value) ?? {}
|
|
639
|
-
const refs = [
|
|
640
|
-
...refsOf(declaration.extends, 'interface'),
|
|
641
|
-
...refsOf(declaration.implements, 'interface'),
|
|
642
|
-
]
|
|
643
|
-
collectPathSchemaRefs(declaration.properties, refs)
|
|
644
|
-
const endpoints = asRecord(declaration.endpoints)
|
|
645
|
-
for (const endpoint of [
|
|
646
|
-
endpoints?.source,
|
|
647
|
-
endpoints?.target,
|
|
648
|
-
...arrayOf(declaration.endpoints),
|
|
649
|
-
]) {
|
|
650
|
-
refs.push(...refsOf(asRecord(endpoint)?.accepts, ['class', 'interface']))
|
|
651
|
-
}
|
|
652
|
-
for (const [, callable] of entriesOf(declaration.methods)) collectCallableRefs(callable, refs)
|
|
653
|
-
for (const [, policy] of entriesOf(declaration.policies)) addRef(policy, refs)
|
|
654
|
-
return uniqueRefs(refs)
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
function collectCallableRefs(value: unknown, refs: IrSchemaRef[]): void {
|
|
658
|
-
const callable = asRecord(value)
|
|
659
|
-
if (!callable) return
|
|
660
|
-
collectPathSchemaRefs(callable.input, refs)
|
|
661
|
-
const output = asRecord(callable.output)
|
|
662
|
-
collectPathSchemaRefs(output?.schema ?? output?.item, refs)
|
|
663
|
-
collectPolicyRefs(callable.policy, refs)
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/** Embedded value data is opaque; only the declared x-astrale-path vocabulary carries refs. */
|
|
667
|
-
function collectPathSchemaRefs(value: unknown, refs: IrSchemaRef[]): void {
|
|
668
|
-
if (Array.isArray(value)) {
|
|
669
|
-
for (const item of value) collectPathSchemaRefs(item, refs)
|
|
670
|
-
return
|
|
671
|
-
}
|
|
672
|
-
const object = asRecord(value)
|
|
673
|
-
if (!object) return
|
|
674
|
-
const annotation = asRecord(object['x-astrale-path'])
|
|
675
|
-
if (annotation) refs.push(...refsOf(annotation.accepts, ['class', 'interface']))
|
|
676
|
-
for (const [key, child] of Object.entries(object)) {
|
|
677
|
-
if (key === 'const' || key === 'enum' || key === 'default' || key === 'examples') continue
|
|
678
|
-
collectPathSchemaRefs(child, refs)
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
function collectPolicyRefs(value: unknown, refs: IrSchemaRef[]): void {
|
|
683
|
-
if (Array.isArray(value)) {
|
|
684
|
-
for (const item of value) collectPolicyRefs(item, refs)
|
|
685
|
-
return
|
|
686
|
-
}
|
|
687
|
-
const object = asRecord(value)
|
|
688
|
-
if (!object) return
|
|
689
|
-
for (const key of ['check', 'class', 'ref']) addRef(object[key], refs)
|
|
690
|
-
for (const key of [
|
|
691
|
-
'allOf',
|
|
692
|
-
'anyOf',
|
|
693
|
-
'exists',
|
|
694
|
-
'where',
|
|
695
|
-
'match',
|
|
696
|
-
'nodes',
|
|
697
|
-
'source',
|
|
698
|
-
'target',
|
|
699
|
-
'object',
|
|
700
|
-
]) {
|
|
701
|
-
collectPolicyRefs(object[key], refs)
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
function addRef(value: unknown, refs: IrSchemaRef[]): void {
|
|
706
|
-
const ref = schemaRef(value)
|
|
707
|
-
if (ref) refs.push(ref)
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
function uniqueRefs(refs: readonly IrSchemaRef[]): IrSchemaRef[] {
|
|
711
|
-
const seen = new Set<string>()
|
|
712
|
-
return refs.filter((ref) => {
|
|
713
|
-
const key = schemaRefKey(ref)
|
|
714
|
-
if (seen.has(key)) return false
|
|
715
|
-
seen.add(key)
|
|
716
|
-
return true
|
|
717
|
-
})
|
|
718
|
-
}
|
|
719
|
-
|
|
720
416
|
function coreEndpoint(value: unknown, rootOrigin: string): string {
|
|
721
417
|
const endpoint = asRecord(value)
|
|
722
|
-
if (endpoint?.kind === 'domain')
|
|
723
|
-
|
|
418
|
+
if (endpoint?.kind === 'domain') {
|
|
419
|
+
return `/:${typeof endpoint.origin === 'string' ? endpoint.origin : rootOrigin}`
|
|
420
|
+
}
|
|
421
|
+
const ref = definitionRefOf(value)
|
|
724
422
|
return ref?.kind === 'core' ? corePath(ref) : `/:${rootOrigin}`
|
|
725
423
|
}
|
|
726
424
|
|
|
727
|
-
function corePath(ref: Pick<IrSchemaRef, 'origin' | '
|
|
425
|
+
function corePath(ref: Pick<IrSchemaRef, 'origin' | 'name'>): string {
|
|
728
426
|
return `/:${ref.origin}:core.${ref.name}`
|
|
729
427
|
}
|
|
730
428
|
|
|
731
|
-
function admitClosure(
|
|
732
|
-
value: unknown,
|
|
733
|
-
root: CanonicalDomainSchemaV1,
|
|
734
|
-
): CanonicalDomainSchemaV1[] | null {
|
|
735
|
-
if (!Array.isArray(value)) return null
|
|
736
|
-
const closure: CanonicalDomainSchemaV1[] = []
|
|
737
|
-
const origins = new Set<string>()
|
|
738
|
-
for (const schema of value) {
|
|
739
|
-
if (!isCanonicalDomainSchemaV1(schema) || schema.origin === root.origin) return null
|
|
740
|
-
if (origins.has(schema.origin)) return null
|
|
741
|
-
origins.add(schema.origin)
|
|
742
|
-
closure.push(schema)
|
|
743
|
-
}
|
|
744
|
-
return closure
|
|
745
|
-
}
|
|
746
|
-
|
|
747
429
|
function studioSchema(value: unknown): JsonSchema {
|
|
748
430
|
if (value === true) return {}
|
|
749
431
|
if (value === false) return { not: {} }
|
|
750
432
|
return (jsonCopy(asRecord(value) ?? {}) ?? {}) as JsonSchema
|
|
751
433
|
}
|
|
752
434
|
|
|
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> {
|
|
435
|
+
function recordCopy(value: unknown): Record<string, unknown> {
|
|
789
436
|
const record = asRecord(value)
|
|
790
|
-
return record ? (jsonCopy(record) as Record<string,
|
|
437
|
+
return record === null ? {} : (jsonCopy(record) as Record<string, unknown>)
|
|
791
438
|
}
|
|
792
439
|
|
|
793
440
|
function jsonCopy(value: unknown): any {
|
|
794
441
|
if (Array.isArray(value)) return value.map(jsonCopy)
|
|
795
442
|
const record = asRecord(value)
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
443
|
+
return record === null
|
|
444
|
+
? value
|
|
445
|
+
: Object.fromEntries(Object.entries(record).map(([key, item]) => [key, jsonCopy(item)]))
|
|
799
446
|
}
|
|
800
447
|
|
|
801
448
|
function entriesOf(value: unknown): [string, unknown][] {
|