@sixb/sqlite 0.1.7 → 0.1.9

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.
Files changed (68) hide show
  1. package/dist/action-run-storage.d.ts.map +1 -1
  2. package/dist/agents/rows.d.ts +0 -1
  3. package/dist/agents/rows.d.ts.map +1 -1
  4. package/dist/agents/runs.d.ts.map +1 -1
  5. package/dist/ai-cost-storage.d.ts.map +1 -1
  6. package/dist/ai-usage-storage.d.ts.map +1 -1
  7. package/dist/execution-storage.d.ts +3 -2
  8. package/dist/execution-storage.d.ts.map +1 -1
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1567 -430
  12. package/dist/index.js.map +25 -19
  13. package/dist/migrations.d.ts.map +1 -1
  14. package/dist/objects/index.d.ts +3 -0
  15. package/dist/objects/index.d.ts.map +1 -0
  16. package/dist/{object-query-compiler.d.ts → objects/query-compiler.d.ts} +16 -1
  17. package/dist/objects/query-compiler.d.ts.map +1 -0
  18. package/dist/objects/read-scope.d.ts +17 -0
  19. package/dist/objects/read-scope.d.ts.map +1 -0
  20. package/dist/objects/reader.d.ts +51 -0
  21. package/dist/objects/reader.d.ts.map +1 -0
  22. package/dist/objects/rows.d.ts +33 -0
  23. package/dist/objects/rows.d.ts.map +1 -0
  24. package/dist/objects/selected-reader.d.ts +9 -0
  25. package/dist/objects/selected-reader.d.ts.map +1 -0
  26. package/dist/{object-storage.d.ts → objects/storage.d.ts} +12 -9
  27. package/dist/objects/storage.d.ts.map +1 -0
  28. package/dist/pipeline-run-storage.d.ts.map +1 -1
  29. package/dist/share-grant-storage.d.ts +20 -0
  30. package/dist/share-grant-storage.d.ts.map +1 -0
  31. package/dist/share-session-storage.d.ts +19 -0
  32. package/dist/share-session-storage.d.ts.map +1 -0
  33. package/dist/storage-errors.d.ts +1 -0
  34. package/dist/storage-errors.d.ts.map +1 -1
  35. package/dist/sync-run-storage.d.ts.map +1 -1
  36. package/dist/transactions.d.ts +8 -0
  37. package/dist/transactions.d.ts.map +1 -1
  38. package/dist/workflow-run-storage.d.ts.map +1 -1
  39. package/package.json +4 -4
  40. package/src/action-run-storage.ts +6 -7
  41. package/src/agents/rows.ts +0 -2
  42. package/src/agents/runs.ts +3 -7
  43. package/src/ai-cost-groups.ts +3 -0
  44. package/src/ai-cost-storage.ts +11 -2
  45. package/src/ai-usage-storage.ts +1 -2
  46. package/src/execution-storage.ts +28 -7
  47. package/src/index.ts +22 -5
  48. package/src/migrations/035-share-grants.sql +66 -0
  49. package/src/migrations/036-share-sessions.sql +226 -0
  50. package/src/migrations/037-execution-requester-groups.sql +24 -0
  51. package/src/migrations.ts +9 -0
  52. package/src/objects/index.ts +2 -0
  53. package/src/{object-query-compiler.ts → objects/query-compiler.ts} +104 -54
  54. package/src/objects/read-scope.ts +316 -0
  55. package/src/objects/reader.ts +526 -0
  56. package/src/objects/rows.ts +123 -0
  57. package/src/objects/selected-reader.ts +119 -0
  58. package/src/objects/storage.ts +296 -0
  59. package/src/pipeline-run-storage.ts +2 -5
  60. package/src/share-grant-storage.ts +252 -0
  61. package/src/share-session-storage.ts +189 -0
  62. package/src/storage-errors.ts +4 -0
  63. package/src/sync-run-storage.ts +2 -5
  64. package/src/transactions.ts +32 -0
  65. package/src/workflow-run-storage.ts +1 -6
  66. package/dist/object-query-compiler.d.ts.map +0 -1
  67. package/dist/object-storage.d.ts.map +0 -1
  68. package/src/object-storage.ts +0 -709
package/dist/index.js CHANGED
@@ -2,18 +2,21 @@
2
2
  // src/index.ts
3
3
  import { AsyncLocalStorage } from "async_hooks";
4
4
  import { ProviderMaterializationTransactionLifecycle } from "@sixb/core/internal/ontology-storage-provider";
5
+ import {
6
+ createTransactionStorageProxy,
7
+ throwNestedStorageTransaction
8
+ } from "@sixb/core/internal/storage";
5
9
  import {
6
10
  createAgentOperationScope,
7
11
  createAuthOperationScope,
12
+ createObjectOperationScope,
8
13
  createOntologyOperationScope,
9
14
  createOperationScopedFacade,
10
15
  createStorageOperationScope,
11
16
  createWorkflowRunOperationScope
12
17
  } from "@sixb/core/internal/storage-operation-scope";
13
18
  import {
14
- createTransactionStorageProxy,
15
- StorageTransactionError,
16
- throwNestedStorageTransaction
19
+ StorageTransactionError
17
20
  } from "@sixb/core/storage";
18
21
 
19
22
  // src/action-run-storage.ts
@@ -23,12 +26,11 @@ import {
23
26
  } from "@sixb/core/internal/action-run-storage";
24
27
  import { assertActionRunExecution } from "@sixb/core/internal/action-run-storage-provider";
25
28
  import {
26
- ActionRunError,
27
29
  actionRunPhaseRecordsEqual,
28
30
  canRequeueActionRunAfterEnqueueFailure,
29
- finishActionRunPhase,
30
- isTerminalActionRun
31
- } from "@sixb/core/storage";
31
+ finishActionRunPhase
32
+ } from "@sixb/core/internal/storage";
33
+ import { ActionRunError, isTerminalActionRun } from "@sixb/core/storage";
32
34
 
33
35
  // src/migrations.ts
34
36
  import { Database } from "bun:sqlite";
@@ -3632,6 +3634,331 @@ CREATE INDEX idx_auth_device_authorizations_pending
3632
3634
  ON auth_device_authorizations(project_id, status, expires_at);
3633
3635
  `;
3634
3636
 
3637
+ // src/migrations/035-share-grants.sql
3638
+ var _035_share_grants_default = `CREATE TABLE share_grants (
3639
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
3640
+ id TEXT NOT NULL CHECK (length(trim(id)) > 0),
3641
+ definition_id TEXT NOT NULL CHECK (length(trim(definition_id)) > 0),
3642
+ target_object_type_id TEXT NOT NULL CHECK (length(trim(target_object_type_id)) > 0),
3643
+ target_primary_id TEXT NOT NULL CHECK (length(trim(target_primary_id)) > 0),
3644
+ issued_by_type TEXT NOT NULL CHECK (issued_by_type IN ('user', 'serviceAccount')),
3645
+ issued_by_id TEXT NOT NULL CHECK (length(trim(issued_by_id)) > 0),
3646
+ authority_version INTEGER NOT NULL CHECK (authority_version > 0),
3647
+ authority_snapshot TEXT NOT NULL CHECK (
3648
+ json_valid(authority_snapshot)
3649
+ AND json_type(authority_snapshot) = 'object'
3650
+ AND json_type(authority_snapshot, '$.version') = 'integer'
3651
+ AND json_extract(authority_snapshot, '$.version') = authority_version
3652
+ AND json_type(authority_snapshot, '$.access') = 'object'
3653
+ ),
3654
+ authority_digest TEXT NOT NULL CHECK (
3655
+ length(authority_digest) = 64
3656
+ AND authority_digest = lower(authority_digest)
3657
+ AND authority_digest NOT GLOB '*[^0-9a-f]*'
3658
+ ),
3659
+ token_hash TEXT NOT NULL CHECK (
3660
+ length(token_hash) = 64
3661
+ AND token_hash = lower(token_hash)
3662
+ AND token_hash NOT GLOB '*[^0-9a-f]*'
3663
+ ),
3664
+ destination_path TEXT NOT NULL CHECK (
3665
+ substr(destination_path, 1, 1) = '/'
3666
+ AND instr(destination_path, '?') = 0
3667
+ AND instr(destination_path, '#') = 0
3668
+ AND instr(destination_path, char(92)) = 0
3669
+ ),
3670
+ created_at TEXT NOT NULL,
3671
+ expires_at TEXT NOT NULL,
3672
+ revoked_at TEXT,
3673
+ revoked_by_type TEXT CHECK (
3674
+ revoked_by_type IS NULL OR revoked_by_type IN ('user', 'serviceAccount', 'system')
3675
+ ),
3676
+ revoked_by_id TEXT CHECK (revoked_by_id IS NULL OR length(trim(revoked_by_id)) > 0),
3677
+ PRIMARY KEY (project_id, id),
3678
+ UNIQUE (project_id, token_hash),
3679
+ CHECK (expires_at > created_at),
3680
+ CHECK (revoked_at IS NULL OR revoked_at >= created_at),
3681
+ CHECK (
3682
+ (revoked_at IS NULL AND revoked_by_type IS NULL AND revoked_by_id IS NULL)
3683
+ OR
3684
+ (revoked_at IS NOT NULL AND revoked_by_type IS NOT NULL AND revoked_by_id IS NOT NULL)
3685
+ )
3686
+ );
3687
+
3688
+ CREATE INDEX idx_share_grants_target
3689
+ ON share_grants (
3690
+ project_id,
3691
+ definition_id,
3692
+ target_object_type_id,
3693
+ target_primary_id,
3694
+ created_at DESC,
3695
+ id DESC
3696
+ );
3697
+
3698
+ CREATE INDEX idx_share_grants_project_created
3699
+ ON share_grants (project_id, created_at DESC, id DESC);
3700
+
3701
+ CREATE INDEX idx_share_grants_expiry
3702
+ ON share_grants (project_id, expires_at)
3703
+ WHERE revoked_at IS NULL;
3704
+ `;
3705
+
3706
+ // src/migrations/036-share-sessions.sql
3707
+ var _036_share_sessions_default = `-- Shared sessions and their execution provenance.
3708
+ CREATE TABLE share_sessions (
3709
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
3710
+ id TEXT NOT NULL CHECK (length(trim(id)) > 0),
3711
+ grant_id TEXT NOT NULL CHECK (length(trim(grant_id)) > 0),
3712
+ token_hash TEXT NOT NULL CHECK (
3713
+ length(token_hash) = 64
3714
+ AND token_hash = lower(token_hash)
3715
+ AND token_hash NOT GLOB '*[^0-9a-f]*'
3716
+ ),
3717
+ created_at TEXT NOT NULL,
3718
+ expires_at TEXT NOT NULL,
3719
+ absolute_expires_at TEXT NOT NULL,
3720
+ revoked_at TEXT,
3721
+ PRIMARY KEY (project_id, id),
3722
+ UNIQUE (project_id, id, grant_id),
3723
+ UNIQUE (project_id, token_hash),
3724
+ FOREIGN KEY (project_id, grant_id)
3725
+ REFERENCES share_grants (project_id, id),
3726
+ CHECK (expires_at > created_at),
3727
+ CHECK (absolute_expires_at > created_at),
3728
+ CHECK (expires_at <= absolute_expires_at),
3729
+ CHECK (revoked_at IS NULL OR revoked_at >= created_at)
3730
+ );
3731
+
3732
+ CREATE INDEX idx_share_sessions_grant
3733
+ ON share_sessions (project_id, grant_id, created_at DESC, id DESC);
3734
+
3735
+ CREATE INDEX idx_share_sessions_expiry
3736
+ ON share_sessions (project_id, expires_at)
3737
+ WHERE revoked_at IS NULL;
3738
+
3739
+ -- Persist shared-session provenance on request executions without fabricating a principal.
3740
+ CREATE TABLE executions_v2 (
3741
+ project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
3742
+ id TEXT NOT NULL CHECK (length(trim(id)) > 0),
3743
+ executor_kind TEXT NOT NULL CHECK (
3744
+ executor_kind IN (
3745
+ 'request', 'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow',
3746
+ 'agent', 'kernel'
3747
+ )
3748
+ ),
3749
+ executor_id TEXT NOT NULL CHECK (length(trim(executor_id)) > 0),
3750
+ source_kind TEXT NOT NULL CHECK (
3751
+ source_kind IN ('http', 'webhook', 'schedule', 'event', 'datasetVersion', 'execution')
3752
+ ),
3753
+ source_id TEXT NOT NULL CHECK (length(trim(source_id)) > 0),
3754
+ requested_by_user_id TEXT,
3755
+ requested_by_service_account_id TEXT,
3756
+ correlation_id TEXT NOT NULL CHECK (length(trim(correlation_id)) > 0),
3757
+ parent_execution_id TEXT,
3758
+ authority_kind TEXT NOT NULL CHECK (
3759
+ authority_kind IN ('principal', 'trustedPrimitive', 'delegated', 'kernel', 'disabled')
3760
+ ),
3761
+ authority_user_id TEXT,
3762
+ authority_service_account_id TEXT,
3763
+ authority_session_id TEXT,
3764
+ authority_access_token_id TEXT,
3765
+ authority_primitive_kind TEXT CHECK (
3766
+ authority_primitive_kind IS NULL
3767
+ OR authority_primitive_kind IN (
3768
+ 'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow'
3769
+ )
3770
+ ),
3771
+ authority_primitive_id TEXT,
3772
+ authority_kernel_operation TEXT CHECK (
3773
+ authority_kernel_operation IS NULL OR authority_kernel_operation = 'ontology.recover'
3774
+ ),
3775
+ authority_delegation_kind TEXT CHECK (
3776
+ authority_delegation_kind IS NULL OR authority_delegation_kind = 'share'
3777
+ ),
3778
+ authority_delegation_id TEXT,
3779
+ authority_delegation_session_id TEXT,
3780
+ created_at TEXT NOT NULL,
3781
+ PRIMARY KEY (project_id, id),
3782
+ FOREIGN KEY (project_id, parent_execution_id) REFERENCES executions (project_id, id),
3783
+ FOREIGN KEY (project_id, requested_by_user_id) REFERENCES auth_users (project_id, id),
3784
+ FOREIGN KEY (project_id, requested_by_service_account_id)
3785
+ REFERENCES auth_service_accounts (project_id, id),
3786
+ FOREIGN KEY (project_id, authority_user_id) REFERENCES auth_users (project_id, id),
3787
+ FOREIGN KEY (project_id, authority_service_account_id)
3788
+ REFERENCES auth_service_accounts (project_id, id),
3789
+ FOREIGN KEY (project_id, authority_session_id) REFERENCES auth_sessions (project_id, id),
3790
+ FOREIGN KEY (project_id, authority_access_token_id)
3791
+ REFERENCES auth_access_tokens (project_id, id),
3792
+ FOREIGN KEY (
3793
+ project_id, authority_delegation_session_id, authority_delegation_id
3794
+ ) REFERENCES share_sessions (project_id, id, grant_id),
3795
+ CHECK ((requested_by_user_id IS NOT NULL) + (requested_by_service_account_id IS NOT NULL) <= 1),
3796
+ CHECK (
3797
+ (source_kind = 'execution' AND parent_execution_id = source_id)
3798
+ OR (source_kind <> 'execution' AND parent_execution_id IS NULL)
3799
+ ),
3800
+ CHECK (
3801
+ (
3802
+ authority_kind = 'principal'
3803
+ AND (authority_user_id IS NOT NULL) + (authority_service_account_id IS NOT NULL) = 1
3804
+ AND (authority_session_id IS NOT NULL) + (authority_access_token_id IS NOT NULL) <= 1
3805
+ AND (authority_session_id IS NULL OR authority_user_id IS NOT NULL)
3806
+ AND authority_primitive_kind IS NULL
3807
+ AND authority_primitive_id IS NULL
3808
+ AND authority_kernel_operation IS NULL
3809
+ AND authority_delegation_kind IS NULL
3810
+ AND authority_delegation_id IS NULL
3811
+ AND authority_delegation_session_id IS NULL
3812
+ )
3813
+ OR (
3814
+ authority_kind = 'trustedPrimitive'
3815
+ AND authority_user_id IS NULL
3816
+ AND authority_service_account_id IS NULL
3817
+ AND authority_session_id IS NULL
3818
+ AND authority_access_token_id IS NULL
3819
+ AND authority_primitive_kind IS NOT NULL
3820
+ AND authority_primitive_id IS NOT NULL
3821
+ AND length(trim(authority_primitive_id)) > 0
3822
+ AND authority_kernel_operation IS NULL
3823
+ AND authority_delegation_kind IS NULL
3824
+ AND authority_delegation_id IS NULL
3825
+ AND authority_delegation_session_id IS NULL
3826
+ )
3827
+ OR (
3828
+ authority_kind = 'delegated'
3829
+ AND authority_user_id IS NULL
3830
+ AND authority_service_account_id IS NULL
3831
+ AND authority_session_id IS NULL
3832
+ AND authority_access_token_id IS NULL
3833
+ AND authority_primitive_kind IS NULL
3834
+ AND authority_primitive_id IS NULL
3835
+ AND authority_kernel_operation IS NULL
3836
+ AND authority_delegation_kind IS NOT NULL
3837
+ AND authority_delegation_kind = 'share'
3838
+ AND authority_delegation_id IS NOT NULL
3839
+ AND length(trim(authority_delegation_id)) > 0
3840
+ AND authority_delegation_session_id IS NOT NULL
3841
+ AND length(trim(authority_delegation_session_id)) > 0
3842
+ )
3843
+ OR (
3844
+ authority_kind = 'kernel'
3845
+ AND authority_user_id IS NULL
3846
+ AND authority_service_account_id IS NULL
3847
+ AND authority_session_id IS NULL
3848
+ AND authority_access_token_id IS NULL
3849
+ AND authority_primitive_kind IS NULL
3850
+ AND authority_primitive_id IS NULL
3851
+ AND authority_kernel_operation IS NOT NULL
3852
+ AND authority_delegation_kind IS NULL
3853
+ AND authority_delegation_id IS NULL
3854
+ AND authority_delegation_session_id IS NULL
3855
+ )
3856
+ OR (
3857
+ authority_kind = 'disabled'
3858
+ AND authority_user_id IS NULL
3859
+ AND authority_service_account_id IS NULL
3860
+ AND authority_session_id IS NULL
3861
+ AND authority_access_token_id IS NULL
3862
+ AND authority_primitive_kind IS NULL
3863
+ AND authority_primitive_id IS NULL
3864
+ AND authority_kernel_operation IS NULL
3865
+ AND authority_delegation_kind IS NULL
3866
+ AND authority_delegation_id IS NULL
3867
+ AND authority_delegation_session_id IS NULL
3868
+ )
3869
+ ),
3870
+ CHECK (
3871
+ (
3872
+ executor_kind = 'request'
3873
+ AND source_kind = 'http'
3874
+ AND executor_id = source_id
3875
+ AND (
3876
+ (
3877
+ authority_kind = 'principal'
3878
+ AND requested_by_user_id IS authority_user_id
3879
+ AND requested_by_service_account_id IS authority_service_account_id
3880
+ )
3881
+ OR (
3882
+ authority_kind IN ('delegated', 'disabled')
3883
+ AND requested_by_user_id IS NULL
3884
+ AND requested_by_service_account_id IS NULL
3885
+ )
3886
+ )
3887
+ )
3888
+ OR (
3889
+ executor_kind IN ('action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow')
3890
+ AND authority_kind = 'trustedPrimitive'
3891
+ AND executor_kind = authority_primitive_kind
3892
+ )
3893
+ OR (
3894
+ executor_kind = 'agent'
3895
+ AND source_kind = 'execution'
3896
+ AND (
3897
+ (
3898
+ authority_kind = 'principal'
3899
+ AND authority_service_account_id IS NOT NULL
3900
+ AND authority_session_id IS NULL
3901
+ AND authority_access_token_id IS NULL
3902
+ )
3903
+ OR (
3904
+ authority_kind = 'principal'
3905
+ AND authority_user_id IS NOT NULL
3906
+ AND (authority_session_id IS NOT NULL) + (authority_access_token_id IS NOT NULL) = 1
3907
+ )
3908
+ OR authority_kind = 'disabled'
3909
+ )
3910
+ )
3911
+ OR (executor_kind = 'kernel' AND authority_kind = 'kernel')
3912
+ )
3913
+ );
3914
+
3915
+ INSERT INTO executions_v2 (
3916
+ project_id, id, executor_kind, executor_id, source_kind, source_id,
3917
+ requested_by_user_id, requested_by_service_account_id, correlation_id, parent_execution_id,
3918
+ authority_kind, authority_user_id, authority_service_account_id, authority_session_id,
3919
+ authority_access_token_id, authority_primitive_kind, authority_primitive_id,
3920
+ authority_kernel_operation, authority_delegation_kind, authority_delegation_id,
3921
+ authority_delegation_session_id, created_at
3922
+ )
3923
+ SELECT
3924
+ project_id, id, executor_kind, executor_id, source_kind, source_id,
3925
+ requested_by_user_id, requested_by_service_account_id, correlation_id, parent_execution_id,
3926
+ authority_kind, authority_user_id, authority_service_account_id, authority_session_id,
3927
+ authority_access_token_id, authority_primitive_kind, authority_primitive_id,
3928
+ authority_kernel_operation, NULL, NULL, NULL, created_at
3929
+ FROM executions;
3930
+
3931
+ DROP TABLE executions;
3932
+ ALTER TABLE executions_v2 RENAME TO executions;
3933
+ `;
3934
+
3935
+ // src/migrations/037-execution-requester-groups.sql
3936
+ var _037_execution_requester_groups_default = `ALTER TABLE executions ADD COLUMN requester_group_ids TEXT NOT NULL DEFAULT '[]';
3937
+
3938
+ -- Preserve each admitted snapshot, including an intentionally empty one. Executions without
3939
+ -- their own historical snapshot inherit from their parent; historical roots remain unknown/empty.
3940
+ WITH RECURSIVE attribution(project_id, id, groups) AS (
3941
+ SELECT e.project_id, e.id, COALESCE(w.requester_group_ids, a.requester_group_ids, '[]')
3942
+ FROM executions e
3943
+ LEFT JOIN workflow_runs w ON w.project_id = e.project_id AND w.execution_id = e.id
3944
+ LEFT JOIN agent_runs a ON a.project_id = e.project_id AND a.execution_id = e.id
3945
+ WHERE e.parent_execution_id IS NULL
3946
+ UNION ALL
3947
+ SELECT e.project_id, e.id, COALESCE(w.requester_group_ids, a.requester_group_ids, p.groups)
3948
+ FROM executions e
3949
+ JOIN attribution p ON p.project_id = e.project_id AND p.id = e.parent_execution_id
3950
+ LEFT JOIN workflow_runs w ON w.project_id = e.project_id AND w.execution_id = e.id
3951
+ LEFT JOIN agent_runs a ON a.project_id = e.project_id AND a.execution_id = e.id
3952
+ )
3953
+ UPDATE executions SET requester_group_ids = (
3954
+ SELECT groups FROM attribution
3955
+ WHERE attribution.project_id = executions.project_id AND attribution.id = executions.id
3956
+ );
3957
+
3958
+ ALTER TABLE workflow_runs DROP COLUMN requester_group_ids;
3959
+ ALTER TABLE agent_runs DROP COLUMN requester_group_ids;
3960
+ `;
3961
+
3635
3962
  // src/migrations.ts
3636
3963
  var MIGRATIONS_TABLE_SQL = `
