@githolon/dsl 0.108.6 → 0.110.0
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/assets/operation-contract.wasm +0 -0
- package/check_package.mjs +12 -15
- package/compile_package.mjs +17 -18
- package/compile_proof.mjs +16 -1
- package/dart/CHANGELOG.md +76 -1
- package/dart/README.md +25 -2
- package/dart/lib/src/admission_decision.dart +41 -0
- package/dart/lib/src/application_framework_calls.dart +52 -2
- package/dart/lib/src/application_model_adoption.dart +187 -0
- package/dart/lib/src/application_status.dart +73 -0
- package/dart/lib/src/console_enrolment.dart +271 -0
- package/dart/lib/src/console_http_io.dart +26 -0
- package/dart/lib/src/console_http_stub.dart +7 -0
- package/dart/lib/src/dev_cloud.dart +69 -22
- package/dart/lib/src/generated_call.dart +83 -23
- package/dart/lib/src/kernel_primitives.dart +119 -0
- package/dart/lib/src/nms_conformance_runtime.dart +1 -1
- package/dart/lib/src/nms_generated_runtime.dart +32 -10
- package/dart/lib/src/nms_test_environment.dart +1 -1
- package/dart/lib/src/node_runner_transport.dart +41 -46
- package/dart/lib/src/observed_session_directory.dart +70 -0
- package/dart/lib/src/operation_receipts.dart +112 -0
- package/dart/lib/src/peer_application_streams.dart +103 -0
- package/dart/lib/src/reception/dart_reception.dart +4280 -0
- package/dart/lib/src/reception/event_desk.dart +211 -0
- package/dart/lib/src/reception/extremum_read.dart +46 -0
- package/dart/lib/src/reception/local_copies.dart +371 -0
- package/dart/lib/src/reception/peer_office_connection.dart +11 -0
- package/dart/lib/src/reception/progress_desk.dart +267 -0
- package/dart/lib/src/reception/reception_contract.dart +11 -0
- package/dart/lib/src/reception/reception_session.dart +284 -0
- package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
- package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
- package/dart/lib/src/replica_readiness.dart +123 -0
- package/dart/lib/src/runner/node-entry.mjs +23905 -15774
- package/dart/lib/src/runtime_v2_gateway.dart +56 -3
- package/dart/lib/src/runtime_v2_transport.dart +544 -3
- package/dart/lib/src/session_bound.dart +200 -311
- package/dart/lib/src/signing_identity_kernel.dart +84 -0
- package/dart/lib/src/sync_status_projection.dart +548 -0
- package/dart/lib/src/sync_status_reducer.dart +170 -0
- package/dart/lib/src/test_harness.dart +809 -1153
- package/dart/lib/src/testing/test_custody.dart +66 -0
- package/dart/lib/src/testing/test_peer.dart +328 -0
- package/dart/lib/src/typed_watch_registry.dart +169 -0
- package/dart/lib/src/workspace_open_progress.dart +27 -0
- package/dart/pubspec.lock +24 -1
- package/dart/pubspec.yaml +7 -2
- package/dart/pubspec_overrides.yaml +2 -0
- package/dart/test/admission_decision_test.dart +48 -0
- package/dart/test/background_model_adoption_test.dart +228 -0
- package/dart/test/bundle_version_pairing_test.dart +4 -4
- package/dart/test/child_authentication_repair_test.dart +359 -0
- package/dart/test/cloud_bound_local_shell_test.dart +119 -0
- package/dart/test/console_enrolment_refusal_test.dart +52 -0
- package/dart/test/console_enrolment_test.dart +227 -0
- package/dart/test/errors_test.dart +5 -5
- package/dart/test/event_plane_seam_test.dart +50 -164
- package/dart/test/extremum_read_test.dart +58 -0
- package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
- package/dart/test/generated_query_test.dart +32 -0
- package/dart/test/harness_phase_timeout_test.dart +18 -23
- package/dart/test/kernel_author_after_read_test.dart +242 -0
- package/dart/test/kernel_fence_test.dart +137 -0
- package/dart/test/local_copies_test.dart +222 -0
- package/dart/test/model_adoption_test.dart +198 -0
- package/dart/test/node_peer_local_custody_test.dart +153 -0
- package/dart/test/offer_authentication_proof_test.dart +352 -52
- package/dart/test/operation_compatibility_test.dart +151 -0
- package/dart/test/platform_credential_effect_test.dart +386 -0
- package/dart/test/receiving_group_open_test.dart +213 -0
- package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
- package/dart/test/reception_span_trace_test.dart +23 -0
- package/dart/test/replica_readiness_test.dart +156 -0
- package/dart/test/runtime_projections_test.dart +92 -0
- package/dart/test/runtime_v2_gateway_test.dart +131 -0
- package/dart/test/signing_identity_kernel_test.dart +75 -0
- package/dart/test/sync_status_preparation_report_test.dart +29 -0
- package/dart/test/sync_status_reducer_test.dart +419 -0
- package/dart/test/test_harness_test.dart +71 -95
- package/dart/test/typed_query_watch_test.dart +170 -0
- package/dart/test/typed_watch_registry_test.dart +249 -0
- package/dart_lints/README.md +18 -0
- package/dart_lints/lib/nomos_lints.dart +70 -0
- package/dart_lints/pubspec.lock +580 -0
- package/dart_lints/pubspec.yaml +12 -0
- package/dart_lints/test/reference_identity_test.dart +75 -0
- package/dart_types/CHANGELOG.md +18 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
- package/dart_types/lib/nomos_types.dart +9 -1
- package/dart_types/lib/src/admission_decision.dart +37 -0
- package/dart_types/lib/src/aggregate_meta.dart +1 -1
- package/dart_types/lib/src/approved_model.dart +65 -0
- package/dart_types/lib/src/custody.dart +10 -18
- package/dart_types/lib/src/error_info.dart +4 -4
- package/dart_types/lib/src/errors.dart +29 -29
- package/dart_types/lib/src/foreign_ref.dart +62 -33
- package/dart_types/lib/src/local_work_rejected.dart +3 -3
- package/dart_types/lib/src/nomos_ref.dart +3 -1
- package/dart_types/lib/src/principal.dart +2 -2
- package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
- package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/runtime_outcome.dart +133 -0
- package/dart_types/lib/src/subscriptions.dart +116 -23
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/extremum_subscriptions_test.dart +40 -0
- package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
- package/dart_types/test/nomos_ref_address_test.dart +25 -0
- package/dart_types/test/ordered_subscriptions_test.dart +34 -0
- package/operation_contract_check.mjs +24 -0
- package/package.json +11 -6
- package/src/aggregate.ts +70 -10
- package/src/application_topology.ts +18 -11
- package/src/attested.ts +3 -0
- package/src/attested_core.ts +46 -3
- package/src/birth_meter_ensure.ts +19 -7
- package/src/births.ts +29 -4
- package/src/build_package.ts +140 -13
- package/src/business_birth.ts +181 -4
- package/src/codegen_call_proto.ts +1274 -0
- package/src/codegen_call_usda.ts +49 -6
- package/src/codegen_dart.ts +79 -44
- package/src/codegen_dart_client.ts +458 -800
- package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
- package/src/codegen_dart_framework_calls.ts +320 -37
- package/src/codegen_dart_indexed_variant.ts +159 -0
- package/src/codegen_dart_scenario.ts +42 -13
- package/src/codegen_proof.ts +3 -3
- package/src/codegen_provider_dart.ts +5 -0
- package/src/codegen_query_proto.ts +158 -0
- package/src/codegen_refusal.ts +189 -0
- package/src/codegen_result_proto.ts +931 -0
- package/src/codegen_result_usda.ts +4 -0
- package/src/codegen_ts.ts +464 -616
- package/src/codegen_workspace_access.ts +105 -0
- package/src/compatibility_graph.ts +759 -0
- package/src/compatibility_usda.ts +264 -0
- package/src/compile_package_main.ts +2083 -1248
- package/src/custody_placement.ts +156 -0
- package/src/custody_placement_usda.ts +91 -0
- package/src/decl_shapes.ts +21 -2
- package/src/describe_model.ts +1 -0
- package/src/directive.ts +59 -2
- package/src/drivers.ts +51 -0
- package/src/engine_entry.ts +52 -8
- package/src/evolve_preview.ts +11 -4
- package/src/exists.ts +2 -0
- package/src/fields.ts +75 -11
- package/src/flow_gate.ts +2 -1
- package/src/framework/application_home.ts +170 -18
- package/src/framework/attested_receipts.ts +8 -3
- package/src/framework/auth_provider_law.ts +169 -9
- package/src/framework/birth_certificate_law.ts +4 -0
- package/src/framework/capability.ts +34 -3
- package/src/framework/child_authentication.ts +346 -0
- package/src/framework/console_governance.ts +295 -0
- package/src/framework/delegated_roles.ts +301 -0
- package/src/framework/domain_lifecycle.ts +26 -13
- package/src/framework/founding_genesis.ts +193 -0
- package/src/framework/framework_aggregates.ts +1 -1
- package/src/framework/genesis.ts +6 -1
- package/src/framework/governance_relations.ts +19 -0
- package/src/framework/home.ts +5 -734
- package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
- package/src/framework/model_authority_repair.ts +116 -0
- package/src/framework/model_authority_seed.ts +185 -0
- package/src/framework/model_transition.ts +211 -0
- package/src/framework/owned_custody.ts +1 -1
- package/src/framework/principal_enrollment.ts +302 -0
- package/src/framework/receiving_group.ts +857 -0
- package/src/framework/receiving_group_recovery.ts +314 -0
- package/src/framework/relations.ts +4 -8
- package/src/framework/signing_identity.ts +242 -95
- package/src/framework/source_roster.ts +11 -5
- package/src/framework/workspace_access.ts +149 -0
- package/src/framework/workspace_invariant.ts +73 -52
- package/src/framework/workspaces.ts +407 -40
- package/src/framework_call_definitions.ts +32 -3
- package/src/generated_client_bundle_boundary.ts +97 -0
- package/src/generated_client_names.ts +1 -0
- package/src/identity_usda.ts +27 -0
- package/src/index.ts +183 -12
- package/src/indexed_variant.ts +83 -0
- package/src/intent.ts +173 -45
- package/src/law_check.ts +8 -5
- package/src/law_dependency_index.ts +231 -0
- package/src/law_graph.ts +508 -71
- package/src/law_graph_usda.ts +170 -24
- package/src/law_identity.ts +119 -81
- package/src/law_source_map.ts +4 -1
- package/src/law_units.ts +498 -0
- package/src/law_value_type.ts +7 -3
- package/src/member_authentication.ts +302 -0
- package/src/null_meta.ts +1 -1
- package/src/openusd_compiled_layer.ts +132 -0
- package/src/operation_contract_check.ts +31 -0
- package/src/ordered_read.ts +2 -1
- package/src/package_config.ts +33 -12
- package/src/package_dependencies.ts +1 -0
- package/src/plan_source_slice.ts +30 -1
- package/src/protobuf_field_number.ts +36 -0
- package/src/provenance_wire.ts +6 -0
- package/src/query.ts +2 -1
- package/src/read.ts +50 -0
- package/src/relation.ts +4 -1
- package/src/relation_schema.ts +15 -2
- package/src/report.ts +2 -2
- package/src/sqlite_computation_bundle.ts +78 -0
- package/src/sqlite_computed.ts +102 -0
- package/src/sqlite_counts.ts +154 -0
- package/src/sqlite_layout.ts +823 -0
- package/src/sqlite_reads.ts +275 -0
- package/src/sqlite_samples.ts +185 -0
- package/src/stable_id_identity_bridge.ts +1 -1
- package/src/stable_ids_for_module.ts +16 -4
- package/src/test_call_usda.ts +6 -6
- package/src/usda_relationship_integrity.ts +20 -3
- package/src/usda_text.ts +21 -1
- package/src/value_object.ts +81 -11
- package/src/wire.ts +22 -0
- package/src/wire_encode.ts +227 -20
- package/src/workspace_sharding.ts +15 -6
- package/src/workspace_sharing.ts +51 -49
- package/src/zod_introspection.ts +2 -0
- package/compat_package.mjs +0 -46
- package/dart/lib/src/home.dart +0 -15
- package/dart/lib/src/runner/offline-install.mjs +0 -15367
- package/dart/lib/src/source_trust.dart +0 -51
- package/dart/lib/src/webview_bridge.dart +0 -6540
- package/dart/lib/src/workspace_name.dart +0 -30
- package/dart/test/acquire_session_credentials_test.dart +0 -150
- package/dart/test/acquire_session_test.dart +0 -534
- package/dart/test/adv_realm_interference_test.dart +0 -600
- package/dart/test/cloud_bound_topology_test.dart +0 -168
- package/dart/test/cluster_births_test.dart +0 -202
- package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
- package/dart/test/cluster_local_autosync_test.dart +0 -123
- package/dart/test/cluster_scale_test.dart +0 -122
- package/dart/test/compat_test.dart +0 -61
- package/dart/test/dlq_session_surface_test.dart +0 -222
- package/dart/test/first_app_gauntlet_test.dart +0 -386
- package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
- package/dart/test/four_layer_nested_topology_test.dart +0 -290
- package/dart/test/home_parity_test.dart +0 -13
- package/dart/test/local_sync_inert_test.dart +0 -107
- package/dart/test/node_runner_transport_test.dart +0 -78
- package/dart/test/parent_signer_recovery_test.dart +0 -155
- package/dart/test/residency_refusal_test.dart +0 -49
- package/dart/test/sealing_client_test.dart +0 -181
- package/dart/test/session_bound_test.dart +0 -285
- package/dart/test/stale_bundle_test.dart +0 -80
- package/dart/test/vo_read_typing_test.dart +0 -131
- package/dart/test/watch_after_offer_stall_test.dart +0 -100
- package/dart/test/webview_bridge_test.dart +0 -1930
- package/dart/test/workspace_name_test.dart +0 -24
- package/runtime/generated-application-host.js +0 -22860
- package/src/codegen_query_usda.ts +0 -214
- package/src/compat_manifest.ts +0 -26
- package/src/compat_package_main.ts +0 -224
- package/src/shape_compat.ts +0 -352
- package/src/usd_state.ts +0 -505
|
@@ -0,0 +1,823 @@
|
|
|
1
|
+
/** Experimental, disposable SQLite projection plan. NOT law or custody.
|
|
2
|
+
* One traversal emits access offsets, DDL and compatibility costs. Only opted-in
|
|
3
|
+
* experiments consume this; unsupported shapes fail, never silently become JSON.
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import type { LawGraph, LawValueTypeRef } from "./law_graph.js";
|
|
7
|
+
import {
|
|
8
|
+
planSqliteComputations,
|
|
9
|
+
type SqliteComputedPlan,
|
|
10
|
+
} from "./sqlite_computed.js";
|
|
11
|
+
import {
|
|
12
|
+
planSqliteCounts,
|
|
13
|
+
type SqliteCountPlan,
|
|
14
|
+
type SqliteCountSnapshot,
|
|
15
|
+
} from "./sqlite_counts.js";
|
|
16
|
+
import {
|
|
17
|
+
inspectSqliteReads,
|
|
18
|
+
planSqliteQueries,
|
|
19
|
+
type SqliteQueryPlan,
|
|
20
|
+
} from "./sqlite_reads.js";
|
|
21
|
+
|
|
22
|
+
export interface SqliteColumn {
|
|
23
|
+
name: string;
|
|
24
|
+
type: "TEXT" | "INTEGER" | "BLOB";
|
|
25
|
+
}
|
|
26
|
+
export interface SqliteSlot {
|
|
27
|
+
name: string;
|
|
28
|
+
sid: string;
|
|
29
|
+
presence: number; // 0 absent, 1 explicit null, 2 value
|
|
30
|
+
required: boolean;
|
|
31
|
+
nullable: boolean;
|
|
32
|
+
metadata: number[]; // physical, logical, replica, author, contested, schema
|
|
33
|
+
value: SqliteValue;
|
|
34
|
+
surfaced?: SqliteValue;
|
|
35
|
+
}
|
|
36
|
+
export interface SqliteValue {
|
|
37
|
+
kind:
|
|
38
|
+
| "string"
|
|
39
|
+
| "integer"
|
|
40
|
+
| "boolean"
|
|
41
|
+
| "number"
|
|
42
|
+
| "null"
|
|
43
|
+
| "object"
|
|
44
|
+
| "map"
|
|
45
|
+
| "set"
|
|
46
|
+
| "list"
|
|
47
|
+
| "nominal-map"
|
|
48
|
+
| "union"
|
|
49
|
+
| "reference"
|
|
50
|
+
| "versioned-reference";
|
|
51
|
+
column?: number;
|
|
52
|
+
typeName?: string;
|
|
53
|
+
members?: SqliteSlot[];
|
|
54
|
+
table?: string;
|
|
55
|
+
columns?: SqliteColumn[];
|
|
56
|
+
element?: SqliteSlot;
|
|
57
|
+
variants?: { label: string; value: SqliteValue }[];
|
|
58
|
+
literal?: string | number | boolean | null;
|
|
59
|
+
}
|
|
60
|
+
export interface SqliteLayout {
|
|
61
|
+
version: 2;
|
|
62
|
+
aggregateSid: string;
|
|
63
|
+
wireType: string;
|
|
64
|
+
table: string;
|
|
65
|
+
columns: SqliteColumn[];
|
|
66
|
+
fields: SqliteSlot[];
|
|
67
|
+
indexColumn: number;
|
|
68
|
+
spatialIndexes: { sid: string; name: string; path: string[] }[];
|
|
69
|
+
queryReads: SqliteQueryPlan[];
|
|
70
|
+
queryRefusals: { sid: string; reason: string }[];
|
|
71
|
+
countReads: SqliteCountPlan[];
|
|
72
|
+
countRefusals: { sid: string; reason: string }[];
|
|
73
|
+
countSnapshot: SqliteCountSnapshot | null;
|
|
74
|
+
computedReads: SqliteComputedPlan[];
|
|
75
|
+
computedRefusals: { sid: string; reason: string }[];
|
|
76
|
+
ddl: string;
|
|
77
|
+
fingerprint: string;
|
|
78
|
+
budget: {
|
|
79
|
+
columnLimit: number;
|
|
80
|
+
tables: { name: string; columns: number }[];
|
|
81
|
+
maxColumns: number;
|
|
82
|
+
totalColumns: number;
|
|
83
|
+
indexes: number;
|
|
84
|
+
queryIndexes: { name: string; keyColumns: number; parameters: number }[];
|
|
85
|
+
computedSubstrate: {
|
|
86
|
+
declarations: number;
|
|
87
|
+
sqlTemporalReads: number;
|
|
88
|
+
sealedEngineReads: number;
|
|
89
|
+
storedColumns: 0;
|
|
90
|
+
queueTables: 0;
|
|
91
|
+
};
|
|
92
|
+
countSubstrate: {
|
|
93
|
+
declarations: number;
|
|
94
|
+
tables: { name: string; columns: number }[];
|
|
95
|
+
snapshotColumns: number;
|
|
96
|
+
snapshotParameters: number;
|
|
97
|
+
};
|
|
98
|
+
// Shared once per database, not multiplied by aggregate or declaration.
|
|
99
|
+
spatialSubstrate: {
|
|
100
|
+
declarations: number;
|
|
101
|
+
tables: { name: string; columns: number }[];
|
|
102
|
+
};
|
|
103
|
+
statementsPerPage: number;
|
|
104
|
+
maxJoinTables: number;
|
|
105
|
+
maxResultColumns: number;
|
|
106
|
+
maxInsertParameters: number;
|
|
107
|
+
warnings: string[];
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function planSqliteLayout(
|
|
112
|
+
closure: readonly LawGraph[],
|
|
113
|
+
aggregateSid: string,
|
|
114
|
+
indexFieldSid: string,
|
|
115
|
+
options: { columnLimit?: number; warnColumns?: number } = {}
|
|
116
|
+
): SqliteLayout {
|
|
117
|
+
return planWithNodes(
|
|
118
|
+
new Map(
|
|
119
|
+
closure.flatMap((g) => g.nodes.map((n) => [n.entity.id, n] as const))
|
|
120
|
+
),
|
|
121
|
+
aggregateSid,
|
|
122
|
+
indexFieldSid,
|
|
123
|
+
options
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Reuse the graph index for whole-package inspection. The private field probe is
|
|
128
|
+
// diagnostics only; it is never emitted as an executable full-aggregate layout.
|
|
129
|
+
function planWithNodes(
|
|
130
|
+
nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
|
|
131
|
+
aggregateSid: string,
|
|
132
|
+
indexFieldSid: string,
|
|
133
|
+
options: { columnLimit?: number; warnColumns?: number } = {},
|
|
134
|
+
onlyFieldSid?: string
|
|
135
|
+
): SqliteLayout {
|
|
136
|
+
const columnLimit = options.columnLimit ?? 2000;
|
|
137
|
+
// Target compatibility, not SQLITE_MAX_COLUMN's theoretical configurable ceiling.
|
|
138
|
+
if (!Number.isInteger(columnLimit) || columnLimit < 1 || columnLimit > 2000)
|
|
139
|
+
throw new Error("SQLite experimental target column limit must be 1..2000");
|
|
140
|
+
const aggregate = [...nodes.values()].find(
|
|
141
|
+
(n) => n.kind === "aggregate" && n.entity.sid === aggregateSid
|
|
142
|
+
);
|
|
143
|
+
if (aggregate?.kind !== "aggregate")
|
|
144
|
+
throw new Error(`unknown aggregate SID ${aggregateSid}`);
|
|
145
|
+
const identifier = (sid: string) => {
|
|
146
|
+
if (!/^[a-f0-9]+$/i.test(sid))
|
|
147
|
+
throw new Error(`unsafe stable identity ${sid}`);
|
|
148
|
+
return sid.toLowerCase();
|
|
149
|
+
};
|
|
150
|
+
const table = `a_${identifier(aggregateSid)}`;
|
|
151
|
+
const spatialIndexes = [...nodes.values()]
|
|
152
|
+
.flatMap((n) => {
|
|
153
|
+
if (
|
|
154
|
+
n.kind !== "read" ||
|
|
155
|
+
n.readKind !== "spatial" ||
|
|
156
|
+
n.of.id !== aggregate.entity.id
|
|
157
|
+
)
|
|
158
|
+
return [];
|
|
159
|
+
if (n.predicate)
|
|
160
|
+
throw new Error(
|
|
161
|
+
`spatial ${n.entity.label}: filtered index mapping is not supported`
|
|
162
|
+
);
|
|
163
|
+
return [
|
|
164
|
+
{
|
|
165
|
+
sid: n.entity.sid,
|
|
166
|
+
name: n.entity.label,
|
|
167
|
+
path: n.geometry.authoredPath.split("."),
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
})
|
|
171
|
+
.sort((a, b) => a.sid.localeCompare(b.sid));
|
|
172
|
+
const tables: { name: string; columns: SqliteColumn[]; child: boolean }[] =
|
|
173
|
+
[];
|
|
174
|
+
const rootColumns: SqliteColumn[] = [{ name: "id", type: "TEXT" }];
|
|
175
|
+
tables.push({ name: table, columns: rootColumns, child: false });
|
|
176
|
+
const column = (columns: SqliteColumn[], type: SqliteColumn["type"]) => {
|
|
177
|
+
const offset = columns.length;
|
|
178
|
+
columns.push({ name: `c${offset}`, type });
|
|
179
|
+
// Stop expansion promptly; do not build a million-column graph just to report it.
|
|
180
|
+
if (columns.length > columnLimit)
|
|
181
|
+
throw new Error(
|
|
182
|
+
`SQLite layout ${aggregate.entity.label}: table exceeds ${columnLimit} columns (including metadata/presence)`
|
|
183
|
+
);
|
|
184
|
+
return offset;
|
|
185
|
+
};
|
|
186
|
+
const slot = (
|
|
187
|
+
name: string,
|
|
188
|
+
sid: string,
|
|
189
|
+
columns: SqliteColumn[],
|
|
190
|
+
required: boolean,
|
|
191
|
+
nullable: boolean,
|
|
192
|
+
driven: boolean,
|
|
193
|
+
build: () => SqliteValue
|
|
194
|
+
): SqliteSlot => ({
|
|
195
|
+
name,
|
|
196
|
+
sid,
|
|
197
|
+
required,
|
|
198
|
+
nullable,
|
|
199
|
+
presence: column(columns, "INTEGER"),
|
|
200
|
+
metadata: driven
|
|
201
|
+
? (["BLOB", "INTEGER", "BLOB", "TEXT", "INTEGER", "TEXT"] as const).map(
|
|
202
|
+
(t) => column(columns, t)
|
|
203
|
+
)
|
|
204
|
+
: [],
|
|
205
|
+
value: (() => {
|
|
206
|
+
try {
|
|
207
|
+
return build();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
throw new Error(`${name}: ${(error as Error).message}`);
|
|
210
|
+
}
|
|
211
|
+
})(),
|
|
212
|
+
});
|
|
213
|
+
// Each finite schema path owns a table. Instance ancestry is a length-prefixed
|
|
214
|
+
// parent key, not a JSON path; nested lists/maps never expand root row width.
|
|
215
|
+
function collection(
|
|
216
|
+
kind: SqliteValue["kind"],
|
|
217
|
+
build: (c: SqliteColumn[]) => SqliteSlot
|
|
218
|
+
): SqliteValue {
|
|
219
|
+
const name = `${table}_n${tables.length}`;
|
|
220
|
+
const columns: SqliteColumn[] = [
|
|
221
|
+
{ name: "id", type: "TEXT" },
|
|
222
|
+
{ name: "key", type: "TEXT" },
|
|
223
|
+
{ name: "parent", type: "TEXT" },
|
|
224
|
+
];
|
|
225
|
+
tables.push({ name, columns, child: true });
|
|
226
|
+
return { kind, table: name, columns, element: build(columns) };
|
|
227
|
+
}
|
|
228
|
+
const isNullable = (ref: LawValueTypeRef): boolean => {
|
|
229
|
+
const n = nodes.get(ref.id);
|
|
230
|
+
return (
|
|
231
|
+
n?.kind === "structural" &&
|
|
232
|
+
n.structuralKind === "value-type" &&
|
|
233
|
+
(n.expression.kind === "nullable" ||
|
|
234
|
+
(n.expression.kind === "default" && isNullable(n.expression.value)))
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
function value(
|
|
238
|
+
ref: LawValueTypeRef,
|
|
239
|
+
columns: SqliteColumn[],
|
|
240
|
+
active: Set<string>,
|
|
241
|
+
nominalName?: string
|
|
242
|
+
): SqliteValue {
|
|
243
|
+
if (active.has(ref.id))
|
|
244
|
+
throw new Error(
|
|
245
|
+
"SQLite layout: recursive shape requires a separate mapping experiment"
|
|
246
|
+
);
|
|
247
|
+
const next = new Set(active).add(ref.id);
|
|
248
|
+
const node = nodes.get(ref.id);
|
|
249
|
+
if (node?.kind === "type") {
|
|
250
|
+
if (node.form === "geometry") {
|
|
251
|
+
// Geometry's public encoder emits Int for integral coordinates, Double
|
|
252
|
+
// otherwise. A tagged numeric leaf retains both (not coercion to REAL).
|
|
253
|
+
const coords = (c: SqliteColumn[], depth: number): SqliteValue =>
|
|
254
|
+
collection("list", (inner) =>
|
|
255
|
+
slot("element", node.entity.id, inner, true, false, false, () =>
|
|
256
|
+
depth === 1
|
|
257
|
+
? {
|
|
258
|
+
kind: "number",
|
|
259
|
+
column: column(inner, "BLOB"),
|
|
260
|
+
typeName: "geometry-number",
|
|
261
|
+
}
|
|
262
|
+
: coords(inner, depth - 1)
|
|
263
|
+
)
|
|
264
|
+
);
|
|
265
|
+
return {
|
|
266
|
+
kind: "object",
|
|
267
|
+
typeName: node.entity.label,
|
|
268
|
+
members: [
|
|
269
|
+
slot("type", node.entity.id, columns, true, false, false, () => ({
|
|
270
|
+
kind: "string",
|
|
271
|
+
column: column(columns, "TEXT"),
|
|
272
|
+
literal: node.geometry!,
|
|
273
|
+
})),
|
|
274
|
+
slot(
|
|
275
|
+
"coordinates",
|
|
276
|
+
node.entity.id,
|
|
277
|
+
columns,
|
|
278
|
+
true,
|
|
279
|
+
false,
|
|
280
|
+
false,
|
|
281
|
+
() =>
|
|
282
|
+
coords(
|
|
283
|
+
columns,
|
|
284
|
+
node.geometry === "Point"
|
|
285
|
+
? 1
|
|
286
|
+
: node.geometry === "Polygon"
|
|
287
|
+
? 3
|
|
288
|
+
: 2
|
|
289
|
+
)
|
|
290
|
+
),
|
|
291
|
+
],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (node.form === "sum")
|
|
295
|
+
return {
|
|
296
|
+
kind: "union",
|
|
297
|
+
typeName: node.entity.label,
|
|
298
|
+
column: column(columns, "INTEGER"),
|
|
299
|
+
variants: node.variants.map((r) => {
|
|
300
|
+
const arm = nodes.get(r.id);
|
|
301
|
+
if (
|
|
302
|
+
arm?.kind !== "structural" ||
|
|
303
|
+
arm.structuralKind !== "value-variant" ||
|
|
304
|
+
arm.label === undefined
|
|
305
|
+
)
|
|
306
|
+
throw new Error("missing nominal arm");
|
|
307
|
+
const target = nodes.get(arm.value.id);
|
|
308
|
+
const body =
|
|
309
|
+
target?.kind === "type"
|
|
310
|
+
? {
|
|
311
|
+
kind: "object" as const,
|
|
312
|
+
typeName: node.entity.label,
|
|
313
|
+
members: [
|
|
314
|
+
slot(
|
|
315
|
+
"value",
|
|
316
|
+
arm.entity.id,
|
|
317
|
+
columns,
|
|
318
|
+
true,
|
|
319
|
+
false,
|
|
320
|
+
false,
|
|
321
|
+
() => value(arm.value, columns, next)
|
|
322
|
+
),
|
|
323
|
+
],
|
|
324
|
+
}
|
|
325
|
+
: value(arm.value, columns, next, node.entity.label);
|
|
326
|
+
return { label: arm.label, value: body };
|
|
327
|
+
}),
|
|
328
|
+
};
|
|
329
|
+
return {
|
|
330
|
+
kind: "object",
|
|
331
|
+
typeName: node.entity.label,
|
|
332
|
+
members: node.fields.map((r) => {
|
|
333
|
+
const f = nodes.get(r.id);
|
|
334
|
+
if (f?.kind !== "field")
|
|
335
|
+
throw new Error(`missing type member ${r.id}`);
|
|
336
|
+
return slot(
|
|
337
|
+
f.entity.label,
|
|
338
|
+
f.entity.sid,
|
|
339
|
+
columns,
|
|
340
|
+
f.required,
|
|
341
|
+
f.nullable,
|
|
342
|
+
false,
|
|
343
|
+
() => value(f.value, columns, next)
|
|
344
|
+
);
|
|
345
|
+
}),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (node?.kind !== "structural" || node.structuralKind !== "value-type")
|
|
349
|
+
throw new Error(`missing value type ${ref.id}`);
|
|
350
|
+
const e = node.expression;
|
|
351
|
+
switch (e.kind) {
|
|
352
|
+
case "default":
|
|
353
|
+
case "nullable":
|
|
354
|
+
return value(e.value, columns, next);
|
|
355
|
+
case "string":
|
|
356
|
+
case "enum":
|
|
357
|
+
return { kind: "string", column: column(columns, "TEXT") };
|
|
358
|
+
case "integer":
|
|
359
|
+
return { kind: "integer", column: column(columns, "INTEGER") };
|
|
360
|
+
case "boolean":
|
|
361
|
+
return { kind: "boolean", column: column(columns, "INTEGER") };
|
|
362
|
+
// IEEE bits retain negative zero; no text decode, no SQLite numeric coercion.
|
|
363
|
+
case "number":
|
|
364
|
+
return { kind: "number", column: column(columns, "BLOB") };
|
|
365
|
+
case "null":
|
|
366
|
+
return { kind: "null" };
|
|
367
|
+
case "literal": {
|
|
368
|
+
if (typeof e.value === "string")
|
|
369
|
+
return {
|
|
370
|
+
kind: "string",
|
|
371
|
+
column: column(columns, "TEXT"),
|
|
372
|
+
literal: e.value,
|
|
373
|
+
};
|
|
374
|
+
if (typeof e.value === "boolean")
|
|
375
|
+
return { kind: "boolean", column: column(columns, "INTEGER") };
|
|
376
|
+
throw new Error("SQLite layout: unsupported literal representation");
|
|
377
|
+
}
|
|
378
|
+
case "record": {
|
|
379
|
+
const typeName =
|
|
380
|
+
e.semantic?.kind === "value-object" ? e.semantic.name : nominalName;
|
|
381
|
+
const reference =
|
|
382
|
+
e.semantic?.kind === "address-reference" ||
|
|
383
|
+
e.semantic?.kind === "versioned-reference";
|
|
384
|
+
if (typeName === undefined && !reference)
|
|
385
|
+
throw new Error(
|
|
386
|
+
"SQLite layout: anonymous record needs an explicit nominal storage mapping"
|
|
387
|
+
);
|
|
388
|
+
const result: SqliteValue = {
|
|
389
|
+
kind: reference
|
|
390
|
+
? e.semantic!.kind === "versioned-reference"
|
|
391
|
+
? "versioned-reference"
|
|
392
|
+
: "reference"
|
|
393
|
+
: "object",
|
|
394
|
+
...(typeName === undefined ? {} : { typeName }),
|
|
395
|
+
members: e.fields.map((r) => {
|
|
396
|
+
const f = nodes.get(r.id);
|
|
397
|
+
if (f?.kind !== "structural" || f.structuralKind !== "value-field")
|
|
398
|
+
throw new Error(`missing record member ${r.id}`);
|
|
399
|
+
return slot(
|
|
400
|
+
f.name,
|
|
401
|
+
f.entity.id,
|
|
402
|
+
columns,
|
|
403
|
+
f.required,
|
|
404
|
+
isNullable(f.value),
|
|
405
|
+
false,
|
|
406
|
+
() => value(f.value, columns, next)
|
|
407
|
+
);
|
|
408
|
+
}),
|
|
409
|
+
};
|
|
410
|
+
if (e.semantic?.kind === "value-object" && e.semantic.geometry) {
|
|
411
|
+
const mark = (s: SqliteValue) => {
|
|
412
|
+
if (s.kind === "number") s.typeName = "geometry-number";
|
|
413
|
+
s.members?.forEach((m) => mark(m.value));
|
|
414
|
+
if (s.element) mark(s.element.value);
|
|
415
|
+
};
|
|
416
|
+
mark(result);
|
|
417
|
+
}
|
|
418
|
+
return result;
|
|
419
|
+
}
|
|
420
|
+
case "list":
|
|
421
|
+
case "map": {
|
|
422
|
+
const child = e.kind === "list" ? e.element : e.value;
|
|
423
|
+
return collection(e.kind === "list" ? "list" : "nominal-map", (c) =>
|
|
424
|
+
slot("element", ref.id, c, true, isNullable(child), false, () =>
|
|
425
|
+
value(child, c, next)
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
case "sum": {
|
|
430
|
+
// Descriptor-lowered named sums carry authored `type` fields in their
|
|
431
|
+
// structural arms, unlike nominal LawTypeNode variants. Preserve both.
|
|
432
|
+
if (e.semantic?.kind !== "value-object")
|
|
433
|
+
throw new Error("unsupported anonymous union storage");
|
|
434
|
+
const name = e.semantic.name;
|
|
435
|
+
return {
|
|
436
|
+
kind: "union",
|
|
437
|
+
typeName: name,
|
|
438
|
+
column: column(columns, "INTEGER"),
|
|
439
|
+
variants: e.variants.map((r) => {
|
|
440
|
+
const arm = nodes.get(r.id);
|
|
441
|
+
if (
|
|
442
|
+
arm?.kind !== "structural" ||
|
|
443
|
+
arm.structuralKind !== "value-variant"
|
|
444
|
+
)
|
|
445
|
+
throw new Error("missing sum arm");
|
|
446
|
+
const body = value(arm.value, columns, next, name);
|
|
447
|
+
const discriminator = body.members?.find((s) => s.name === "type")
|
|
448
|
+
?.value.literal;
|
|
449
|
+
if (typeof discriminator !== "string")
|
|
450
|
+
throw new Error("named union has no literal discriminator");
|
|
451
|
+
return { label: discriminator, value: body };
|
|
452
|
+
}),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
default:
|
|
456
|
+
throw new Error(
|
|
457
|
+
`SQLite layout: unsupported ${e.kind}; no JSON fallback`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const fields = [
|
|
462
|
+
slot("__type", "builtin:type", rootColumns, true, false, true, () => ({
|
|
463
|
+
kind: "string",
|
|
464
|
+
column: column(rootColumns, "TEXT"),
|
|
465
|
+
})),
|
|
466
|
+
];
|
|
467
|
+
for (const r of aggregate.fields) {
|
|
468
|
+
const f = nodes.get(r.id);
|
|
469
|
+
if (f?.kind !== "field") throw new Error(`missing aggregate field ${r.id}`);
|
|
470
|
+
if (onlyFieldSid !== undefined && f.entity.sid !== onlyFieldSid) continue;
|
|
471
|
+
if (
|
|
472
|
+
// Explicit top-level null is a different kernel Value representation;
|
|
473
|
+
// this slice only models null inside nominal records.
|
|
474
|
+
f.encrypted ||
|
|
475
|
+
f.blindIndexed ||
|
|
476
|
+
f.mergeDrivers.some(
|
|
477
|
+
(d) =>
|
|
478
|
+
d.kind !== "Lww" &&
|
|
479
|
+
d.kind !== "FirstWriterWins" &&
|
|
480
|
+
d.kind !== "FirstWriterCandidates" &&
|
|
481
|
+
d.kind !== "MapOf" &&
|
|
482
|
+
d.kind !== "AddWins" &&
|
|
483
|
+
d.kind !== "Surfaced"
|
|
484
|
+
)
|
|
485
|
+
) {
|
|
486
|
+
throw new Error(
|
|
487
|
+
`SQLite layout: unsupported storage/driver on ${f.entity.label} ` +
|
|
488
|
+
`(kind=${f.storageKind}; drivers=${f.mergeDrivers
|
|
489
|
+
.map((d) => d.kind)
|
|
490
|
+
.join("/")}; ` +
|
|
491
|
+
`nullable=${f.nullable}; encrypted=${f.encrypted}; blindIndexed=${f.blindIndexed})`
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
fields.push(
|
|
495
|
+
slot(
|
|
496
|
+
f.entity.label,
|
|
497
|
+
f.entity.sid,
|
|
498
|
+
rootColumns,
|
|
499
|
+
f.required,
|
|
500
|
+
f.nullable,
|
|
501
|
+
true,
|
|
502
|
+
() => {
|
|
503
|
+
if (f.storageKind === "set" || f.storageKind === "map") {
|
|
504
|
+
const type = nodes.get(f.value.id);
|
|
505
|
+
if (
|
|
506
|
+
type?.kind !== "structural" ||
|
|
507
|
+
type.structuralKind !== "value-type"
|
|
508
|
+
)
|
|
509
|
+
throw new Error("missing collection value type");
|
|
510
|
+
const e = type.expression;
|
|
511
|
+
const child =
|
|
512
|
+
e.kind === "list"
|
|
513
|
+
? e.element
|
|
514
|
+
: e.kind === "map"
|
|
515
|
+
? e.value
|
|
516
|
+
: undefined;
|
|
517
|
+
if (!child) throw new Error("unsupported collection shape");
|
|
518
|
+
if (
|
|
519
|
+
f.storageKind === "map" &&
|
|
520
|
+
!["string", "int", "bool", "json"].includes(f.mapValueKind ?? "")
|
|
521
|
+
)
|
|
522
|
+
throw new Error("unsupported driven map value kind");
|
|
523
|
+
return collection(f.storageKind, (columns) =>
|
|
524
|
+
slot(
|
|
525
|
+
"element",
|
|
526
|
+
f.entity.sid,
|
|
527
|
+
columns,
|
|
528
|
+
true,
|
|
529
|
+
false,
|
|
530
|
+
f.storageKind === "map",
|
|
531
|
+
() => value(child, columns, new Set())
|
|
532
|
+
)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
if (
|
|
536
|
+
// storedFieldValue lowers ordinary ID references to string. Atomic
|
|
537
|
+
// address/versioned refs retain their structural shape and still
|
|
538
|
+
// pass through value(), which refuses an unsupported mapping.
|
|
539
|
+
!["string", "int", "bool", "enum", "json", "ref"].includes(
|
|
540
|
+
f.storageKind ?? ""
|
|
541
|
+
)
|
|
542
|
+
)
|
|
543
|
+
throw new Error(`unsupported field storage kind ${f.storageKind}`);
|
|
544
|
+
return value(f.value, rootColumns, new Set());
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
);
|
|
548
|
+
if (f.mergeDrivers.some((d) => d.kind === "Surfaced")) {
|
|
549
|
+
fields.at(-1)!.surfaced = collection("list", (columns) => {
|
|
550
|
+
// observation stamp + principal + authored law + schema are independent
|
|
551
|
+
// of outer Field provenance. Six dedicated columns, never a winner-only view.
|
|
552
|
+
const s = slot(
|
|
553
|
+
"observation",
|
|
554
|
+
f.entity.sid,
|
|
555
|
+
columns,
|
|
556
|
+
true,
|
|
557
|
+
f.nullable,
|
|
558
|
+
true,
|
|
559
|
+
() => value(f.value, columns, new Set())
|
|
560
|
+
);
|
|
561
|
+
columns[s.metadata[4]!]!.type = "TEXT"; // authored_under, not was_contested
|
|
562
|
+
return s;
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const indexed = fields.find((f) => f.sid === indexFieldSid);
|
|
567
|
+
const hasReads = [...nodes.values()].some(
|
|
568
|
+
(n) =>
|
|
569
|
+
n.kind === "read" &&
|
|
570
|
+
(n.of.id === aggregate.entity.id ||
|
|
571
|
+
(n.readKind === "combined" && n.reads.id === aggregate.entity.id))
|
|
572
|
+
);
|
|
573
|
+
if (hasReads && onlyFieldSid === undefined) {
|
|
574
|
+
// Kernel archive metadata is retained, never translated to a tenant field.
|
|
575
|
+
fields.push(
|
|
576
|
+
slot(
|
|
577
|
+
"__archived",
|
|
578
|
+
"builtin:archived",
|
|
579
|
+
rootColumns,
|
|
580
|
+
false,
|
|
581
|
+
false,
|
|
582
|
+
true,
|
|
583
|
+
() => ({ kind: "integer", column: column(rootColumns, "INTEGER") })
|
|
584
|
+
)
|
|
585
|
+
);
|
|
586
|
+
for (const index of spatialIndexes) {
|
|
587
|
+
let members = fields;
|
|
588
|
+
let shape: SqliteValue | undefined;
|
|
589
|
+
for (const name of index.path) {
|
|
590
|
+
const member = members.find((f) => f.name === name);
|
|
591
|
+
if (!member)
|
|
592
|
+
throw new Error(
|
|
593
|
+
`spatial ${index.name}: unmapped path ${index.path.join(".")}`
|
|
594
|
+
);
|
|
595
|
+
shape = member.value;
|
|
596
|
+
members = shape.members ?? [];
|
|
597
|
+
}
|
|
598
|
+
if (
|
|
599
|
+
shape?.kind !== "object" ||
|
|
600
|
+
!shape.members?.some((m) => m.name === "coordinates")
|
|
601
|
+
)
|
|
602
|
+
throw new Error(
|
|
603
|
+
`spatial ${index.name}: path does not resolve to mapped geometry`
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (
|
|
608
|
+
!indexed ||
|
|
609
|
+
indexed.value.kind !== "string" ||
|
|
610
|
+
indexed.value.column === undefined
|
|
611
|
+
)
|
|
612
|
+
throw new Error("experimental page index requires a string field SID");
|
|
613
|
+
const indexColumn = indexed.value.column;
|
|
614
|
+
const queries =
|
|
615
|
+
onlyFieldSid === undefined
|
|
616
|
+
? planSqliteQueries(nodes, aggregate.entity.id, {
|
|
617
|
+
table,
|
|
618
|
+
wireType: aggregate.wireType,
|
|
619
|
+
fields,
|
|
620
|
+
})
|
|
621
|
+
: { plans: [], refusals: [] };
|
|
622
|
+
const maintained =
|
|
623
|
+
onlyFieldSid === undefined
|
|
624
|
+
? planSqliteCounts(nodes, aggregate.entity.id, {
|
|
625
|
+
table,
|
|
626
|
+
wireType: aggregate.wireType,
|
|
627
|
+
fields,
|
|
628
|
+
})
|
|
629
|
+
: { plans: [], refusals: [], snapshot: null };
|
|
630
|
+
const computed =
|
|
631
|
+
onlyFieldSid === undefined
|
|
632
|
+
? planSqliteComputations(nodes, aggregate.entity.id, {
|
|
633
|
+
table,
|
|
634
|
+
wireType: aggregate.wireType,
|
|
635
|
+
fields,
|
|
636
|
+
})
|
|
637
|
+
: { plans: [], refusals: [] };
|
|
638
|
+
const ddl =
|
|
639
|
+
tables
|
|
640
|
+
.map(
|
|
641
|
+
(t) =>
|
|
642
|
+
`CREATE TABLE ${t.name} (${t.columns
|
|
643
|
+
.map((c) => `${c.name} ${c.type}`)
|
|
644
|
+
.join(",")}, PRIMARY KEY(${
|
|
645
|
+
t.child ? "id,parent,key" : "id"
|
|
646
|
+
})) WITHOUT ROWID;`
|
|
647
|
+
)
|
|
648
|
+
.join("\n") +
|
|
649
|
+
`\nCREATE INDEX ${table}_page ON ${table}(c${indexColumn},id) WHERE c${indexed.presence}=2;` +
|
|
650
|
+
queries.plans.map((q) => "\n" + q.ddl).join("");
|
|
651
|
+
const counts = tables.map((t) => ({
|
|
652
|
+
name: t.name,
|
|
653
|
+
columns: t.columns.length,
|
|
654
|
+
}));
|
|
655
|
+
const maxColumns = Math.max(...counts.map((t) => t.columns));
|
|
656
|
+
const result = {
|
|
657
|
+
version: 2 as const,
|
|
658
|
+
aggregateSid,
|
|
659
|
+
wireType: aggregate.wireType,
|
|
660
|
+
table,
|
|
661
|
+
columns: rootColumns,
|
|
662
|
+
fields,
|
|
663
|
+
indexColumn,
|
|
664
|
+
spatialIndexes,
|
|
665
|
+
queryReads: queries.plans,
|
|
666
|
+
queryRefusals: queries.refusals,
|
|
667
|
+
countReads: maintained.plans,
|
|
668
|
+
countRefusals: maintained.refusals,
|
|
669
|
+
countSnapshot: maintained.snapshot,
|
|
670
|
+
computedReads: computed.plans,
|
|
671
|
+
computedRefusals: computed.refusals,
|
|
672
|
+
ddl,
|
|
673
|
+
budget: {
|
|
674
|
+
columnLimit,
|
|
675
|
+
tables: counts,
|
|
676
|
+
maxColumns,
|
|
677
|
+
totalColumns: counts.reduce((sum, t) => sum + t.columns, 0),
|
|
678
|
+
indexes: 1 + queries.plans.length,
|
|
679
|
+
computedSubstrate: {
|
|
680
|
+
declarations: computed.plans.length,
|
|
681
|
+
sqlTemporalReads: computed.plans.filter((p) => p.temporal).length,
|
|
682
|
+
sealedEngineReads: computed.plans.filter((p) => !p.temporal).length,
|
|
683
|
+
storedColumns: 0 as const,
|
|
684
|
+
queueTables: 0 as const,
|
|
685
|
+
},
|
|
686
|
+
queryIndexes: queries.plans.map((q) => ({
|
|
687
|
+
name: q.index,
|
|
688
|
+
keyColumns: q.keys.length + 1,
|
|
689
|
+
parameters: q.keys.length + 2,
|
|
690
|
+
})),
|
|
691
|
+
countSubstrate: {
|
|
692
|
+
declarations: maintained.plans.length,
|
|
693
|
+
tables: maintained.plans.length
|
|
694
|
+
? [{ name: "flat_counts", columns: 3 }]
|
|
695
|
+
: [],
|
|
696
|
+
snapshotColumns: maintained.snapshot?.columns ?? 0,
|
|
697
|
+
snapshotParameters: maintained.snapshot ? 1 : 0,
|
|
698
|
+
},
|
|
699
|
+
spatialSubstrate: {
|
|
700
|
+
declarations: spatialIndexes.length,
|
|
701
|
+
tables: spatialIndexes.length
|
|
702
|
+
? [
|
|
703
|
+
{ name: "spatial_rtree", columns: 5 },
|
|
704
|
+
{ name: "spatial_rowid", columns: 3 },
|
|
705
|
+
]
|
|
706
|
+
: [],
|
|
707
|
+
},
|
|
708
|
+
statementsPerPage: tables.length,
|
|
709
|
+
maxJoinTables: tables.length > 1 ? 2 : 1,
|
|
710
|
+
maxResultColumns: maxColumns,
|
|
711
|
+
maxInsertParameters: maxColumns,
|
|
712
|
+
warnings:
|
|
713
|
+
maxColumns > (options.warnColumns ?? 256)
|
|
714
|
+
? [
|
|
715
|
+
"Wide table: benchmark prepare, read and write costs; 256 is an advisory threshold, not a measured performance guarantee.",
|
|
716
|
+
]
|
|
717
|
+
: [],
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
return {
|
|
721
|
+
...result,
|
|
722
|
+
fingerprint: createHash("sha256")
|
|
723
|
+
.update(JSON.stringify(result))
|
|
724
|
+
.digest("hex"),
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/** A report of the REAL compiler closure. No graph serialization, law emission,
|
|
729
|
+
* tenant-specific name switches, or successful-looking budgets for blocked types.
|
|
730
|
+
* The single builtin-type index is a diagnostic probe, NOT tenant query planning.
|
|
731
|
+
*/
|
|
732
|
+
export function inspectSqliteLayouts(closure: readonly LawGraph[]) {
|
|
733
|
+
const nodes = new Map(
|
|
734
|
+
closure.flatMap((g) => g.nodes.map((n) => [n.entity.id, n] as const))
|
|
735
|
+
);
|
|
736
|
+
const domains = new Map<string, string[]>();
|
|
737
|
+
for (const g of closure)
|
|
738
|
+
for (const n of g.nodes)
|
|
739
|
+
if (n.kind === "aggregate") {
|
|
740
|
+
const owners = domains.get(n.entity.sid) ?? [];
|
|
741
|
+
if (!owners.includes(g.domain.entity.label))
|
|
742
|
+
owners.push(g.domain.entity.label);
|
|
743
|
+
domains.set(n.entity.sid, owners);
|
|
744
|
+
}
|
|
745
|
+
const aggregates = [...nodes.values()]
|
|
746
|
+
.filter((n) => n.kind === "aggregate")
|
|
747
|
+
.map((a) => {
|
|
748
|
+
const base = {
|
|
749
|
+
sid: a.entity.sid,
|
|
750
|
+
label: a.entity.label,
|
|
751
|
+
wireType: a.wireType,
|
|
752
|
+
domains: domains.get(a.entity.sid)!.sort(),
|
|
753
|
+
fields: a.fields.length,
|
|
754
|
+
};
|
|
755
|
+
try {
|
|
756
|
+
const layout = planWithNodes(nodes, a.entity.sid, "builtin:type");
|
|
757
|
+
return { ...base, status: "planned" as const, layout };
|
|
758
|
+
} catch (error) {
|
|
759
|
+
const fieldRefusals: { field: string; sid: string; reason: string }[] =
|
|
760
|
+
[];
|
|
761
|
+
let supportedFields = 0;
|
|
762
|
+
for (const r of a.fields) {
|
|
763
|
+
const f = nodes.get(r.id);
|
|
764
|
+
if (f?.kind !== "field") throw new Error(`missing field ${r.id}`);
|
|
765
|
+
try {
|
|
766
|
+
planWithNodes(
|
|
767
|
+
nodes,
|
|
768
|
+
a.entity.sid,
|
|
769
|
+
"builtin:type",
|
|
770
|
+
{},
|
|
771
|
+
f.entity.sid
|
|
772
|
+
);
|
|
773
|
+
supportedFields++;
|
|
774
|
+
} catch (e) {
|
|
775
|
+
fieldRefusals.push({
|
|
776
|
+
field: f.entity.label,
|
|
777
|
+
sid: f.entity.sid,
|
|
778
|
+
reason: (e as Error).message,
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
return {
|
|
783
|
+
...base,
|
|
784
|
+
status: "blocked" as const,
|
|
785
|
+
reason: (error as Error).message,
|
|
786
|
+
supportedFields,
|
|
787
|
+
fieldRefusals,
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
})
|
|
791
|
+
.sort((a, b) => a.wireType.localeCompare(b.wireType));
|
|
792
|
+
const planned = aggregates.filter((a) => a.status === "planned");
|
|
793
|
+
return {
|
|
794
|
+
version: 1,
|
|
795
|
+
kind: "experimental-sqlite-layout-inspection",
|
|
796
|
+
indexPolicy:
|
|
797
|
+
"builtin type / id, declared query expression indexes, maintained count totals and spatial indexes; see per-read coverage and runtime gates",
|
|
798
|
+
summary: {
|
|
799
|
+
domains: closure.length,
|
|
800
|
+
uniqueAggregates: aggregates.length,
|
|
801
|
+
planned: planned.length,
|
|
802
|
+
blocked: aggregates.length - planned.length,
|
|
803
|
+
maxPlannedColumns: Math.max(
|
|
804
|
+
0,
|
|
805
|
+
...planned.map((a) => a.layout.budget.maxColumns)
|
|
806
|
+
),
|
|
807
|
+
maxPlannedTables: Math.max(
|
|
808
|
+
0,
|
|
809
|
+
...planned.map((a) => a.layout.budget.tables.length)
|
|
810
|
+
),
|
|
811
|
+
allSupported: planned.length === aggregates.length,
|
|
812
|
+
spatialIndexes: planned.reduce(
|
|
813
|
+
(n, a) => n + a.layout.spatialIndexes.length,
|
|
814
|
+
0
|
|
815
|
+
),
|
|
816
|
+
},
|
|
817
|
+
aggregates,
|
|
818
|
+
readCoverage: inspectSqliteReads(
|
|
819
|
+
nodes,
|
|
820
|
+
planned.map((a) => a.layout)
|
|
821
|
+
),
|
|
822
|
+
};
|
|
823
|
+
}
|