3637
3964
  CREATE TABLE IF NOT EXISTS sixb_migrations (
@@ -3700,7 +4027,10 @@ var sqliteStorageMigrations = defineMigrations({
3700
4027
  sqliteSql("031-subagent-runs", _031_subagent_runs_default),
3701
4028
  sqliteSql("032-conversation-run-spec", _032_conversation_run_spec_default),
3702
4029
  sqliteSql("033-retire-agent-definitions", _033_retire_agent_definitions_default),
3703
- sqliteSql("034-device-authorizations", _034_device_authorizations_default)
4030
+ sqliteSql("034-device-authorizations", _034_device_authorizations_default),
4031
+ sqliteSql("035-share-grants", _035_share_grants_default),
4032
+ sqliteSql("036-share-sessions", _036_share_sessions_default),
4033
+ sqliteSql("037-execution-requester-groups", _037_execution_requester_groups_default)
3704
4034
  ]
3705
4035
  });
3706
4036
  function sqliteStoragePath(basePath) {
@@ -3879,6 +4209,7 @@ function closeSqliteStoreConnection(connection) {
3879
4209
  }
3880
4210
  }
3881
4211
  var activeImmediateTransactions = new WeakSet;
4212
+ var activeDeferredReadTransactions = new WeakSet;
3882
4213
  function runImmediateTransaction(db, run) {
3883
4214
  if (activeImmediateTransactions.has(db)) {
3884
4215
  return run();
@@ -3916,6 +4247,26 @@ async function runImmediateTransactionAsync(db, run) {
3916
4247
  activeImmediateTransactions.delete(db);
3917
4248
  }
3918
4249
  }
4250
+ function runDeferredReadTransaction(db, run) {
4251
+ if (activeImmediateTransactions.has(db) || activeDeferredReadTransactions.has(db)) {
4252
+ return run();
4253
+ }
4254
+ if (db.inTransaction) {
4255
+ throw new Error("[SixbSqlite] Selected object reads cannot join an unverified SQLite transaction.");
4256
+ }
4257
+ db.run("BEGIN DEFERRED");
4258
+ activeDeferredReadTransactions.add(db);
4259
+ try {
4260
+ const result = run();
4261
+ db.run("COMMIT");
4262
+ return result;
4263
+ } catch (error) {
4264
+ rollbackQuietly2(db);
4265
+ throw error;
4266
+ } finally {
4267
+ activeDeferredReadTransactions.delete(db);
4268
+ }
4269
+ }
3919
4270
  function rollbackQuietly2(db) {
3920
4271
  try {
3921
4272
  db.run("ROLLBACK");
@@ -4388,7 +4739,6 @@ function rowToRunRecord(row) {
4388
4739
  id: row.id,
4389
4740
  projectId: row.project_id,
4390
4741
  executionId: row.execution_id,
4391
- requesterGroupIds: JSON.parse(row.requester_group_ids),
4392
4742
  status: row.status,
4393
4743
  modelId: row.model_id ?? undefined,
4394
4744
  finishReason: coerceAgentRunFinishReason(row.finish_reason),
@@ -4602,6 +4952,9 @@ import {
4602
4952
  function isUniqueConstraintError2(error) {
4603
4953
  return error instanceof Error && error.message.includes("UNIQUE constraint failed");
4604
4954
  }
4955
+ function isForeignKeyConstraintError(error) {
4956
+ return error instanceof Error && error.message.includes("FOREIGN KEY constraint failed");
4957
+ }
4605
4958
 
4606
4959
  // src/agents/messages.ts
4607
4960
  class SqliteAgentMessageStore {
@@ -4731,7 +5084,6 @@ import {
4731
5084
  assertSubagentRunResult,
4732
5085
  subagentRunMatchesCreateInput
4733
5086
  } from "@sixb/core/internal/agent-run-storage-provider";
4734
- import { normalizeRequesterGroupIds } from "@sixb/core/internal/auth";
4735
5087
  import { serializeSixbFailure } from "@sixb/core/internal/errors";
4736
5088
  import {
4737
5089
  AGENT_RUN_FAILURE_CODES as AGENT_RUN_FAILURE_CODES2,
@@ -4826,12 +5178,11 @@ class SqliteAgentRunStore {
4826
5178
  thread_id,
4827
5179
  trigger_message_id,
4828
5180
  spec,
4829
- requester_group_ids,
4830
5181
  status,
4831
5182
  attempt,
4832
5183
  created_at
4833
- ) VALUES (?, ?, ?, 'conversation', ?, ?, ?, ?, 'queued', 0, ?)
4834
- `).run(input.projectId, input.id, input.executionId, input.threadId, input.triggerMessageId, JSON.stringify(input.spec), JSON.stringify(normalizeRequesterGroupIds(input.requesterGroupIds)), createdAt.toISOString());
5184
+ ) VALUES (?, ?, ?, 'conversation', ?, ?, ?, 'queued', 0, ?)
5185
+ `).run(input.projectId, input.id, input.executionId, input.threadId, input.triggerMessageId, JSON.stringify(input.spec), createdAt.toISOString());
4835
5186
  } catch (error) {
4836
5187
  if (isUniqueConstraintError2(error)) {
4837
5188
  throw new AgentStorageError3("duplicate_id", `[SixbSqlite] Agent run '${input.id}' already exists for project '${input.projectId}'.`);
@@ -4884,9 +5235,9 @@ class SqliteAgentRunStore {
4884
5235
  this.db.query(`
4885
5236
  INSERT INTO agent_runs (
4886
5237
  project_id, id, execution_id, kind, parent_run_id, spawn_key, spec,
4887
- requester_group_ids, status, attempt, created_at
4888
- ) VALUES (?, ?, ?, 'subagent', ?, ?, ?, ?, 'queued', 0, ?)
4889
- `).run(input.projectId, input.id, input.executionId, input.parentRunId, input.spawnKey, JSON.stringify(input.spec), parentRow.requester_group_ids, (input.createdAt ?? new Date).toISOString());
5238
+ status, attempt, created_at
5239
+ ) VALUES (?, ?, ?, 'subagent', ?, ?, ?, 'queued', 0, ?)
5240
+ `).run(input.projectId, input.id, input.executionId, input.parentRunId, input.spawnKey, JSON.stringify(input.spec), (input.createdAt ?? new Date).toISOString());
4890
5241
  } catch (error) {
4891
5242
  if (isUniqueConstraintError2(error)) {
4892
5243
  throw new AgentStorageError3("duplicate_id", `[SixbSqlite] Subagent run '${input.id}' conflicts with an existing durable spawn.`);
@@ -5174,6 +5525,7 @@ import {
5174
5525
  aiModelCallCostDetails,
5175
5526
  aiModelCallCostMatchesUsage,
5176
5527
  buildAiAccountingOverviewFromFragments,
5528
+ directModelCallAttribution,
5177
5529
  normalizeAiAccountingQuery,
5178
5530
  normalizeAiModelCallAccountingQuery as normalizeAiModelCallAccountingQuery2,
5179
5531
  normalizeAiModelCallCostRecord,
@@ -5214,6 +5566,7 @@ WITH filtered AS (
5214
5566
  SELECT COALESCE(root_agent.execution_id, usage.execution_id) AS root_execution_id,
5215
5567
  usage.execution_id, usage.provider_id, usage.requested_model_id, usage.occurred_at,
5216
5568
  usage.total_tokens,
5569
+ execution.executor_kind, execution.executor_id, execution.authority_primitive_id AS primitive_id,
5217
5570
  COALESCE(cost.status, 'unvalued') AS valuation_status,
5218
5571
  cost.currency AS amount_currency, cost.amount_nanos,
5219
5572
  root_agent.id AS root_agent_run_id,
@@ -5226,6 +5579,7 @@ WITH filtered AS (
5226
5579
  workflow_node.workflow_id AS workflow_id,
5227
5580
  workflow_node.workflow_run_id AS workflow_run_id
5228
5581
  FROM ai_model_call_usage AS usage
5582
+ JOIN executions AS execution ON execution.project_id = usage.project_id AND execution.id = usage.execution_id
5229
5583
  LEFT JOIN agent_runs AS direct_agent
5230
5584
  ON direct_agent.project_id = usage.project_id AND direct_agent.execution_id = usage.execution_id
5231
5585
  LEFT JOIN agent_runs AS root_agent
@@ -5263,6 +5617,7 @@ SELECT filtered.root_execution_id, execution_id, provider_id, requested_model_id
5263
5617
  CAST(SUM((amount_nanos / 1000000) % 1000000) AS TEXT) AS amount_middle,
5264
5618
  CAST(SUM(amount_nanos % 1000000) AS TEXT) AS amount_low,
5265
5619
  MAX(root_agent_run_id) AS root_agent_run_id,
5620
+ MAX(executor_kind) AS executor_kind, MAX(executor_id) AS executor_id, MAX(primitive_id) AS primitive_id,
5266
5621
  MAX(root_thread_id) AS root_thread_id,
5267
5622
  MAX(direct_kind) AS direct_kind,
5268
5623
  MAX(direct_run_id) AS direct_run_id,
@@ -5277,12 +5632,11 @@ GROUP BY filtered.root_execution_id, execution_id, provider_id, requested_model_
5277
5632
  `;
5278
5633
 
5279
5634
  // src/ai-usage-storage.ts
5635
+ import { assertAiUsageExecutionId, normalizeAiModelCallRecord } from "@sixb/core/internal/storage";
5280
5636
  import { isModelReasoning, normalizeModelProviderIds } from "@sixb/core/models";
5281
5637
  import {
5282
5638
  AiUsageStorageError,
5283
- aggregateAiModelCallUsage,
5284
- assertAiUsageExecutionId,
5285
- normalizeAiModelCallRecord
5639
+ aggregateAiModelCallUsage
5286
5640
  } from "@sixb/core/storage";
5287
5641
  class SqliteAiUsageStorage {
5288
5642
  connection;
@@ -5605,6 +5959,7 @@ var SQLITE_ACCOUNTING_LIST_PAGE_SQL = `
5605
5959
  workflow_agent.node_run_id AS attribution_node_run_id,
5606
5960
  workflow_node.workflow_id AS attribution_workflow_id,
5607
5961
  workflow_node.workflow_run_id AS attribution_workflow_run_id,
5962
+ execution.executor_kind, execution.executor_id, execution.authority_primitive_id AS primitive_id,
5608
5963
  COALESCE((
5609
5964
  SELECT json_group_array(group_id) FROM (
5610
5965
  SELECT group_id FROM ai_model_call_usage_groups
@@ -5621,6 +5976,7 @@ var SQLITE_ACCOUNTING_LIST_PAGE_SQL = `
5621
5976
  cost.details AS cost_details,
5622
5977
  cost.rated_at AS cost_rated_at
5623
5978
  FROM ai_model_call_usage AS usage
5979
+ JOIN executions AS execution ON execution.project_id = usage.project_id AND execution.id = usage.execution_id
5624
5980
  LEFT JOIN agent_runs AS direct_agent
5625
5981
  ON direct_agent.project_id = usage.project_id
5626
5982
  AND direct_agent.execution_id = usage.execution_id
@@ -5655,7 +6011,9 @@ var SQLITE_ACCOUNTING_OVERVIEW_SQL = `
5655
6011
  ELSE NULL
5656
6012
  END AS attribution_agent_kind,
5657
6013
  workflow_node.node_id AS attribution_agent_step_id,
5658
- workflow_node.workflow_id AS attribution_workflow_id,
6014
+ COALESCE(workflow_node.workflow_id,
6015
+ CASE WHEN execution.executor_kind = 'workflow' THEN execution.authority_primitive_id END
6016
+ ) AS attribution_workflow_id,
5659
6017
  CASE
5660
6018
  WHEN cost.status = 'rated' THEN 'rated'
5661
6019
  WHEN cost.status = 'unpriceable' THEN 'unpriceable'
@@ -5664,6 +6022,7 @@ var SQLITE_ACCOUNTING_OVERVIEW_SQL = `
5664
6022
  CASE WHEN cost.status = 'rated' THEN cost.currency ELSE NULL END AS amount_currency,
5665
6023
  CASE WHEN cost.status = 'rated' THEN cost.amount_nanos ELSE NULL END AS amount_nanos
5666
6024
  FROM ai_model_call_usage AS usage
6025
+ JOIN executions AS execution ON execution.project_id = usage.project_id AND execution.id = usage.execution_id
5667
6026
  LEFT JOIN agent_runs AS direct_agent
5668
6027
  ON direct_agent.project_id = usage.project_id
5669
6028
  AND direct_agent.execution_id = usage.execution_id
@@ -5795,7 +6154,7 @@ function sqliteAggregateAmount(row) {
5795
6154
  function accountingItemFromRow(row) {
5796
6155
  const usage = usageFromRow2(row, parseStringArray(row.requester_group_ids));
5797
6156
  const cost = row.cost_status === null ? undefined : costFromJoinedRow(row);
5798
- const attribution = row.attribution_kind === null ? undefined : row.attribution_kind === "agent" ? {
6157
+ const attribution = row.attribution_kind === null ? directModelCallAttribution(row.executor_kind, row.executor_id, row.primitive_id) : row.attribution_kind === "agent" ? {
5799
6158
  kind: "agent",
5800
6159
  agentRunId: required(row.attribution_agent_run_id),
5801
6160
  threadId: required(row.attribution_thread_id)
@@ -7207,8 +7566,8 @@ class SqliteAuthAccessTokenStore {
7207
7566
  }
7208
7567
  function assertSubjectExists(db, projectId, subjectType, subjectId) {
7209
7568
  if (subjectType === "user") {
7210
- const row2 = db.query("SELECT * FROM auth_users WHERE project_id = ? AND id = ?").get(projectId, subjectId);
7211
- if (row2)
7569
+ const row = db.query("SELECT * FROM auth_users WHERE project_id = ? AND id = ?").get(projectId, subjectId);
7570
+ if (row)
7212
7571
  return;
7213
7572
  throw new AuthStorageError2("missing_user", `[Sixb] User '${subjectId}' not found for project '${projectId}'.`);
7214
7573
  }
@@ -8339,7 +8698,7 @@ class SqliteAuthStorage {
8339
8698
  }
8340
8699
  this.consumeOidcAttempt(input, completedAt, projectId);
8341
8700
  if (!userRow) {
8342
- const user2 = this.insertUser({
8701
+ const user = this.insertUser({
8343
8702
  id: newUserId ?? assertNonEmpty(input.newUserId, "User id"),
8344
8703
  projectId,
8345
8704
  email,
@@ -8348,14 +8707,14 @@ class SqliteAuthStorage {
8348
8707
  createdAt: completedAt
8349
8708
  });
8350
8709
  userRow = {
8351
- project_id: user2.projectId,
8352
- id: user2.id,
8353
- email: user2.email,
8354
- display_name: user2.displayName ?? null,
8355
- avatar_url: user2.avatarUrl ?? null,
8356
- status: user2.status,
8357
- created_at: toIso(user2.createdAt),
8358
- updated_at: toIso(user2.updatedAt)
8710
+ project_id: user.projectId,
8711
+ id: user.id,
8712
+ email: user.email,
8713
+ display_name: user.displayName ?? null,
8714
+ avatar_url: user.avatarUrl ?? null,
8715
+ status: user.status,
8716
+ created_at: toIso(user.createdAt),
8717
+ updated_at: toIso(user.updatedAt)
8359
8718
  };
8360
8719
  }
8361
8720
  const user = rowToUserRecord(userRow);
@@ -9073,23 +9432,24 @@ function assertChanged(changes, label, id) {
9073
9432
  import {
9074
9433
  executionRecordFromStorageRow,
9075
9434
  executionRecordToStorageRow,
9076
- normalizeExecutionRecord,
9077
- validateExecutionRecordReferences
9435
+ prepareExecutionRecord
9078
9436
  } from "@sixb/core/internal/execution-storage";
9079
9437
  import { ExecutionStorageError } from "@sixb/core/storage";
9080
9438
 
9081
9439
  class SqliteExecutionStorage {
9082
9440
  db;
9083
9441
  auth;
9084
- constructor(db, auth) {
9442
+ shareSessions;
9443
+ constructor(db, auth, shareSessions) {
9085
9444
  this.db = db;
9086
9445
  this.auth = auth;
9446
+ this.shareSessions = shareSessions;
9087
9447
  }
9088
9448
  async create(input) {
9089
- const record = normalizeExecutionRecord(input);
9090
- await validateExecutionRecordReferences(record, {
9449
+ const record = await prepareExecutionRecord(input, {
9091
9450
  auth: this.auth,
9092
- getExecution: (params) => this.getById(params)
9451
+ getExecution: (params) => this.getById(params),
9452
+ getShareSession: (params) => this.shareSessions.getById(params)
9093
9453
  });
9094
9454
  const row = executionRecordToStorageRow(record);
9095
9455
  try {
@@ -9103,6 +9463,7 @@ class SqliteExecutionStorage {
9103
9463
  source_id,
9104
9464
  requested_by_user_id,
9105
9465
  requested_by_service_account_id,
9466
+ requester_group_ids,
9106
9467
  correlation_id,
9107
9468
  parent_execution_id,
9108
9469
  authority_kind,
@@ -9113,9 +9474,12 @@ class SqliteExecutionStorage {
9113
9474
  authority_primitive_kind,
9114
9475
  authority_primitive_id,
9115
9476
  authority_kernel_operation,
9477
+ authority_delegation_kind,
9478
+ authority_delegation_id,
9479
+ authority_delegation_session_id,
9116
9480
  created_at
9117
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9118
- `).run(row.projectId, row.id, row.executorKind, row.executorId, row.sourceKind, row.sourceId, row.requestedByUserId, row.requestedByServiceAccountId, row.correlationId, row.parentExecutionId, row.authorityKind, row.authorityUserId, row.authorityServiceAccountId, row.authoritySessionId, row.authorityAccessTokenId, row.authorityPrimitiveKind, row.authorityPrimitiveId, row.authorityKernelOperation, row.createdAt.toISOString());
9481
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9482
+ `).run(row.projectId, row.id, row.executorKind, row.executorId, row.sourceKind, row.sourceId, row.requestedByUserId, row.requestedByServiceAccountId, JSON.stringify(row.requesterGroupIds), row.correlationId, row.parentExecutionId, row.authorityKind, row.authorityUserId, row.authorityServiceAccountId, row.authoritySessionId, row.authorityAccessTokenId, row.authorityPrimitiveKind, row.authorityPrimitiveId, row.authorityKernelOperation, row.authorityDelegationKind, row.authorityDelegationId, row.authorityDelegationSessionId, row.createdAt.toISOString());
9119
9483
  } catch (error) {
9120
9484
  if (isDuplicateExecutionError(error)) {
9121
9485
  throw new ExecutionStorageError("duplicate_execution", `[SixbSqlite] Execution '${record.id}' already exists in project '${record.projectId}'.`, { cause: error });
@@ -9143,6 +9507,7 @@ function toStorageRow(row) {
9143
9507
  sourceId: row.source_id,
9144
9508
  requestedByUserId: row.requested_by_user_id,
9145
9509
  requestedByServiceAccountId: row.requested_by_service_account_id,
9510
+ requesterGroupIds: JSON.parse(row.requester_group_ids),
9146
9511
  correlationId: row.correlation_id,
9147
9512
  parentExecutionId: row.parent_execution_id,
9148
9513
  authorityKind: row.authority_kind,
@@ -9153,6 +9518,9 @@ function toStorageRow(row) {
9153
9518
  authorityPrimitiveKind: row.authority_primitive_kind,
9154
9519
  authorityPrimitiveId: row.authority_primitive_id,
9155
9520
  authorityKernelOperation: row.authority_kernel_operation,
9521
+ authorityDelegationKind: row.authority_delegation_kind,
9522
+ authorityDelegationId: row.authority_delegation_id,
9523
+ authorityDelegationSessionId: row.authority_delegation_session_id,
9156
9524
  createdAt: new Date(row.created_at)
9157
9525
  };
9158
9526
  }
@@ -9160,56 +9528,62 @@ function isDuplicateExecutionError(error) {
9160
9528
  return error instanceof Error && error.message.includes("UNIQUE constraint failed: executions.project_id, executions.id");
9161
9529
  }
9162
9530
 
9163
- // src/object-storage.ts
9164
- import { linkBatchKey, objectBatchKey } from "@sixb/core/storage";
9165
-
9166
- // src/object-query-compiler.ts
9531
+ // src/objects/query-compiler.ts
9167
9532
  import {
9168
9533
  ObjectQueryExecutionError
9169
9534
  } from "@sixb/core";
9170
9535
  var PAGE_TOKEN_PREFIX = "keyset:";
9171
- var exactContext = { probeLimit: false };
9172
9536
  var DEFAULT_EXPANSION_FANOUT = 1000;
9173
9537
  function compileObjectQuery(projectId, query, options = {}) {
9174
- const ctx = { probeLimit: options.includeTotal === false };
9175
- return compileObjectQueryInternal(projectId, query, ctx);
9538
+ const source = options.source ?? DEFAULT_OBJECT_QUERY_SOURCE;
9539
+ const ctx = { probeLimit: options.includeTotal === false, source };
9540
+ return source.wrapQuery(compileObjectQueryInternal(projectId, query, ctx));
9541
+ }
9542
+ var DEFAULT_OBJECT_QUERY_SOURCE = {
9543
+ objectsTable: "objects",
9544
+ linksTable: "links",
9545
+ wrapStatement: (sql, args = []) => ({ sql, args: [...args] }),
9546
+ wrapQuery: (query) => query
9547
+ };
9548
+ function exactContext(ctx) {
9549
+ return ctx.probeLimit ? { ...ctx, probeLimit: false } : ctx;
9176
9550
  }
9177
9551
  function compileObjectQueryInternal(projectId, query, ctx) {
9178
9552
  switch (query.kind) {
9179
9553
  case "start":
9180
- return compileStart(projectId, query);
9554
+ return compileStart(projectId, query, ctx);
9181
9555
  case "refs":
9182
- return compileRefs(projectId, query);
9556
+ return compileRefs(projectId, query, ctx);
9183
9557
  case "filter":
9184
- return compileFilter(projectId, query.input, query.predicate);
9558
+ return compileFilter(projectId, query.input, query.predicate, ctx);
9185
9559
  case "sort":
9186
9560
  return compileSort(projectId, query.input, query.fields, ctx);
9187
9561
  case "limit":
9188
9562
  return compileLimit(projectId, query.input, query.limit, ctx);
9189
9563
  case "page":
9190
- return compilePage(projectId, query.input, query.pageSize, query.pageToken);
9564
+ return compilePage(projectId, query.input, query.pageSize, query.pageToken, ctx);
9191
9565
  case "traverse":
9192
- return compileTraversal(projectId, query.input, query.linkId, query.direction, query.sourceObjectTypeId);
9566
+ return compileTraversal(projectId, query.input, query.linkId, query.direction, query.sourceObjectTypeId, ctx);
9193
9567
  case "set":
9194
- return compileSet(projectId, query.op, query.inputs);
9568
+ return compileSet(projectId, query.op, query.inputs, ctx);
9195
9569
  case "project":
9196
9570
  return compileProject(projectId, query.input, query.properties, ctx);
9197
9571
  case "text":
9198
- return compileText(projectId, query.input, query.query, query.fields, query.fieldsByObjectType);
9572
+ return compileText(projectId, query.input, query.query, query.fields, query.fieldsByObjectType, ctx);
9199
9573
  case "expand":
9200
- return compileExpand(projectId, query.input, query.expansions);
9574
+ return compileExpand(projectId, query.input, query.expansions, ctx);
9201
9575
  case "vector":
9202
9576
  throw new Error(`[Sixb] SQLite object storage does not support query node '${query.kind}'`);
9203
9577
  }
9204
9578
  }
9205
- function compileStart(projectId, query) {
9579
+ function compileStart(projectId, query, ctx) {
9206
9580
  if (query.includeSubtypes === true) {
9207
9581
  throw new Error("[Sixb] SQLite object storage does not support start.includeSubtypes");
9208
9582
  }
9209
9583
  const order = compileOrder(identityOrderFields());
9210
9584
  const sql = `
9211
9585
  SELECT *, properties AS _cursor_properties
9212
- FROM objects
9586
+ FROM ${ctx.source.objectsTable}
9213
9587
  WHERE project_id = ? AND object_type_id = ?
9214
9588
  ORDER BY ${order.sql}
9215
9589
  `;
@@ -9217,7 +9591,7 @@ function compileStart(projectId, query) {
9217
9591
  return {
9218
9592
  sql,
9219
9593
  args,
9220
- totalSql: "SELECT COUNT(*) as total FROM objects WHERE project_id = ? AND object_type_id = ?",
9594
+ totalSql: `SELECT COUNT(*) as total FROM ${ctx.source.objectsTable} WHERE project_id = ? AND object_type_id = ?`,
9221
9595
  totalArgs: [projectId, query.objectTypeId],
9222
9596
  order,
9223
9597
  hasMore: () => false,
@@ -9227,7 +9601,7 @@ function compileStart(projectId, query) {
9227
9601
  }
9228
9602
  };
9229
9603
  }
9230
- function compileRefs(projectId, query) {
9604
+ function compileRefs(projectId, query, ctx) {
9231
9605
  if (query.refs.length === 0) {
9232
9606
  throw new Error("[Sixb] SQLite object storage requires at least one ref");
9233
9607
  }
@@ -9241,10 +9615,9 @@ function compileRefs(projectId, query) {
9241
9615
  FROM json_each(?) AS ref
9242
9616
  `;
9243
9617
  const sql = `
9244
- WITH requested AS (${requested})
9245
9618
  SELECT selected.*, selected.properties AS _cursor_properties
9246
- FROM requested
9247
- JOIN objects AS selected
9619
+ FROM (${requested}) AS requested
9620
+ JOIN ${ctx.source.objectsTable} AS selected
9248
9621
  ON selected.project_id = ?
9249
9622
  AND selected.object_type_id = requested.object_type_id
9250
9623
  AND selected.primary_id = requested.primary_id
@@ -9254,10 +9627,9 @@ function compileRefs(projectId, query) {
9254
9627
  sql,
9255
9628
  args: [refsJson, projectId, ...selectedOrder.args],
9256
9629
  totalSql: `
9257
- WITH requested AS (${requested})
9258
9630
  SELECT COUNT(*) AS total
9259
- FROM requested
9260
- JOIN objects AS selected
9631
+ FROM (${requested}) AS requested
9632
+ JOIN ${ctx.source.objectsTable} AS selected
9261
9633
  ON selected.project_id = ?
9262
9634
  AND selected.object_type_id = requested.object_type_id
9263
9635
  AND selected.primary_id = requested.primary_id
@@ -9271,12 +9643,12 @@ function compileRefs(projectId, query) {
9271
9643
  }
9272
9644
  };
9273
9645
  }
9274
- function compileFilter(projectId, inputQuery, predicateNode) {
9646
+ function compileFilter(projectId, inputQuery, predicateNode, ctx) {
9275
9647
  const predicate = compilePredicate(predicateNode);
9276
- return compileWhere(projectId, inputQuery, predicate);
9648
+ return compileWhere(projectId, inputQuery, predicate, ctx);
9277
9649
  }
9278
- function compileWhere(projectId, inputQuery, predicate) {
9279
- const input = compileObjectQueryInternal(projectId, inputQuery, exactContext);
9650
+ function compileWhere(projectId, inputQuery, predicate, ctx) {
9651
+ const input = compileObjectQueryInternal(projectId, inputQuery, exactContext(ctx));
9280
9652
  const sql = `
9281
9653
  SELECT *
9282
9654
  FROM (${input.sql}) AS input
@@ -9300,15 +9672,15 @@ function compileWhere(projectId, inputQuery, predicate) {
9300
9672
  }
9301
9673
  };
9302
9674
  }
9303
- function compileText(projectId, inputQuery, query, fields, fieldsByObjectType) {
9675
+ function compileText(projectId, inputQuery, query, fields, fieldsByObjectType, ctx) {
9304
9676
  const predicate = fields && fields.length > 0 ? compileTextPredicate(query, fields) : compileScopedTextPredicate(query, fieldsByObjectType);
9305
9677
  if (!predicate) {
9306
9678
  throw new Error("[Sixb] SQLite object text search requires fields or resolved text defaults");
9307
9679
  }
9308
- return compileWhere(projectId, inputQuery, predicate);
9680
+ return compileWhere(projectId, inputQuery, predicate, ctx);
9309
9681
  }
9310
9682
  function compileSort(projectId, inputQuery, fields, ctx) {
9311
- const input = compileObjectQueryInternal(projectId, inputQuery, exactContext);
9683
+ const input = compileObjectQueryInternal(projectId, inputQuery, exactContext(ctx));
9312
9684
  const probeInput = ctx.probeLimit && needsLimitProbe(inputQuery) ? compileObjectQueryInternal(projectId, inputQuery, ctx) : undefined;
9313
9685
  const order = compileOrder(sortOrderFields(fields));
9314
9686
  return {
@@ -9337,7 +9709,7 @@ function compileSort(projectId, inputQuery, fields, ctx) {
9337
9709
  };
9338
9710
  }
9339
9711
  function compileLimit(projectId, inputQuery, rawLimit, ctx) {
9340
- const input = compileObjectQueryInternal(projectId, inputQuery, exactContext);
9712
+ const input = compileObjectQueryInternal(projectId, inputQuery, exactContext(ctx));
9341
9713
  const limit = Math.max(0, rawLimit);
9342
9714
  const rowLimit = ctx.probeLimit ? limit + 1 : limit;
9343
9715
  return {
@@ -9361,8 +9733,8 @@ function compileLimit(projectId, inputQuery, rawLimit, ctx) {
9361
9733
  }
9362
9734
  };
9363
9735
  }
9364
- function compilePage(projectId, inputQuery, rawPageSize, pageToken) {
9365
- const input = compileObjectQueryInternal(projectId, inputQuery, exactContext);
9736
+ function compilePage(projectId, inputQuery, rawPageSize, pageToken, ctx) {
9737
+ const input = compileObjectQueryInternal(projectId, inputQuery, exactContext(ctx));
9366
9738
  const pageSize = Math.max(0, rawPageSize);
9367
9739
  const cursor = pageToken ? decodePageToken(pageToken, input.order.fields) : undefined;
9368
9740
  const cursorPredicate = cursor ? compileKeysetPredicate(input.order.fields, cursor, input.order.propertyColumn) : undefined;
@@ -9391,27 +9763,27 @@ function compilePage(projectId, inputQuery, rawPageSize, pageToken) {
9391
9763
  }
9392
9764
  };
9393
9765
  }
9394
- function compileTraversal(projectId, inputQuery, linkId, direction, sourceObjectTypeId) {
9395
- const input = compileObjectQueryInternal(projectId, inputQuery, exactContext);
9766
+ function compileTraversal(projectId, inputQuery, linkId, direction, sourceObjectTypeId, ctx) {
9767
+ const input = compileObjectQueryInternal(projectId, inputQuery, exactContext(ctx));
9396
9768
  const outputAlias = direction === "outgoing" ? "target_object" : "source_object";
9397
9769
  const joinSql = direction === "outgoing" ? `
9398
- JOIN links AS edge
9770
+ JOIN ${ctx.source.linksTable} AS edge
9399
9771
  ON edge.project_id = input.project_id
9400
9772
  AND edge.source_type_id = input.object_type_id
9401
9773
  AND edge.source_id = input.primary_id
9402
9774
  AND edge.link_id = ?
9403
- JOIN objects AS target_object
9775
+ JOIN ${ctx.source.objectsTable} AS target_object
9404
9776
  ON target_object.project_id = edge.project_id
9405
9777
  AND target_object.object_type_id = edge.target_type_id
9406
9778
  AND target_object.primary_id = edge.target_id
9407
9779
  ` : `
9408
- JOIN links AS edge
9780
+ JOIN ${ctx.source.linksTable} AS edge
9409
9781
  ON edge.project_id = input.project_id
9410
9782
  AND edge.target_type_id = input.object_type_id
9411
9783
  AND edge.target_id = input.primary_id
9412
9784
  AND edge.link_id = ?${sourceObjectTypeId === undefined ? "" : `
9413
9785
  AND edge.source_type_id = ?`}
9414
- JOIN objects AS source_object
9786
+ JOIN ${ctx.source.objectsTable} AS source_object
9415
9787
  ON source_object.project_id = edge.project_id
9416
9788
  AND source_object.object_type_id = edge.source_type_id
9417
9789
  AND source_object.primary_id = edge.source_id
@@ -9443,9 +9815,9 @@ function compileTraversal(projectId, inputQuery, linkId, direction, sourceObject
9443
9815
  }
9444
9816
  };
9445
9817
  }
9446
- function compileExpand(projectId, inputQuery, expansions) {
9447
- const input = compileObjectQueryInternal(projectId, inputQuery, exactContext);
9448
- const expand = compileExpansionsObject(expansions, { project: "input.project_id", type: "input.object_type_id", id: "input.primary_id" }, "");
9818
+ function compileExpand(projectId, inputQuery, expansions, ctx) {
9819
+ const input = compileObjectQueryInternal(projectId, inputQuery, exactContext(ctx));
9820
+ const expand = compileExpansionsObject(expansions, { project: "input.project_id", type: "input.object_type_id", id: "input.primary_id" }, "", ctx);
9449
9821
  const inputOrder = compileOrder(input.order.fields, "input", "input._cursor_properties");
9450
9822
  const sql = `
9451
9823
  SELECT input.*, ${expand.sql} AS _expand
@@ -9464,18 +9836,18 @@ function compileExpand(projectId, inputQuery, expansions) {
9464
9836
  nextPageToken: input.nextPageToken
9465
9837
  };
9466
9838
  }
9467
- function compileExpansionsObject(expansions, parent, pathPrefix) {
9839
+ function compileExpansionsObject(expansions, parent, pathPrefix, ctx) {
9468
9840
  const parts = [];
9469
9841
  const args = [];
9470
9842
  expansions.forEach((expansion, index) => {
9471
9843
  const path = pathPrefix === "" ? `${index}` : `${pathPrefix}_${index}`;
9472
- const value = compileExpansionValue(expansion, parent, path);
9844
+ const value = compileExpansionValue(expansion, parent, path, ctx);
9473
9845
  parts.push("?", `json(${value.sql})`);
9474
9846
  args.push(expansion.linkId, ...value.args);
9475
9847
  });
9476
9848
  return { sql: `json_object(${parts.join(", ")})`, args };
9477
9849
  }
9478
- function compileExpansionValue(expansion, parent, path) {
9850
+ function compileExpansionValue(expansion, parent, path, ctx) {
9479
9851
  const edge = `edge_${path}`;
9480
9852
  const target = `tgt_${path}`;
9481
9853
  const ranked = `ranked_${path}`;
@@ -9484,7 +9856,7 @@ function compileExpansionValue(expansion, parent, path) {
9484
9856
  const neighborId = incoming ? `${edge}.source_id` : `${edge}.target_id`;
9485
9857
  const parentType = incoming ? `${edge}.target_type_id` : `${edge}.source_type_id`;
9486
9858
  const parentId = incoming ? `${edge}.target_id` : `${edge}.source_id`;
9487
- const child = compileExpansionChildJson(expansion, edge, target, path);
9859
+ const child = compileExpansionChildJson(expansion, edge, target, path, ctx);
9488
9860
  const order = compileExpansionOrder(expansion, target, neighborType, neighborId);
9489
9861
  const whereParts = [
9490
9862
  `${edge}.project_id = ${parent.project}`,
@@ -9499,8 +9871,8 @@ function compileExpansionValue(expansion, parent, path) {
9499
9871
  }
9500
9872
  const inner = `
9501
9873
  SELECT ${child.sql} AS elem, row_number() OVER (ORDER BY ${order.sql}) AS _ord
9502
- FROM links AS ${edge}
9503
- JOIN objects AS ${target}
9874
+ FROM ${ctx.source.linksTable} AS ${edge}
9875
+ JOIN ${ctx.source.objectsTable} AS ${target}
9504
9876
  ON ${target}.project_id = ${edge}.project_id
9505
9877
  AND ${target}.object_type_id = ${neighborType}
9506
9878
  AND ${target}.primary_id = ${neighborId}
@@ -9519,7 +9891,7 @@ function compileExpansionValue(expansion, parent, path) {
9519
9891
  args: [...innerArgs, limit]
9520
9892
  };
9521
9893
  }
9522
- function compileExpansionChildJson(expansion, edge, target, path) {
9894
+ function compileExpansionChildJson(expansion, edge, target, path, ctx) {
9523
9895
  const fields = [
9524
9896
  `'projectId', ${target}.project_id`,
9525
9897
  `'objectTypeId', ${target}.object_type_id`,
@@ -9537,7 +9909,7 @@ function compileExpansionChildJson(expansion, edge, target, path) {
9537
9909
  project: `${target}.project_id`,
9538
9910
  type: `${target}.object_type_id`,
9539
9911
  id: `${target}.primary_id`
9540
- }, path);
9912
+ }, path, ctx);
9541
9913
  fields.push(`'links', ${nested.sql}`);
9542
9914
  args.push(...nested.args);
9543
9915
  }
@@ -9561,20 +9933,20 @@ function compileExpansionOrder(expansion, target, neighborType, neighborId) {
9561
9933
  clauses.push(`${neighborType} ASC`, `${neighborId} ASC`);
9562
9934
  return { sql: clauses.join(", "), args };
9563
9935
  }
9564
- function compileSet(projectId, op, inputs) {
9936
+ function compileSet(projectId, op, inputs, ctx) {
9565
9937
  if (inputs.length === 0) {
9566
- const order2 = compileOrder(identityOrderFields());
9938
+ const order = compileOrder(identityOrderFields());
9567
9939
  return {
9568
9940
  sql: `
9569
9941
  SELECT *
9570
- FROM objects
9942
+ FROM ${ctx.source.objectsTable}
9571
9943
  WHERE 1 = 0
9572
- ORDER BY ${order2.sql}
9944
+ ORDER BY ${order.sql}
9573
9945
  `,
9574
- args: order2.args,
9946
+ args: order.args,
9575
9947
  totalSql: "SELECT 0 as total",
9576
9948
  totalArgs: [],
9577
- order: order2,
9949
+ order,
9578
9950
  hasMore: () => false,
9579
9951
  trimRows: identityRows,
9580
9952
  nextPageToken: () => {
@@ -9582,14 +9954,14 @@ function compileSet(projectId, op, inputs) {
9582
9954
  }
9583
9955
  };
9584
9956
  }
9585
- const compiledInputs = inputs.map((input) => compileObjectQueryInternal(projectId, input, exactContext));
9957
+ const compiledInputs = inputs.map((input) => compileObjectQueryInternal(projectId, input, exactContext(ctx)));
9586
9958
  const identities = compileSetIdentities(op, compiledInputs);
9587
9959
  const order = compileOrder(identityOrderFields());
9588
9960
  const selectedOrder = compileOrder(identityOrderFields(), "selected");
9589
9961
  const sql = `
9590
9962
  SELECT selected.*, selected.properties AS _cursor_properties
9591
9963
  FROM (${identities.sql}) AS ids
9592
- JOIN objects AS selected
9964
+ JOIN ${ctx.source.objectsTable} AS selected
9593
9965
  ON selected.project_id = ?
9594
9966
  AND selected.object_type_id = ids.object_type_id
9595
9967
  AND selected.primary_id = ids.primary_id
@@ -9674,7 +10046,7 @@ function compileProjectionExpression(properties) {
9674
10046
  sql: `
9675
10047
  COALESCE(
9676
10048
  (
9677
- SELECT json_group_object(projected.key, projected.value)
10049
+ SELECT json_group_object(projected.key, ${sqliteJsonEachValue("projected")})
9678
10050
  FROM json_each(input.properties) AS projected
9679
10051
  WHERE projected.key IN (${properties.map(() => "?").join(", ")})
9680
10052
  ),
@@ -9684,6 +10056,13 @@ function compileProjectionExpression(properties) {
9684
10056
  args: [...properties]
9685
10057
  };
9686
10058
  }
10059
+ function sqliteJsonEachValue(alias) {
10060
+ return `CASE ${alias}.type
10061
+ WHEN 'true' THEN json('true')
10062
+ WHEN 'false' THEN json('false')
10063
+ ELSE ${alias}.value
10064
+ END`;
10065
+ }
9687
10066
  function compilePredicate(predicate) {
9688
10067
  if ("scalarKind" in predicate && predicate.scalarKind === "decimal" && (predicate.op === "lt" || predicate.op === "lte" || predicate.op === "gt" || predicate.op === "gte")) {
9689
10068
  throw new ObjectQueryExecutionError("exact_decimal_not_supported", "SQLite object storage cannot push down exact decimal predicates", "$.predicate");
@@ -10006,112 +10385,131 @@ function isPlainObject(value) {
10006
10385
  return typeof value === "object" && value !== null && !Array.isArray(value);
10007
10386
  }
10008
10387
 
10009
- // src/object-storage.ts
10010
- var SQLITE_OBJECT_QUERY_CAPABILITIES = {
10011
- queryObjects: true,
10012
- countObjects: true,
10013
- existsObjects: true,
10014
- facetObjects: true,
10015
- nodes: {
10016
- start: true,
10017
- refs: true,
10018
- filter: true,
10019
- text: true,
10020
- sort: true,
10021
- limit: true,
10022
- page: true,
10023
- traverse: true,
10024
- set: true,
10025
- project: true,
10026
- expand: true
10027
- },
10028
- predicateOps: {
10029
- and: true,
10030
- or: true,
10031
- not: true,
10032
- eq: true,
10033
- neq: true,
10034
- lt: true,
10035
- lte: true,
10036
- gt: true,
10037
- gte: true,
10038
- in: true,
10039
- exists: true,
10040
- contains: true
10041
- },
10042
- sortKinds: {
10043
- property: true
10044
- },
10045
- traversalDirections: {
10046
- outgoing: true,
10047
- incoming: true
10048
- },
10049
- setOps: {
10050
- union: true,
10051
- intersect: true,
10052
- subtract: true
10053
- },
10054
- scalarOperations: {
10055
- string: { equality: true, ordering: true },
10056
- uuid: { equality: true, ordering: true },
10057
- boolean: { equality: true },
10058
- integer: { equality: true, ordering: true },
10059
- double: { equality: true, ordering: true },
10060
- decimal: { equality: true },
10061
- date: { equality: true, ordering: true },
10062
- timestamp: { equality: true, ordering: true }
10063
- },
10064
- limits: {
10065
- totalCount: true,
10066
- stablePageTokens: true
10067
- },
10068
- notes: [
10069
- "SQLite object query pushdown supports start/refs/filter/text/sort/limit/page/traverse/set/project/expand over JSON properties and object links.",
10070
- "expand hydrates linked objects in-database (top-N per parent via row_number() + json_group_array); core resolves each expansion's cardinality before pushdown, and a mixed/unresolved one stays on the fallback.",
10071
- "Ordered decimal predicates and sorting use the bounded core fallback because SQLite has no native exact decimal type; canonical decimal equality remains pushdown-safe.",
10072
- "Relevance sorting, vector search, and unresolved start.includeSubtypes remain planner fallback or rejection cases."
10073
- ]
10074
- };
10388
+ // src/objects/reader.ts
10389
+ import { linkBatchKey, objectBatchKey } from "@sixb/core/storage";
10075
10390
 
10076
- class SqliteObjectStorage {
10077
- connection;
10078
- db;
10079
- constructor(options = {}) {
10080
- this.connection = openSqliteStoreConnection(options);
10081
- this.db = this.connection.db;
10082
- if (this.connection.installFreshSchema) {
10083
- installFreshSqliteSchema(this.db);
10084
- }
10391
+ // src/objects/rows.ts
10392
+ function rowToObject(row) {
10393
+ return {
10394
+ projectId: row.project_id,
10395
+ objectTypeId: row.object_type_id,
10396
+ primaryId: row.primary_id,
10397
+ properties: JSON.parse(row.properties),
10398
+ createdAt: new Date(row.created_at),
10399
+ updatedAt: new Date(row.updated_at),
10400
+ version: row.version,
10401
+ lastCommitId: row.last_commit_id
10402
+ };
10403
+ }
10404
+ function queryRowToObject(row) {
10405
+ const base = rowToObject(row);
10406
+ const links = reviveExpandedLinks(parseExpandColumn(row._expand));
10407
+ return links ? { ...base, links } : base;
10408
+ }
10409
+ function rowToLink(row) {
10410
+ return {
10411
+ projectId: row.project_id,
10412
+ sourceTypeId: row.source_type_id,
10413
+ sourceId: row.source_id,
10414
+ linkId: row.link_id,
10415
+ targetTypeId: row.target_type_id,
10416
+ targetId: row.target_id,
10417
+ ...row.properties === null ? {} : { properties: JSON.parse(row.properties) },
10418
+ createdAt: new Date(row.created_at),
10419
+ updatedAt: new Date(row.updated_at),
10420
+ lastCommitId: row.last_commit_id
10421
+ };
10422
+ }
10423
+ function parseExpandColumn(value) {
10424
+ if (typeof value !== "string")
10425
+ return value ?? undefined;
10426
+ return JSON.parse(value);
10427
+ }
10428
+ function reviveExpandedLinks(value) {
10429
+ if (!isPlainObject2(value))
10430
+ return;
10431
+ const links = {};
10432
+ for (const [linkId, raw] of Object.entries(value)) {
10433
+ links[linkId] = reviveExpandedLinkValue(raw);
10085
10434
  }
10086
- queryCapabilities() {
10087
- return SQLITE_OBJECT_QUERY_CAPABILITIES;
10435
+ return links;
10436
+ }
10437
+ function reviveExpandedLinkValue(value) {
10438
+ if (value === null || value === undefined)
10439
+ return null;
10440
+ if (Array.isArray(value))
10441
+ return value.map(reviveExpandedRow);
10442
+ return reviveExpandedRow(value);
10443
+ }
10444
+ function reviveExpandedRow(value) {
10445
+ const row = isPlainObject2(value) ? value : {};
10446
+ const expanded = {
10447
+ projectId: String(row.projectId),
10448
+ objectTypeId: String(row.objectTypeId),
10449
+ primaryId: String(row.primaryId),
10450
+ properties: isPlainObject2(row.properties) ? row.properties : {},
10451
+ createdAt: new Date(row.createdAt),
10452
+ updatedAt: new Date(row.updatedAt),
10453
+ version: Number(row.version),
10454
+ lastCommitId: String(row.lastCommitId)
10455
+ };
10456
+ if (isPlainObject2(row.linkProperties) && Object.keys(row.linkProperties).length > 0) {
10457
+ expanded.linkProperties = row.linkProperties;
10088
10458
  }
10089
- async queryObjects(params) {
10459
+ const links = reviveExpandedLinks(row.links);
10460
+ if (links)
10461
+ expanded.links = links;
10462
+ return expanded;
10463
+ }
10464
+ function isPlainObject2(value) {
10465
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10466
+ }
10467
+
10468
+ // src/objects/reader.ts
10469
+ class SqliteObjectReader {
10470
+ db;
10471
+ source;
10472
+ constructor(db, source) {
10473
+ this.db = db;
10474
+ this.source = source;
10475
+ }
10476
+ queryObjects(params) {
10477
+ const source = this.source;
10090
10478
  const compiled = compileObjectQuery(params.projectId, params.query, {
10091
- includeTotal: params.includeTotal
10479
+ includeTotal: params.includeTotal,
10480
+ source
10092
10481
  });
10093
10482
  const total = params.includeTotal === false ? undefined : readTotal(this.db, compiled);
10094
10483
  const rawRows = this.db.query(compiled.sql).all(...compiled.args);
10095
10484
  const rows = compiled.trimRows(rawRows);
10096
10485
  const hasMore = total === undefined && compiled.hasMoreProbe ? compiled.hasMoreProbe.hasMore(this.db.query(compiled.hasMoreProbe.sql).all(...compiled.hasMoreProbe.args).length) : compiled.hasMore(rawRows.length, total);
10097
10486
  return {
10098
- objects: rows.map((row) => this.queryRowToObject(row)),
10487
+ objects: rows.map((row) => queryRowToObject(row)),
10099
10488
  hasMore,
10100
10489
  nextPageToken: compiled.nextPageToken(rows, rawRows.length),
10101
10490
  ...total === undefined ? {} : { total }
10102
10491
  };
10103
10492
  }
10104
- async countObjects(params) {
10493
+ countObjects(params) {
10494
+ const source = this.source;
10105
10495
  return {
10106
- count: readTotal(this.db, compileObjectQuery(params.projectId, stripOuterRowShape(params.query)))
10496
+ count: readTotal(this.db, compileObjectQuery(params.projectId, stripOuterRowShape(params.query), {
10497
+ source
10498
+ }))
10107
10499
  };
10108
10500
  }
10109
- async existsObjects(params) {
10110
- const compiled = compileObjectQuery(params.projectId, existsProbeQuery(params.query));
10501
+ existsObjects(params) {
10502
+ const source = this.source;
10503
+ const compiled = compileObjectQuery(params.projectId, existsProbeQuery(params.query), {
10504
+ source
10505
+ });
10111
10506
  return { exists: this.db.query(compiled.sql).get(...compiled.args) !== null };
10112
10507
  }
10113
- async facetObjects(params) {
10114
- const compiled = compileObjectQuery(params.projectId, stripOuterRowShape(params.query));
10508
+ facetObjects(params) {
10509
+ const source = this.source;
10510
+ const compiled = compileObjectQuery(params.projectId, stripOuterRowShape(params.query), {
10511
+ source
10512
+ });
10115
10513
  return {
10116
10514
  facets: params.facets.map((facet) => ({
10117
10515
  propertyId: facet.propertyId,
@@ -10119,14 +10517,43 @@ class SqliteObjectStorage {
10119
10517
  }))
10120
10518
  };
10121
10519
  }
10122
- async getByPrimaryId(params) {
10123
- const row = this.db.query("SELECT * FROM objects WHERE project_id = ? AND object_type_id = ? AND primary_id = ?").get(params.projectId, params.objectTypeId, params.primaryId);
10124
- return row ? this.rowToObject(row) : null;
10520
+ getByPrimaryId(params) {
10521
+ const source = this.source;
10522
+ const objectsTable = source.objectsTable;
10523
+ const statement = source.wrapStatement(`SELECT * FROM ${objectsTable} WHERE project_id = ? AND object_type_id = ? AND primary_id = ?`, [params.projectId, params.objectTypeId, params.primaryId]);
10524
+ const row = this.db.query(statement.sql).get(...statement.args);
10525
+ return row ? rowToObject(row) : null;
10125
10526
  }
10126
- async listLinks(params) {
10527
+ selectsObjectProperties(params) {
10528
+ const source = this.source;
10529
+ const result = params.items.map(() => false);
10530
+ if (params.items.length === 0)
10531
+ return result;
10532
+ const items = params.items.map((item, batchIndex) => ({ ...item, batchIndex }));
10533
+ const requestedType = "CAST(json_extract(requested.value, '$.objectTypeId') AS TEXT)";
10534
+ const requestedId = "CAST(json_extract(requested.value, '$.primaryId') AS TEXT)";
10535
+ const requestedProperty = "CAST(json_extract(requested.value, '$.propertyId') AS TEXT)";
10536
+ const selected = "objectPropertyPermissionsTable" in source;
10537
+ const storedTable = selected ? source.objectPropertyPermissionsTable : source.objectsTable;
10538
+ const propertyJoin = selected ? `AND stored.property_id = ${requestedProperty}` : "";
10539
+ const statement = source.wrapStatement(`SELECT DISTINCT
10540
+ CAST(json_extract(requested.value, '$.batchIndex') AS INTEGER) AS _batch_index
10541
+ FROM ${storedTable} AS stored
10542
+ JOIN json_each(?) AS requested
10543
+ ON stored.object_type_id = ${requestedType}
10544
+ AND stored.primary_id = ${requestedId}
10545
+ ${propertyJoin}
10546
+ WHERE stored.project_id = ?`, [JSON.stringify(items), params.projectId]);
10547
+ const rows = this.db.query(statement.sql).all(...statement.args);
10548
+ for (const row of rows)
10549
+ result[row._batch_index] = true;
10550
+ return result;
10551
+ }
10552
+ listLinks(params) {
10553
+ const source = this.source;
10127
10554
  const direction = params.direction ?? "outgoing";
10128
10555
  const directionWhere = direction === "incoming" ? "target_type_id = ? AND target_id = ?" : direction === "both" ? "((source_type_id = ? AND source_id = ?) OR (target_type_id = ? AND target_id = ?))" : "source_type_id = ? AND source_id = ?";
10129
- let query = `SELECT * FROM links WHERE project_id = ? AND ${directionWhere}`;
10556
+ let query = `SELECT * FROM ${source.linksTable} WHERE project_id = ? AND ${directionWhere}`;
10130
10557
  const args = direction === "both" ? [
10131
10558
  params.projectId,
10132
10559
  params.objectTypeId,
@@ -10138,64 +10565,104 @@ class SqliteObjectStorage {
10138
10565
  query += " AND link_id = ?";
10139
10566
  args.push(params.linkId);
10140
10567
  }
10141
- const rows = this.db.query(query).all(...args);
10142
- return rows.map((row) => this.rowToLink(row));
10568
+ const statement = source.wrapStatement(query, args);
10569
+ const rows = this.db.query(statement.sql).all(...statement.args);
10570
+ return rows.map((row) => rowToLink(row));
10143
10571
  }
10144
- async getByPrimaryIdBatch(params) {
10572
+ getByPrimaryIdBatch(params) {
10573
+ const source = this.source;
10145
10574
  const result = new Map;
10146
10575
  if (params.items.length === 0)
10147
10576
  return result;
10148
- const rows = this.db.query(`
10149
- SELECT object.*
10150
- FROM json_each(?) AS requested
10151
- JOIN objects AS object
10152
- ON object.project_id = ?
10153
- AND object.object_type_id = json_extract(requested.value, '$.objectTypeId')
10154
- AND object.primary_id = json_extract(requested.value, '$.primaryId')
10155
- `).all(JSON.stringify(params.items), params.projectId);
10156
- for (const row of rows) {
10157
- result.set(objectBatchKey(row.object_type_id, row.primary_id), this.rowToObject(row));
10577
+ const items = params.items.map((item, batchIndex) => ({ ...item, batchIndex }));
10578
+ const statement = source.wrapStatement(`
10579
+ SELECT
10580
+ object.*,
10581
+ CAST(json_extract(requested.value, '$.batchIndex') AS INTEGER) AS _batch_index
10582
+ FROM json_each(?) AS requested
10583
+ JOIN ${source.objectsTable} AS object
10584
+ ON object.project_id = ?
10585
+ AND object.object_type_id = json_extract(requested.value, '$.objectTypeId')
10586
+ AND object.primary_id = json_extract(requested.value, '$.primaryId')
10587
+ `, [JSON.stringify(items), params.projectId]);
10588
+ const rows = this.db.query(statement.sql).all(...statement.args);
10589
+ const rowsByIndex = new Map(rows.map((row) => [row._batch_index, row]));
10590
+ for (const [index, item] of params.items.entries()) {
10591
+ const row = rowsByIndex.get(index);
10592
+ if (row)
10593
+ result.set(objectBatchKey(item.objectTypeId, item.primaryId), rowToObject(row));
10158
10594
  }
10159
10595
  return result;
10160
10596
  }
10161
- async listLinksBatch(params) {
10597
+ listLinksBatch(params) {
10598
+ const source = this.source;
10162
10599
  const result = new Map;
10163
10600
  if (params.items.length === 0)
10164
10601
  return result;
10165
- const stmt = this.db.query("SELECT * FROM links WHERE project_id = ? AND source_type_id = ? AND source_id = ? AND link_id = ?");
10166
- for (const item of params.items) {
10167
- const rows = stmt.all(params.projectId, item.objectTypeId, item.objectId, item.linkId);
10168
- if (rows.length > 0) {
10169
- result.set(linkBatchKey(item.objectTypeId, item.objectId, item.linkId), rows.map((r) => this.rowToLink(r)));
10602
+ const direction = params.direction ?? "outgoing";
10603
+ const requestedType = "CAST(json_extract(requested.value, '$.objectTypeId') AS TEXT)";
10604
+ const requestedId = "CAST(json_extract(requested.value, '$.objectId') AS TEXT)";
10605
+ const requestedLink = "CAST(json_extract(requested.value, '$.linkId') AS TEXT)";
10606
+ const outgoing = `stored.source_type_id = ${requestedType} AND stored.source_id = ${requestedId}`;
10607
+ const incoming = `stored.target_type_id = ${requestedType} AND stored.target_id = ${requestedId}`;
10608
+ const directionJoin = direction === "both" ? `((${outgoing}) OR (${incoming}))` : direction === "incoming" ? incoming : outgoing;
10609
+ const items = params.items.map((item, batchIndex) => ({ ...item, batchIndex }));
10610
+ const statement = source.wrapStatement(`
10611
+ SELECT
10612
+ stored.*,
10613
+ CAST(json_extract(requested.value, '$.batchIndex') AS INTEGER) AS _batch_index
10614
+ FROM ${source.linksTable} AS stored
10615
+ JOIN json_each(?) AS requested
10616
+ ON ${directionJoin}
10617
+ AND stored.link_id = ${requestedLink}
10618
+ WHERE stored.project_id = ?
10619
+ ORDER BY _batch_index, source_type_id, source_id, link_id, target_type_id, target_id
10620
+ `, [JSON.stringify(items), params.projectId]);
10621
+ const rows = this.db.query(statement.sql).all(...statement.args);
10622
+ const rowsByIndex = params.items.map(() => new Map);
10623
+ for (const row of rows) {
10624
+ const link = rowToLink(row);
10625
+ rowsByIndex[row._batch_index]?.set(fullLinkIdentity(link), link);
10626
+ }
10627
+ for (const [index, item] of params.items.entries()) {
10628
+ const links = [...rowsByIndex[index]?.values() ?? []];
10629
+ if (links.length > 0) {
10630
+ result.set(linkBatchKey(item.objectTypeId, item.objectId, item.linkId), links);
10170
10631
  }
10171
10632
  }
10172
10633
  return result;
10173
10634
  }
10174
- async queryLinks(params) {
10175
- assertLinkQueryLimit(params.limit);
10635
+ queryLinks(params) {
10636
+ const source = this.source;
10176
10637
  if (params.objectRefs.length === 0 || params.endpointObjectTypeIds?.length === 0) {
10177
10638
  return { links: [], hasMore: false };
10178
10639
  }
10640
+ const requested = `
10641
+ SELECT DISTINCT
10642
+ json_extract(value, '$.objectTypeId') AS object_type_id,
10643
+ json_extract(value, '$.primaryId') AS object_id
10644
+ FROM json_each(?)
10645
+ `;
10646
+ const linksTable = source.linksTable;
10179
10647
  const sourceJoin = `
10180
10648
  SELECT link.*
10181
- FROM links AS link
10182
- JOIN requested
10649
+ FROM ${linksTable} AS link
10650
+ JOIN (${requested}) AS requested
10183
10651
  ON requested.object_type_id = link.source_type_id
10184
10652
  AND requested.object_id = link.source_id
10185
10653
  WHERE link.project_id = ?
10186
10654
  `;
10187
10655
  const targetJoin = `
10188
10656
  SELECT link.*
10189
- FROM links AS link
10190
- JOIN requested
10657
+ FROM ${linksTable} AS link
10658
+ JOIN (${requested}) AS requested
10191
10659
  ON requested.object_type_id = link.target_type_id
10192
10660
  AND requested.object_id = link.target_id
10193
10661
  WHERE link.project_id = ?
10194
10662
  `;
10195
10663
  const incidentSql = params.direction === "outgoing" ? sourceJoin : params.direction === "incoming" ? targetJoin : `${sourceJoin} UNION ${targetJoin}`;
10196
- const args = [JSON.stringify(params.objectRefs), params.projectId];
10197
- if (params.direction === "both")
10198
- args.push(params.projectId);
10664
+ const requestedJson = JSON.stringify(params.objectRefs);
10665
+ const args = params.direction === "both" ? [requestedJson, params.projectId, requestedJson, params.projectId] : [requestedJson, params.projectId];
10199
10666
  const predicates = [];
10200
10667
  if (params.linkId !== undefined) {
10201
10668
  predicates.push("link_id = ?");
@@ -10212,69 +10679,22 @@ class SqliteObjectStorage {
10212
10679
  }
10213
10680
  args.push(params.limit + 1);
10214
10681
  const whereSql = predicates.length > 0 ? `WHERE ${predicates.join(" AND ")}` : "";
10215
- const rows = this.db.query(`
10216
- WITH requested AS (
10217
- SELECT DISTINCT
10218
- json_extract(value, '$.objectTypeId') AS object_type_id,
10219
- json_extract(value, '$.primaryId') AS object_id
10220
- FROM json_each(?)
10221
- ), incident AS (${incidentSql})
10222
- SELECT *
10223
- FROM incident
10224
- ${whereSql}
10225
- ORDER BY source_type_id, source_id, link_id, target_type_id, target_id
10226
- LIMIT ?
10227
- `).all(...args);
10682
+ const statement = source.wrapStatement(`
10683
+ SELECT *
10684
+ FROM (${incidentSql}) AS incident
10685
+ ${whereSql}
10686
+ ORDER BY source_type_id, source_id, link_id, target_type_id, target_id
10687
+ LIMIT ?
10688
+ `, args);
10689
+ const rows = this.db.query(statement.sql).all(...statement.args);
10228
10690
  return {
10229
- links: rows.slice(0, params.limit).map((row) => this.rowToLink(row)),
10691
+ links: rows.slice(0, params.limit).map((row) => rowToLink(row)),
10230
10692
  hasMore: rows.length > params.limit
10231
10693
  };
10232
10694
  }
10233
- async listIncidentLinksBatch(params) {
10234
- if (params.items.length === 0)
10235
- return [];
10236
- const rows = this.db.query(`
10237
- WITH requested AS (
10238
- SELECT
10239
- json_extract(value, '$.objectTypeId') AS object_type_id,
10240
- json_extract(value, '$.objectId') AS object_id
10241
- FROM json_each(?)
10242
- )
10243
- SELECT link.*
10244
- FROM links AS link
10245
- JOIN requested
10246
- ON requested.object_type_id = link.source_type_id
10247
- AND requested.object_id = link.source_id
10248
- WHERE link.project_id = ?
10249
- UNION
10250
- SELECT link.*
10251
- FROM links AS link
10252
- JOIN requested
10253
- ON requested.object_type_id = link.target_type_id
10254
- AND requested.object_id = link.target_id
10255
- WHERE link.project_id = ?
10256
- `).all(JSON.stringify(params.items), params.projectId, params.projectId);
10257
- return rows.map((row) => this.rowToLink(row));
10258
- }
10259
- async listByPrimaryIdPage(params) {
10260
- assertReconciliationPageLimit(params.limit);
10261
- const rows = this.db.query(`
10262
- SELECT * FROM objects
10263
- WHERE project_id = ? AND object_type_id = ?
10264
- AND (? IS NULL OR primary_id > ?)
10265
- ORDER BY primary_id ASC
10266
- LIMIT ?
10267
- `).all(params.projectId, params.objectTypeId, params.afterPrimaryId ?? null, params.afterPrimaryId ?? null, params.limit + 1);
10268
- const hasMore = rows.length > params.limit;
10269
- const objects = rows.slice(0, params.limit).map((row) => this.rowToObject(row));
10270
- const last = objects.at(-1);
10271
- return {
10272
- objects,
10273
- ...hasMore && last ? { nextPrimaryId: last.primaryId } : {}
10274
- };
10275
- }
10276
- async list(params) {
10277
- let query = "SELECT * FROM objects WHERE project_id = ?";
10695
+ list(params) {
10696
+ const source = this.source;
10697
+ let query = `SELECT * FROM ${source.objectsTable} WHERE project_id = ?`;
10278
10698
  const args = [params.projectId];
10279
10699
  if (params.objectTypeId) {
10280
10700
  if (typeof params.objectTypeId === "string") {
@@ -10309,7 +10729,8 @@ class SqliteObjectStorage {
10309
10729
  query += " AND created_at <= ?";
10310
10730
  args.push(params.createdBefore.toISOString());
10311
10731
  }
10312
- const countResult = this.db.query(`SELECT COUNT(*) as total FROM (${query})`).get(...args);
10732
+ const countStatement = source.wrapStatement(`SELECT COUNT(*) as total FROM (${query}) AS filtered_objects`, args);
10733
+ const countResult = this.db.query(countStatement.sql).get(...countStatement.args);
10313
10734
  const total = countResult.total;
10314
10735
  const offset = params.offset ?? 0;
10315
10736
  const limit = params.limit ?? 50;
@@ -10321,42 +10742,605 @@ class SqliteObjectStorage {
10321
10742
  query += ` ORDER BY ${orderColumn} ${order.toUpperCase()}`;
10322
10743
  query += " LIMIT ? OFFSET ?";
10323
10744
  args.push(limit + 1, offset);
10324
- const rows = this.db.query(query).all(...args);
10745
+ const rowsStatement = source.wrapStatement(query, args);
10746
+ const rows = this.db.query(rowsStatement.sql).all(...rowsStatement.args);
10325
10747
  const hasMore = rows.length > limit;
10326
- const objects = rows.slice(0, limit).map((row) => this.rowToObject(row));
10748
+ const objects = rows.slice(0, limit).map((row) => rowToObject(row));
10327
10749
  return { objects, hasMore, total };
10328
10750
  }
10329
- rowToObject(row) {
10330
- return {
10331
- projectId: row.project_id,
10332
- objectTypeId: row.object_type_id,
10333
- primaryId: row.primary_id,
10334
- properties: JSON.parse(row.properties),
10335
- createdAt: new Date(row.created_at),
10336
- updatedAt: new Date(row.updated_at),
10337
- version: row.version,
10338
- lastCommitId: row.last_commit_id
10339
- };
10751
+ }
10752
+ function fullLinkIdentity(link) {
10753
+ return JSON.stringify([
10754
+ link.sourceTypeId,
10755
+ link.sourceId,
10756
+ link.linkId,
10757
+ link.targetTypeId,
10758
+ link.targetId
10759
+ ]);
10760
+ }
10761
+ function assertLinkQueryLimit(limit) {
10762
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
10763
+ throw new Error("Object link query limit must be a positive safe integer.");
10764
+ }
10765
+ }
10766
+ function readTotal(db, compiled) {
10767
+ const row = db.query(compiled.totalSql).get(...compiled.totalArgs);
10768
+ return row.total;
10769
+ }
10770
+ function readFacetBuckets(db, compiled, facet) {
10771
+ const path = sqliteJsonPath(facet.propertyId);
10772
+ const rows = db.query(`
10773
+ SELECT
10774
+ json_type(input.properties, ?) AS value_type,
10775
+ json_extract(input.properties, ?) AS value,
10776
+ COUNT(*) AS count
10777
+ FROM (${compiled.sql}) AS input
10778
+ WHERE json_type(input.properties, ?) IS NOT NULL
10779
+ GROUP BY value_type, value
10780
+ ORDER BY count DESC, CAST(value AS TEXT) ASC
10781
+ LIMIT ?
10782
+ `).all(path, path, ...compiled.args, path, facet.limit);
10783
+ return rows.map((row) => ({
10784
+ value: sqliteFacetValue(row.value_type, row.value),
10785
+ count: Number(row.count)
10786
+ }));
10787
+ }
10788
+ function sqliteJsonPath(propertyId) {
10789
+ return `$."${propertyId.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
10790
+ }
10791
+ function sqliteFacetValue(valueType, value) {
10792
+ if (valueType === "true")
10793
+ return true;
10794
+ if (valueType === "false")
10795
+ return false;
10796
+ if (valueType === "null")
10797
+ return null;
10798
+ return value;
10799
+ }
10800
+ function existsProbeQuery(query) {
10801
+ return { kind: "limit", limit: 1, input: stripOuterRowShape(query) };
10802
+ }
10803
+ function stripOuterRowShape(query) {
10804
+ switch (query.kind) {
10805
+ case "limit":
10806
+ case "page":
10807
+ case "project":
10808
+ case "sort":
10809
+ return stripOuterRowShape(query.input);
10810
+ default:
10811
+ return query;
10812
+ }
10813
+ }
10814
+
10815
+ // src/objects/selected-reader.ts
10816
+ import {
10817
+ assertObjectReaderProject,
10818
+ assertObjectReadFacetCount,
10819
+ assertObjectReadOutputWithinLimit,
10820
+ ObjectReadLimitExceededError,
10821
+ snapshotObjectReadExecutionLimits
10822
+ } from "@sixb/core/storage";
10823
+
10824
+ // src/objects/read-scope.ts
10825
+ function compileSqliteSelectedObjectReadSource(projectId, scope, maxTraversalFacts) {
10826
+ const scopeJson = JSON.stringify({
10827
+ roots: scope.roots,
10828
+ objects: scope.objects,
10829
+ steps: scope.steps
10830
+ });
10831
+ const compiled = compileSelectedReadScopeCte(scopeJson, projectId);
10832
+ const wrapStatement = (sql, args = []) => ({
10833
+ sql: `${compiled.sql}
10834
+ ${sql}`,
10835
+ args: [...compiled.args, ...args]
10836
+ });
10837
+ return Object.freeze({
10838
+ objectsTable: "_sixb_scope_objects",
10839
+ linksTable: "_sixb_scope_links",
10840
+ objectPropertyPermissionsTable: "_sixb_scope_object_permissions",
10841
+ wrapStatement,
10842
+ wrapQuery: (query) => ({
10843
+ ...query,
10844
+ ...wrapStatement(query.sql, query.args),
10845
+ totalSql: `${compiled.sql}
10846
+ ${query.totalSql}`,
10847
+ totalArgs: [...compiled.args, ...query.totalArgs],
10848
+ ...query.hasMoreProbe ? {
10849
+ hasMoreProbe: {
10850
+ ...query.hasMoreProbe,
10851
+ ...wrapStatement(query.hasMoreProbe.sql, query.hasMoreProbe.args)
10852
+ }
10853
+ } : {}
10854
+ }),
10855
+ traversalProbe: wrapStatement(`
10856
+ SELECT COUNT(*) AS total
10857
+ FROM (
10858
+ SELECT 1 AS traversal_fact
10859
+ FROM _sixb_scope_root_facts
10860
+
10861
+ UNION ALL
10862
+
10863
+ SELECT 1 AS traversal_fact
10864
+ FROM _sixb_scope_reached_links
10865
+
10866
+ LIMIT ?
10867
+ ) AS bounded_traversal_facts
10868
+ `, [BigInt(maxTraversalFacts) + 1n])
10869
+ });
10870
+ }
10871
+ function compileSelectedReadScopeCte(scopeJson, projectId) {
10872
+ return {
10873
+ sql: `
10874
+ WITH RECURSIVE
10875
+ _sixb_scope_document(scope) AS (
10876
+ SELECT json(?)
10877
+ ),
10878
+ _sixb_scope_roots(root_id, node_id, object_type_id, primary_id) AS (
10879
+ SELECT
10880
+ CAST(root.key AS INTEGER),
10881
+ CAST(json_extract(root.value, '$.nodeId') AS INTEGER),
10882
+ CAST(json_extract(root.value, '$.objectTypeId') AS TEXT),
10883
+ CAST(json_extract(root.value, '$.primaryId') AS TEXT)
10884
+ FROM _sixb_scope_document AS document,
10885
+ json_each(json_extract(document.scope, '$.roots')) AS root
10886
+ ),
10887
+ _sixb_scope_root_facts(project_id, root_id, node_id, object_type_id, primary_id) AS (
10888
+ SELECT anchor.project_id, root.root_id, root.node_id, root.object_type_id, root.primary_id
10889
+ FROM _sixb_scope_roots AS root
10890
+ JOIN objects AS anchor
10891
+ ON anchor.project_id = ?
10892
+ AND anchor.object_type_id = root.object_type_id
10893
+ AND anchor.primary_id = root.primary_id
10894
+ ),
10895
+ _sixb_scope_object_selections(node_id, object_type_id, property_ids) AS (
10896
+ SELECT
10897
+ CAST(json_extract(selection.value, '$.nodeId') AS INTEGER),
10898
+ CAST(json_extract(selection.value, '$.objectTypeId') AS TEXT),
10899
+ json_extract(selection.value, '$.propertyIds')
10900
+ FROM _sixb_scope_document AS document,
10901
+ json_each(json_extract(document.scope, '$.objects')) AS selection
10902
+ ),
10903
+ _sixb_scope_link_selections(
10904
+ step_id,
10905
+ node_id,
10906
+ parent_node_id,
10907
+ source_object_type_id,
10908
+ link_id,
10909
+ target_object_type_id,
10910
+ property_ids
10911
+ ) AS (
10912
+ SELECT
10913
+ CAST(step.key AS INTEGER),
10914
+ CAST(json_extract(step.value, '$.nodeId') AS INTEGER),
10915
+ CAST(json_extract(step.value, '$.parentNodeId') AS INTEGER),
10916
+ CAST(json_extract(step.value, '$.sourceObjectTypeId') AS TEXT),
10917
+ CAST(json_extract(step.value, '$.linkId') AS TEXT),
10918
+ CAST(json_extract(step.value, '$.targetObjectTypeId') AS TEXT),
10919
+ json_extract(step.value, '$.propertyIds')
10920
+ FROM _sixb_scope_document AS document,
10921
+ json_each(json_extract(document.scope, '$.steps')) AS step
10922
+ ),
10923
+ _sixb_scope_reachable(project_id, node_id, object_type_id, primary_id) AS (
10924
+ SELECT root.project_id, root.node_id, root.object_type_id, root.primary_id
10925
+ FROM _sixb_scope_root_facts AS root
10926
+
10927
+ UNION
10928
+
10929
+ SELECT edge.project_id, selection.node_id, edge.target_type_id, edge.target_id
10930
+ FROM _sixb_scope_reachable AS parent
10931
+ JOIN _sixb_scope_link_selections AS selection
10932
+ ON selection.parent_node_id = parent.node_id
10933
+ AND selection.source_object_type_id = parent.object_type_id
10934
+ JOIN links AS edge
10935
+ ON edge.project_id = parent.project_id
10936
+ AND edge.source_type_id = parent.object_type_id
10937
+ AND edge.source_id = parent.primary_id
10938
+ AND edge.link_id = selection.link_id
10939
+ AND edge.target_type_id = selection.target_object_type_id
10940
+ JOIN objects AS target
10941
+ ON target.project_id = edge.project_id
10942
+ AND target.object_type_id = edge.target_type_id
10943
+ AND target.primary_id = edge.target_id
10944
+ ),
10945
+ _sixb_scope_object_permissions(project_id, object_type_id, primary_id, property_id) AS (
10946
+ SELECT DISTINCT
10947
+ reachable.project_id,
10948
+ reachable.object_type_id,
10949
+ reachable.primary_id,
10950
+ CAST(selected_property.value AS TEXT)
10951
+ FROM _sixb_scope_reachable AS reachable
10952
+ JOIN _sixb_scope_object_selections AS selection
10953
+ ON selection.node_id = reachable.node_id
10954
+ AND selection.object_type_id = reachable.object_type_id
10955
+ JOIN json_each(selection.property_ids) AS selected_property
10956
+ ),
10957
+ _sixb_scope_object_ids(project_id, object_type_id, primary_id) AS (
10958
+ SELECT DISTINCT project_id, object_type_id, primary_id
10959
+ FROM _sixb_scope_reachable
10960
+ ),
10961
+ _sixb_scope_objects AS (
10962
+ SELECT
10963
+ stored.project_id,
10964
+ stored.object_type_id,
10965
+ stored.primary_id,
10966
+ COALESCE(
10967
+ (
10968
+ SELECT json_group_object(property.key, ${sqliteJsonEachValue("property")})
10969
+ FROM json_each(stored.properties) AS property
10970
+ JOIN _sixb_scope_object_permissions AS permission
10971
+ ON permission.project_id = stored.project_id
10972
+ AND permission.object_type_id = stored.object_type_id
10973
+ AND permission.primary_id = stored.primary_id
10974
+ AND permission.property_id = property.key
10975
+ ),
10976
+ json('{}')
10977
+ ) AS properties,
10978
+ stored.created_at,
10979
+ stored.updated_at,
10980
+ stored.version,
10981
+ stored.last_commit_id
10982
+ FROM objects AS stored
10983
+ JOIN _sixb_scope_object_ids AS visible
10984
+ ON visible.project_id = stored.project_id
10985
+ AND visible.object_type_id = stored.object_type_id
10986
+ AND visible.primary_id = stored.primary_id
10987
+ ),
10988
+ _sixb_scope_reached_links(
10989
+ project_id,
10990
+ step_id,
10991
+ node_id,
10992
+ parent_node_id,
10993
+ source_type_id,
10994
+ source_id,
10995
+ link_id,
10996
+ target_type_id,
10997
+ target_id,
10998
+ property_ids
10999
+ ) AS (
11000
+ SELECT
11001
+ edge.project_id,
11002
+ selection.step_id,
11003
+ selection.node_id,
11004
+ selection.parent_node_id,
11005
+ edge.source_type_id,
11006
+ edge.source_id,
11007
+ edge.link_id,
11008
+ edge.target_type_id,
11009
+ edge.target_id,
11010
+ selection.property_ids
11011
+ FROM _sixb_scope_reachable AS parent
11012
+ JOIN _sixb_scope_link_selections AS selection
11013
+ ON selection.parent_node_id = parent.node_id
11014
+ AND selection.source_object_type_id = parent.object_type_id
11015
+ JOIN links AS edge
11016
+ ON edge.project_id = parent.project_id
11017
+ AND edge.source_type_id = parent.object_type_id
11018
+ AND edge.source_id = parent.primary_id
11019
+ AND edge.link_id = selection.link_id
11020
+ AND edge.target_type_id = selection.target_object_type_id
11021
+ JOIN objects AS target
11022
+ ON target.project_id = edge.project_id
11023
+ AND target.object_type_id = edge.target_type_id
11024
+ AND target.primary_id = edge.target_id
11025
+ ),
11026
+ _sixb_scope_link_permissions(
11027
+ project_id,
11028
+ source_type_id,
11029
+ source_id,
11030
+ link_id,
11031
+ target_type_id,
11032
+ target_id,
11033
+ property_id
11034
+ ) AS (
11035
+ SELECT DISTINCT
11036
+ reached.project_id,
11037
+ reached.source_type_id,
11038
+ reached.source_id,
11039
+ reached.link_id,
11040
+ reached.target_type_id,
11041
+ reached.target_id,
11042
+ CAST(selected_property.value AS TEXT)
11043
+ FROM _sixb_scope_reached_links AS reached
11044
+ JOIN json_each(reached.property_ids) AS selected_property
11045
+ ),
11046
+ _sixb_scope_link_ids(
11047
+ project_id,
11048
+ source_type_id,
11049
+ source_id,
11050
+ link_id,
11051
+ target_type_id,
11052
+ target_id
11053
+ ) AS (
11054
+ SELECT DISTINCT
11055
+ project_id,
11056
+ source_type_id,
11057
+ source_id,
11058
+ link_id,
11059
+ target_type_id,
11060
+ target_id
11061
+ FROM _sixb_scope_reached_links
11062
+ ),
11063
+ _sixb_scope_links AS (
11064
+ SELECT
11065
+ stored.project_id,
11066
+ stored.source_type_id,
11067
+ stored.source_id,
11068
+ stored.link_id,
11069
+ stored.target_type_id,
11070
+ stored.target_id,
11071
+ CASE
11072
+ WHEN stored.properties IS NULL THEN NULL
11073
+ ELSE NULLIF(
11074
+ (
11075
+ SELECT json_group_object(property.key, ${sqliteJsonEachValue("property")})
11076
+ FROM json_each(stored.properties) AS property
11077
+ JOIN _sixb_scope_link_permissions AS permission
11078
+ ON permission.project_id = stored.project_id
11079
+ AND permission.source_type_id = stored.source_type_id
11080
+ AND permission.source_id = stored.source_id
11081
+ AND permission.link_id = stored.link_id
11082
+ AND permission.target_type_id = stored.target_type_id
11083
+ AND permission.target_id = stored.target_id
11084
+ AND permission.property_id = property.key
11085
+ ),
11086
+ json('{}')
11087
+ )
11088
+ END AS properties,
11089
+ stored.created_at,
11090
+ stored.updated_at,
11091
+ stored.last_commit_id
11092
+ FROM links AS stored
11093
+ JOIN _sixb_scope_link_ids AS visible
11094
+ ON visible.project_id = stored.project_id
11095
+ AND visible.source_type_id = stored.source_type_id
11096
+ AND visible.source_id = stored.source_id
11097
+ AND visible.link_id = stored.link_id
11098
+ AND visible.target_type_id = stored.target_type_id
11099
+ AND visible.target_id = stored.target_id
11100
+ )
11101
+ `,
11102
+ args: [scopeJson, projectId]
11103
+ };
11104
+ }
11105
+
11106
+ // src/objects/selected-reader.ts
11107
+ function createSqliteSelectedReader(db, params, queryCapabilities) {
11108
+ const projectId = params.projectId;
11109
+ const limits = snapshotObjectReadExecutionLimits(params.limits);
11110
+ const source = compileSqliteSelectedObjectReadSource(projectId, params.scope, limits.maxTraversalFacts);
11111
+ const reader = new SqliteObjectReader(db, source);
11112
+ const assertProject = (actualProjectId) => assertObjectReaderProject(projectId, actualProjectId);
11113
+ const read = (run) => runDeferredReadTransaction(db, () => {
11114
+ assertTraversalBudget(db, source, limits.maxTraversalFacts);
11115
+ const value = run();
11116
+ assertObjectReadOutputWithinLimit(value, limits);
11117
+ return value;
11118
+ });
11119
+ const readMap = (run) => runDeferredReadTransaction(db, () => {
11120
+ assertTraversalBudget(db, source, limits.maxTraversalFacts);
11121
+ const value = run();
11122
+ assertObjectReadOutputWithinLimit([...value.entries()], limits);
11123
+ return value;
11124
+ });
11125
+ return Object.freeze({
11126
+ queryCapabilities,
11127
+ queryObjects: async (input) => {
11128
+ assertProject(input.projectId);
11129
+ return read(() => reader.queryObjects(input));
11130
+ },
11131
+ countObjects: async (input) => {
11132
+ assertProject(input.projectId);
11133
+ return read(() => reader.countObjects(input));
11134
+ },
11135
+ existsObjects: async (input) => {
11136
+ assertProject(input.projectId);
11137
+ return read(() => reader.existsObjects(input));
11138
+ },
11139
+ facetObjects: async (input) => {
11140
+ assertProject(input.projectId);
11141
+ assertObjectReadFacetCount(input.facets.length);
11142
+ return read(() => reader.facetObjects(input));
11143
+ },
11144
+ getByPrimaryId: async (input) => {
11145
+ assertProject(input.projectId);
11146
+ return read(() => reader.getByPrimaryId(input));
11147
+ },
11148
+ selectsObjectProperties: async (input) => {
11149
+ assertProject(input.projectId);
11150
+ return read(() => reader.selectsObjectProperties(input));
11151
+ },
11152
+ listLinks: async (input) => {
11153
+ assertProject(input.projectId);
11154
+ return read(() => reader.listLinks(input));
11155
+ },
11156
+ getByPrimaryIdBatch: async (input) => {
11157
+ assertProject(input.projectId);
11158
+ return readMap(() => reader.getByPrimaryIdBatch(input));
11159
+ },
11160
+ listLinksBatch: async (input) => {
11161
+ assertProject(input.projectId);
11162
+ return readMap(() => reader.listLinksBatch(input));
11163
+ },
11164
+ queryLinks: async (input) => {
11165
+ assertProject(input.projectId);
11166
+ assertLinkQueryLimit(input.limit);
11167
+ return read(() => reader.queryLinks(input));
11168
+ },
11169
+ list: async (input) => {
11170
+ assertProject(input.projectId);
11171
+ return read(() => reader.list(input));
11172
+ }
11173
+ });
11174
+ }
11175
+ function assertTraversalBudget(db, source, maxTraversalFacts) {
11176
+ const row = db.query(source.traversalProbe.sql).get(...source.traversalProbe.args);
11177
+ if (BigInt(row.total) > BigInt(maxTraversalFacts)) {
11178
+ throw new ObjectReadLimitExceededError("traversalFacts", maxTraversalFacts);
11179
+ }
11180
+ }
11181
+
11182
+ // src/objects/storage.ts
11183
+ var SQLITE_OBJECT_QUERY_CAPABILITIES = {
11184
+ queryObjects: true,
11185
+ countObjects: true,
11186
+ existsObjects: true,
11187
+ facetObjects: true,
11188
+ nodes: {
11189
+ start: true,
11190
+ refs: true,
11191
+ filter: true,
11192
+ text: true,
11193
+ sort: true,
11194
+ limit: true,
11195
+ page: true,
11196
+ traverse: true,
11197
+ set: true,
11198
+ project: true,
11199
+ expand: true
11200
+ },
11201
+ predicateOps: {
11202
+ and: true,
11203
+ or: true,
11204
+ not: true,
11205
+ eq: true,
11206
+ neq: true,
11207
+ lt: true,
11208
+ lte: true,
11209
+ gt: true,
11210
+ gte: true,
11211
+ in: true,
11212
+ exists: true,
11213
+ contains: true
11214
+ },
11215
+ sortKinds: {
11216
+ property: true
11217
+ },
11218
+ traversalDirections: {
11219
+ outgoing: true,
11220
+ incoming: true
11221
+ },
11222
+ setOps: {
11223
+ union: true,
11224
+ intersect: true,
11225
+ subtract: true
11226
+ },
11227
+ scalarOperations: {
11228
+ string: { equality: true, ordering: true },
11229
+ uuid: { equality: true, ordering: true },
11230
+ boolean: { equality: true },
11231
+ integer: { equality: true, ordering: true },
11232
+ double: { equality: true, ordering: true },
11233
+ decimal: { equality: true },
11234
+ date: { equality: true, ordering: true },
11235
+ timestamp: { equality: true, ordering: true }
11236
+ },
11237
+ limits: {
11238
+ totalCount: true,
11239
+ stablePageTokens: true
11240
+ },
11241
+ notes: [
11242
+ "SQLite object query pushdown supports start/refs/filter/text/sort/limit/page/traverse/set/project/expand over JSON properties and object links.",
11243
+ "expand hydrates linked objects in-database (top-N per parent via row_number() + json_group_array); core resolves each expansion's cardinality before pushdown, and a mixed/unresolved one stays on the fallback.",
11244
+ "Ordered decimal predicates and sorting use the bounded core fallback because SQLite has no native exact decimal type; canonical decimal equality remains pushdown-safe.",
11245
+ "Relevance sorting, vector search, and unresolved start.includeSubtypes remain planner fallback or rejection cases."
11246
+ ]
11247
+ };
11248
+
11249
+ class SqliteObjectStorage {
11250
+ connection;
11251
+ db;
11252
+ reader;
11253
+ constructor(options = {}) {
11254
+ this.connection = openSqliteStoreConnection(options);
11255
+ this.db = this.connection.db;
11256
+ this.reader = new SqliteObjectReader(this.db, DEFAULT_OBJECT_QUERY_SOURCE);
11257
+ if (this.connection.installFreshSchema) {
11258
+ installFreshSqliteSchema(this.db);
11259
+ }
11260
+ }
11261
+ queryCapabilities() {
11262
+ return SQLITE_OBJECT_QUERY_CAPABILITIES;
11263
+ }
11264
+ createSelectedReadScope(params) {
11265
+ return createSqliteSelectedReader(this.db, params, () => this.queryCapabilities());
11266
+ }
11267
+ async queryObjects(params) {
11268
+ return this.reader.queryObjects(params);
11269
+ }
11270
+ async countObjects(params) {
11271
+ return this.reader.countObjects(params);
11272
+ }
11273
+ async existsObjects(params) {
11274
+ return this.reader.existsObjects(params);
11275
+ }
11276
+ async facetObjects(params) {
11277
+ return this.reader.facetObjects(params);
11278
+ }
11279
+ async getByPrimaryId(params) {
11280
+ return this.reader.getByPrimaryId(params);
11281
+ }
11282
+ async selectsObjectProperties(params) {
11283
+ return this.reader.selectsObjectProperties(params);
11284
+ }
11285
+ async listLinks(params) {
11286
+ return this.reader.listLinks(params);
11287
+ }
11288
+ async getByPrimaryIdBatch(params) {
11289
+ return this.reader.getByPrimaryIdBatch(params);
11290
+ }
11291
+ async listLinksBatch(params) {
11292
+ return this.reader.listLinksBatch(params);
11293
+ }
11294
+ async queryLinks(params) {
11295
+ assertLinkQueryLimit(params.limit);
11296
+ return this.reader.queryLinks(params);
10340
11297
  }
10341
- queryRowToObject(row) {
10342
- const base = this.rowToObject(row);
10343
- const links = reviveExpandedLinks(parseExpandColumn(row._expand));
10344
- return links ? { ...base, links } : base;
11298
+ async listIncidentLinksBatch(params) {
11299
+ if (params.items.length === 0)
11300
+ return [];
11301
+ const rows = this.db.query(`
11302
+ WITH requested AS (
11303
+ SELECT
11304
+ json_extract(value, '$.objectTypeId') AS object_type_id,
11305
+ json_extract(value, '$.objectId') AS object_id
11306
+ FROM json_each(?)
11307
+ )
11308
+ SELECT link.*
11309
+ FROM links AS link
11310
+ JOIN requested
11311
+ ON requested.object_type_id = link.source_type_id
11312
+ AND requested.object_id = link.source_id
11313
+ WHERE link.project_id = ?
11314
+ UNION
11315
+ SELECT link.*
11316
+ FROM links AS link
11317
+ JOIN requested
11318
+ ON requested.object_type_id = link.target_type_id
11319
+ AND requested.object_id = link.target_id
11320
+ WHERE link.project_id = ?
11321
+ `).all(JSON.stringify(params.items), params.projectId, params.projectId);
11322
+ return rows.map((row) => rowToLink(row));
10345
11323
  }
10346
- rowToLink(row) {
11324
+ async listByPrimaryIdPage(params) {
11325
+ assertReconciliationPageLimit(params.limit);
11326
+ const rows = this.db.query(`
11327
+ SELECT * FROM objects
11328
+ WHERE project_id = ? AND object_type_id = ?
11329
+ AND (? IS NULL OR primary_id > ?)
11330
+ ORDER BY primary_id ASC
11331
+ LIMIT ?
11332
+ `).all(params.projectId, params.objectTypeId, params.afterPrimaryId ?? null, params.afterPrimaryId ?? null, params.limit + 1);
11333
+ const hasMore = rows.length > params.limit;
11334
+ const objects = rows.slice(0, params.limit).map((row) => rowToObject(row));
11335
+ const last = objects.at(-1);
10347
11336
  return {
10348
- projectId: row.project_id,
10349
- sourceTypeId: row.source_type_id,
10350
- sourceId: row.source_id,
10351
- linkId: row.link_id,
10352
- targetTypeId: row.target_type_id,
10353
- targetId: row.target_id,
10354
- properties: row.properties ? JSON.parse(row.properties) : undefined,
10355
- createdAt: new Date(row.created_at),
10356
- updatedAt: new Date(row.updated_at),
10357
- lastCommitId: row.last_commit_id
11337
+ objects,
11338
+ ...hasMore && last ? { nextPrimaryId: last.primaryId } : {}
10358
11339
  };
10359
11340
  }
11341
+ async list(params) {
11342
+ return this.reader.list(params);
11343
+ }
10360
11344
  close() {
10361
11345
  closeSqliteStoreConnection(this.connection);
10362
11346
  }
@@ -10366,104 +11350,6 @@ function assertReconciliationPageLimit(limit) {
10366
11350
  throw new Error("Object reconciliation page limit must be a positive safe integer.");
10367
11351
  }
10368
11352
  }
10369
- function assertLinkQueryLimit(limit) {
10370
- if (!Number.isSafeInteger(limit) || limit <= 0) {
10371
- throw new Error("Object link query limit must be a positive safe integer.");
10372
- }
10373
- }
10374
- function readTotal(db, compiled) {
10375
- const row = db.query(compiled.totalSql).get(...compiled.totalArgs);
10376
- return row.total;
10377
- }
10378
- function readFacetBuckets(db, compiled, facet) {
10379
- const path = sqliteJsonPath(facet.propertyId);
10380
- const rows = db.query(`
10381
- SELECT
10382
- json_type(input.properties, ?) AS value_type,
10383
- json_extract(input.properties, ?) AS value,
10384
- COUNT(*) AS count
10385
- FROM (${compiled.sql}) AS input
10386
- WHERE json_type(input.properties, ?) IS NOT NULL
10387
- GROUP BY value_type, value
10388
- ORDER BY count DESC, CAST(value AS TEXT) ASC
10389
- LIMIT ?
10390
- `).all(path, path, ...compiled.args, path, facet.limit);
10391
- return rows.map((row) => ({
10392
- value: sqliteFacetValue(row.value_type, row.value),
10393
- count: Number(row.count)
10394
- }));
10395
- }
10396
- function sqliteJsonPath(propertyId) {
10397
- return `$."${propertyId.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`;
10398
- }
10399
- function sqliteFacetValue(valueType, value) {
10400
- if (valueType === "true")
10401
- return true;
10402
- if (valueType === "false")
10403
- return false;
10404
- if (valueType === "null")
10405
- return null;
10406
- return value;
10407
- }
10408
- function parseExpandColumn(value) {
10409
- if (typeof value !== "string")
10410
- return value ?? undefined;
10411
- return JSON.parse(value);
10412
- }
10413
- function reviveExpandedLinks(value) {
10414
- if (!isPlainObject2(value))
10415
- return;
10416
- const links = {};
10417
- for (const [linkId, raw] of Object.entries(value)) {
10418
- links[linkId] = reviveExpandedLinkValue(raw);
10419
- }
10420
- return links;
10421
- }
10422
- function reviveExpandedLinkValue(value) {
10423
- if (value === null || value === undefined)
10424
- return null;
10425
- if (Array.isArray(value))
10426
- return value.map(reviveExpandedRow);
10427
- return reviveExpandedRow(value);
10428
- }
10429
- function reviveExpandedRow(value) {
10430
- const row = isPlainObject2(value) ? value : {};
10431
- const expanded = {
10432
- projectId: String(row.projectId),
10433
- objectTypeId: String(row.objectTypeId),
10434
- primaryId: String(row.primaryId),
10435
- properties: isPlainObject2(row.properties) ? row.properties : {},
10436
- createdAt: new Date(row.createdAt),
10437
- updatedAt: new Date(row.updatedAt),
10438
- version: Number(row.version),
10439
- lastCommitId: String(row.lastCommitId)
10440
- };
10441
- if (isPlainObject2(row.linkProperties) && Object.keys(row.linkProperties).length > 0) {
10442
- expanded.linkProperties = row.linkProperties;
10443
- }
10444
- const links = reviveExpandedLinks(row.links);
10445
- if (links)
10446
- expanded.links = links;
10447
- return expanded;
10448
- }
10449
- function isPlainObject2(value) {
10450
- return typeof value === "object" && value !== null && !Array.isArray(value);
10451
- }
10452
- function existsProbeQuery(query) {
10453
- return { kind: "limit", limit: 1, input: stripOuterRowShape(query) };
10454
- }
10455
- function stripOuterRowShape(query) {
10456
- switch (query.kind) {
10457
- case "limit":
10458
- case "page":
10459
- case "project":
10460
- case "sort":
10461
- return stripOuterRowShape(query.input);
10462
- default:
10463
- return query;
10464
- }
10465
- }
10466
-
10467
11353
  // src/ontology-storage/shared.ts
10468
11354
  import { parseSixbFailure as parseSixbFailure2 } from "@sixb/core/internal/errors";
10469
11355
  import { MaterializationConflictError } from "@sixb/core/internal/materialization";
@@ -11019,13 +11905,13 @@ class SqliteMaterializationStateReader {
11019
11905
  ORDER BY requested.scope_sort_key, ${linkSortExpression("links")}
11020
11906
  `).iterate(requestedJson, this.projectId);
11021
11907
  for (const row of rows) {
11022
- const accumulator2 = accumulators.get(row.scope_sort_key);
11023
- if (!accumulator2) {
11908
+ const accumulator = accumulators.get(row.scope_sort_key);
11909
+ if (!accumulator) {
11024
11910
  throw new MaterializationConflictError2("effective-state", `Unexpected link scope '${row.scope_sort_key}' returned by storage.`);
11025
11911
  }
11026
11912
  const snapshot = effectiveLinkSnapshot(row);
11027
- appendScopeSnapshot(accumulator2, snapshot);
11028
- effective.set(row.scope_sort_key, accumulator2.effectiveCount === 1 ? snapshot : null);
11913
+ appendScopeSnapshot(accumulator, snapshot);
11914
+ effective.set(row.scope_sort_key, accumulator.effectiveCount === 1 ? snapshot : null);
11029
11915
  }
11030
11916
  return {
11031
11917
  revisions: scopes.map(({ source, linkId }) => finishScopeAccumulator(accumulators.get(linkScopeSortKey(source, linkId)))),
@@ -13037,11 +13923,8 @@ class SqliteOntologyStorage {
13037
13923
  // src/pipeline-run-storage.ts
13038
13924
  import { parseSixbFailure as parseSixbFailure3, serializeSixbFailure as serializeSixbFailure3 } from "@sixb/core/internal/errors";
13039
13925
  import { assertPipelineRunExecution } from "@sixb/core/internal/pipeline-run-storage-provider";
13040
- import {
13041
- canRequeuePipelineRunAfterEnqueueFailure,
13042
- PIPELINE_RUN_FAILURE_CODES,
13043
- PipelineRunError
13044
- } from "@sixb/core/storage";
13926
+ import { canRequeuePipelineRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
13927
+ import { PIPELINE_RUN_FAILURE_CODES, PipelineRunError } from "@sixb/core/storage";
13045
13928
 
13046
13929
  // src/latest-run-query.ts
13047
13930
  function queryLatestRunsByOwnerId(input) {
@@ -14069,14 +14952,263 @@ function rowToRuleStateRecord(row) {
14069
14952
  };
14070
14953
  }
14071
14954
 
14955
+ // src/share-grant-storage.ts
14956
+ import {
14957
+ normalizeGetShareGrantByIdInput,
14958
+ normalizeListShareGrantsInput,
14959
+ normalizeRevokeShareGrantInput,
14960
+ normalizeShareGrantCreate,
14961
+ parseShareGrantRecord
14962
+ } from "@sixb/core/internal/share-grant-storage-provider";
14963
+ import { ShareGrantStorageError } from "@sixb/core/storage";
14964
+ class SqliteShareGrantStorage {
14965
+ connection;
14966
+ db;
14967
+ constructor(options = {}) {
14968
+ this.connection = openSqliteStoreConnection(options);
14969
+ this.db = this.connection.db;
14970
+ if (this.connection.installFreshSchema)
14971
+ installFreshSqliteSchema(this.db);
14972
+ }
14973
+ async create(input) {
14974
+ const record = normalizeShareGrantCreate(input);
14975
+ try {
14976
+ this.db.query(`
14977
+ INSERT INTO share_grants (
14978
+ project_id, id, definition_id, target_object_type_id, target_primary_id,
14979
+ issued_by_type, issued_by_id, authority_version, authority_snapshot,
14980
+ authority_digest, token_hash, destination_path, created_at, expires_at
14981
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
14982
+ `).run(record.projectId, record.id, record.definitionId, record.target.objectTypeId, record.target.primaryId, record.issuedBy.type, record.issuedBy.id, record.authoritySnapshot.version, JSON.stringify(record.authoritySnapshot), record.authorityDigest, record.tokenHash, record.destinationPath, record.createdAt.toISOString(), record.expiresAt.toISOString());
14983
+ } catch (error) {
14984
+ if (isUniqueConstraintError2(error)) {
14985
+ throw new ShareGrantStorageError("duplicate", `[SixbSqlite] Share grant '${record.id}' conflicts with an existing record.`, { cause: error });
14986
+ }
14987
+ throw error;
14988
+ }
14989
+ return this.require(record.projectId, record.id, "create");
14990
+ }
14991
+ async getById(input) {
14992
+ const normalized = normalizeGetShareGrantByIdInput(input);
14993
+ return this.getRow(normalized.projectId, normalized.id);
14994
+ }
14995
+ async list(input) {
14996
+ const normalized = normalizeListShareGrantsInput(input);
14997
+ const where = ["project_id = ?"];
14998
+ const args = [normalized.projectId];
14999
+ if (normalized.definitionId !== undefined) {
15000
+ where.push("definition_id = ?");
15001
+ args.push(normalized.definitionId);
15002
+ }
15003
+ if (normalized.target !== undefined) {
15004
+ where.push("target_object_type_id = ?", "target_primary_id = ?");
15005
+ args.push(normalized.target.objectTypeId, normalized.target.primaryId);
15006
+ }
15007
+ if (!normalized.includeRevoked)
15008
+ where.push("revoked_at IS NULL");
15009
+ if (!normalized.includeExpired) {
15010
+ where.push("expires_at > ?");
15011
+ args.push(normalized.now.toISOString());
15012
+ }
15013
+ const predicate = where.join(" AND ");
15014
+ const totalRow = this.db.query(`SELECT COUNT(*) AS total FROM share_grants WHERE ${predicate}`).get(...args);
15015
+ const rows = this.db.query(`
15016
+ SELECT * FROM share_grants
15017
+ WHERE ${predicate}
15018
+ ORDER BY created_at DESC, id DESC
15019
+ LIMIT ? OFFSET ?
15020
+ `).all(...args, normalized.limit, normalized.offset);
15021
+ const grants = rows.map(rowToRecord);
15022
+ return {
15023
+ grants,
15024
+ total: totalRow.total,
15025
+ hasMore: normalized.offset + grants.length < totalRow.total
15026
+ };
15027
+ }
15028
+ async revoke(input) {
15029
+ const normalized = normalizeRevokeShareGrantInput(input);
15030
+ const current = this.getRow(normalized.projectId, normalized.id);
15031
+ if (!current || current.revokedAt)
15032
+ return current;
15033
+ const revocation = normalizeRevokeShareGrantInput(normalized, current.createdAt);
15034
+ this.db.query(`
15035
+ UPDATE share_grants
15036
+ SET revoked_at = ?, revoked_by_type = ?, revoked_by_id = ?
15037
+ WHERE project_id = ? AND id = ? AND revoked_at IS NULL
15038
+ `).run(revocation.revokedAt.toISOString(), revocation.revokedBy.type, revocation.revokedBy.id, revocation.projectId, revocation.id);
15039
+ return this.require(revocation.projectId, revocation.id, "revoke");
15040
+ }
15041
+ close() {
15042
+ closeSqliteStoreConnection(this.connection);
15043
+ }
15044
+ getRow(projectId, id) {
15045
+ const row = this.db.query("SELECT * FROM share_grants WHERE project_id = ? AND id = ?").get(projectId, id);
15046
+ return row ? rowToRecord(row) : null;
15047
+ }
15048
+ require(projectId, id, operation) {
15049
+ const record = this.getRow(projectId, id);
15050
+ if (!record) {
15051
+ throw new ShareGrantStorageError("invalid_record", `[SixbSqlite] Share grant '${id}' disappeared after ${operation}.`);
15052
+ }
15053
+ return record;
15054
+ }
15055
+ }
15056
+ function rowToRecord(row) {
15057
+ const authoritySnapshot = parseStoredJson(row.authority_snapshot, "authority snapshot");
15058
+ if (typeof row.authority_version !== "number" || !isRecord(authoritySnapshot) || authoritySnapshot.version !== row.authority_version) {
15059
+ throw invalidRecord("Share authority version does not match its snapshot.");
15060
+ }
15061
+ const hasRevokedAt = row.revoked_at !== null;
15062
+ const hasRevokedBy = row.revoked_by_type !== null || row.revoked_by_id !== null;
15063
+ if (hasRevokedAt !== hasRevokedBy) {
15064
+ throw invalidRecord("Share revocation time and actor must be stored together.");
15065
+ }
15066
+ return parseShareGrantRecord({
15067
+ projectId: row.project_id,
15068
+ id: row.id,
15069
+ definitionId: row.definition_id,
15070
+ target: {
15071
+ objectTypeId: row.target_object_type_id,
15072
+ primaryId: row.target_primary_id
15073
+ },
15074
+ issuedBy: { type: row.issued_by_type, id: row.issued_by_id },
15075
+ authoritySnapshot,
15076
+ authorityDigest: row.authority_digest,
15077
+ tokenHash: row.token_hash,
15078
+ destinationPath: row.destination_path,
15079
+ createdAt: row.created_at,
15080
+ expiresAt: row.expires_at,
15081
+ ...hasRevokedAt ? { revokedAt: row.revoked_at } : {},
15082
+ ...hasRevokedBy ? { revokedBy: { type: row.revoked_by_type, id: row.revoked_by_id } } : {}
15083
+ });
15084
+ }
15085
+ function parseStoredJson(value, field) {
15086
+ if (typeof value !== "string")
15087
+ throw invalidRecord(`Share ${field} must be stored as JSON text.`);
15088
+ try {
15089
+ return JSON.parse(value);
15090
+ } catch (cause) {
15091
+ throw new ShareGrantStorageError("invalid_record", `[SixbSqlite] Share ${field} contains invalid JSON.`, { cause });
15092
+ }
15093
+ }
15094
+ function isRecord(value) {
15095
+ return typeof value === "object" && value !== null && !Array.isArray(value);
15096
+ }
15097
+ function invalidRecord(message) {
15098
+ return new ShareGrantStorageError("invalid_record", `[SixbSqlite] ${message}`);
15099
+ }
15100
+
15101
+ // src/share-session-storage.ts
15102
+ import {
15103
+ normalizeGetShareSessionByIdInput,
15104
+ normalizeRenewShareSessionIfValidInput,
15105
+ normalizeRevokeShareSessionInput,
15106
+ normalizeShareSessionCreate,
15107
+ parseShareSessionRecord
15108
+ } from "@sixb/core/internal/share-session-storage-provider";
15109
+ import { ShareSessionStorageError } from "@sixb/core/storage";
15110
+ class SqliteShareSessionStorage {
15111
+ connection;
15112
+ db;
15113
+ constructor(options = {}) {
15114
+ this.connection = openSqliteStoreConnection(options);
15115
+ this.db = this.connection.db;
15116
+ if (this.connection.installFreshSchema)
15117
+ installFreshSqliteSchema(this.db);
15118
+ }
15119
+ async create(input) {
15120
+ const record = normalizeShareSessionCreate(input);
15121
+ try {
15122
+ const row = this.db.query(`
15123
+ INSERT INTO share_sessions (
15124
+ project_id, id, grant_id, token_hash, created_at, expires_at,
15125
+ absolute_expires_at
15126
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
15127
+ RETURNING *
15128
+ `).get(record.projectId, record.id, record.grantId, record.tokenHash, record.createdAt.toISOString(), record.expiresAt.toISOString(), record.absoluteExpiresAt.toISOString());
15129
+ if (!row)
15130
+ throw invalidRecord2(`Share session '${record.id}' disappeared after create.`);
15131
+ return rowToRecord2(row);
15132
+ } catch (error) {
15133
+ if (isUniqueConstraintError2(error)) {
15134
+ throw new ShareSessionStorageError("duplicate", `[SixbSqlite] Share session '${record.id}' conflicts with an existing record.`, { cause: error });
15135
+ }
15136
+ if (isForeignKeyConstraintError(error)) {
15137
+ throw new ShareSessionStorageError("invalid_input", `[SixbSqlite] Share grant '${record.grantId}' does not exist in project '${record.projectId}'.`, { cause: error });
15138
+ }
15139
+ throw error;
15140
+ }
15141
+ }
15142
+ async getById(input) {
15143
+ const normalized = normalizeGetShareSessionByIdInput(input);
15144
+ return this.getRow(normalized.projectId, normalized.id);
15145
+ }
15146
+ async renewIfValid(input) {
15147
+ const normalized = normalizeRenewShareSessionIfValidInput(input);
15148
+ const requestedExpiry = normalized.expiresAt.toISOString();
15149
+ const now = normalized.now.toISOString();
15150
+ const row = this.db.query(`
15151
+ UPDATE share_sessions
15152
+ SET expires_at = CASE
15153
+ WHEN ? > absolute_expires_at THEN absolute_expires_at
15154
+ WHEN ? > expires_at THEN ?
15155
+ ELSE expires_at
15156
+ END
15157
+ WHERE project_id = ?
15158
+ AND id = ?
15159
+ AND grant_id = ?
15160
+ AND token_hash = ?
15161
+ AND revoked_at IS NULL
15162
+ AND created_at <= ?
15163
+ AND expires_at > ?
15164
+ AND absolute_expires_at > ?
15165
+ RETURNING *
15166
+ `).get(requestedExpiry, requestedExpiry, requestedExpiry, normalized.projectId, normalized.id, normalized.grantId, normalized.tokenHash, now, now, now);
15167
+ return row ? rowToRecord2(row) : null;
15168
+ }
15169
+ async revoke(input) {
15170
+ const normalized = normalizeRevokeShareSessionInput(input);
15171
+ const current = this.getRow(normalized.projectId, normalized.id);
15172
+ if (!current || current.revokedAt)
15173
+ return current;
15174
+ const revocation = normalizeRevokeShareSessionInput(normalized, current.createdAt);
15175
+ const row = this.db.query(`
15176
+ UPDATE share_sessions
15177
+ SET revoked_at = ?
15178
+ WHERE project_id = ? AND id = ? AND revoked_at IS NULL
15179
+ RETURNING *
15180
+ `).get(revocation.revokedAt.toISOString(), revocation.projectId, revocation.id);
15181
+ return row ? rowToRecord2(row) : this.getRow(revocation.projectId, revocation.id);
15182
+ }
15183
+ close() {
15184
+ closeSqliteStoreConnection(this.connection);
15185
+ }
15186
+ getRow(projectId, id) {
15187
+ const row = this.db.query("SELECT * FROM share_sessions WHERE project_id = ? AND id = ?").get(projectId, id);
15188
+ return row ? rowToRecord2(row) : null;
15189
+ }
15190
+ }
15191
+ function rowToRecord2(row) {
15192
+ return parseShareSessionRecord({
15193
+ projectId: row.project_id,
15194
+ id: row.id,
15195
+ grantId: row.grant_id,
15196
+ tokenHash: row.token_hash,
15197
+ createdAt: row.created_at,
15198
+ expiresAt: row.expires_at,
15199
+ absoluteExpiresAt: row.absolute_expires_at,
15200
+ ...row.revoked_at === null ? {} : { revokedAt: row.revoked_at }
15201
+ });
15202
+ }
15203
+ function invalidRecord2(message) {
15204
+ return new ShareSessionStorageError("invalid_record", `[SixbSqlite] ${message}`);
15205
+ }
15206
+
14072
15207
  // src/sync-run-storage.ts
14073
15208
  import { parseSixbFailure as parseSixbFailure4, serializeSixbFailure as serializeSixbFailure5 } from "@sixb/core/internal/errors";
15209
+ import { canRequeueSyncRunAfterEnqueueFailure } from "@sixb/core/internal/storage";
14074
15210
  import { assertSyncRunExecution } from "@sixb/core/internal/sync-run-storage-provider";
14075
- import {
14076
- canRequeueSyncRunAfterEnqueueFailure,
14077
- SYNC_RUN_FAILURE_CODES,
14078
- SyncRunError
14079
- } from "@sixb/core/storage";
15211
+ import { SYNC_RUN_FAILURE_CODES, SyncRunError } from "@sixb/core/storage";
14080
15212
  class SqliteSyncRunStorage {
14081
15213
  connection;
14082
15214
  db;
@@ -14792,7 +15924,6 @@ function assertNonNegativeInteger(value, fieldName) {
14792
15924
  }
14793
15925
 
14794
15926
  // src/workflow-run-storage.ts
14795
- import { normalizeRequesterGroupIds as normalizeRequesterGroupIds2 } from "@sixb/core/internal/auth";
14796
15927
  import { parseSixbFailure as parseSixbFailure6, serializeSixbFailure as serializeSixbFailure7 } from "@sixb/core/internal/errors";
14797
15928
  import {
14798
15929
  assertWorkflowAgentNodeRunExecution,
@@ -14839,10 +15970,9 @@ class SqliteWorkflowRunStorage {
14839
15970
  input,
14840
15971
  queued_at,
14841
15972
  started_at,
14842
- requester_group_ids,
14843
15973
  attempt
14844
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
14845
- `).run(input.projectId, input.id, input.executionId, input.workflowId, "queued", serializeRecord2(input.input), queuedAt.toISOString(), queuedAt.toISOString(), JSON.stringify(normalizeRequesterGroupIds2(input.requesterGroupIds)), 0);
15974
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
15975
+ `).run(input.projectId, input.id, input.executionId, input.workflowId, "queued", serializeRecord2(input.input), queuedAt.toISOString(), queuedAt.toISOString(), 0);
14846
15976
  } catch (error) {
14847
15977
  if (isUniqueConstraintError2(error)) {
14848
15978
  throw new WorkflowRunError(`[SixbSqlite] Workflow run '${input.id}' or execution '${input.executionId}' is already linked for project '${input.projectId}'.`);
@@ -15353,7 +16483,6 @@ function rowToWorkflowRunRecord(row) {
15353
16483
  startedAt: new Date(row.started_at),
15354
16484
  finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
15355
16485
  error: row.error === null ? undefined : parseSixbFailure6(row.error, WORKFLOW_RUN_FAILURE_CODES),
15356
- requesterGroupIds: JSON.parse(row.requester_group_ids),
15357
16486
  attempt: row.attempt,
15358
16487
  ...row.execution_token && row.execution_queue_lease_expires_at ? {
15359
16488
  execution: {
@@ -15444,6 +16573,8 @@ class SqliteStorage {
15444
16573
  timeseries;
15445
16574
  webhookRuns;
15446
16575
  rules;
16576
+ shareGrants;
16577
+ shareSessions;
15447
16578
  connectorConnections;
15448
16579
  migrators;
15449
16580
  connection;
@@ -15475,7 +16606,7 @@ class SqliteStorage {
15475
16606
  }, assertRootOperationAvailable) : scope;
15476
16607
  const readObjects = path ? new SqliteObjectStorage({ connection: this.readConnection }) : stores.objects;
15477
16608
  const readTimeseries = path ? new SqliteTimeseriesStorage({ connection: this.readConnection }) : stores.timeseries;
15478
- this.objects = createOperationScopedFacade(readObjects, readScope);
16609
+ this.objects = createObjectOperationScope(readObjects, readScope);
15479
16610
  this.ontology = createOntologyOperationScope(stores.ontology, ontologyScope);
15480
16611
  this.auth = createAuthOperationScope(stores.auth, scope);
15481
16612
  this.executions = createOperationScopedFacade(stores.executions, scope);
@@ -15492,6 +16623,8 @@ class SqliteStorage {
15492
16623
  this.workflowInterventions = createOperationScopedFacade(stores.workflowInterventions, scope);
15493
16624
  this.webhookRuns = createOperationScopedFacade(stores.webhookRuns, scope);
15494
16625
  this.rules = createOperationScopedFacade(stores.rules, scope);
16626
+ this.shareGrants = createOperationScopedFacade(stores.shareGrants, scope);
16627
+ this.shareSessions = createOperationScopedFacade(stores.shareSessions, scope);
15495
16628
  this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope);
15496
16629
  this.migrators = options.path ? createSqliteStorageMigrators(options.path) : [];
15497
16630
  registerSqliteStorageTestingAdapter(this, (durationMs) => stores.connectorConnections.advanceTimeForTesting(durationMs));
@@ -15590,7 +16723,9 @@ class SqliteStorage {
15590
16723
  }
15591
16724
  function createSqliteStores(connection, options) {
15592
16725
  const auth = new SqliteAuthStorage({ connection });
15593
- const executions = new SqliteExecutionStorage(connection.db, auth);
16726
+ const shareGrants = new SqliteShareGrantStorage({ connection });
16727
+ const shareSessions = new SqliteShareSessionStorage({ connection });
16728
+ const executions = new SqliteExecutionStorage(connection.db, auth, shareSessions);
15594
16729
  return {
15595
16730
  objects: new SqliteObjectStorage({ connection }),
15596
16731
  ontology: new SqliteOntologyStorage({
@@ -15613,12 +16748,14 @@ function createSqliteStores(connection, options) {
15613
16748
  workflowInterventions: new SqliteWorkflowInterventionStorage({ connection }),
15614
16749
  webhookRuns: new SqliteWebhookRunStorage({ connection, executions }),
15615
16750
  rules: new SqliteRulesStorage({ connection }),
16751
+ shareGrants,
16752
+ shareSessions,
15616
16753
  connectorConnections: new SqliteConnectorConnectionStorage(connection)
15617
16754
  };
15618
16755
  }
15619
16756
  export {
15620
- migrateSqliteStorage,
15621
- SqliteStorage
16757
+ SqliteStorage,
16758
+ migrateSqliteStorage
15622
16759
  };
15623
16760
 
15624
- //# debugId=CC3E88BEF909A1E364756E2164756E21
16761
+ //# debugId=8848EEC97F2007FB64756E2164756E21