@haverstack/record-adapter-sqlite 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { TokenSession, StackRecord, RecordVersion, ActorOptions, Permission, TypeId, StackQuery, QueryResult, FileId, StackType, Association } from '@haverstack/core';
1
+ import { TokenSession, StackRecord, RecordChanges, RecordVersion, ActorOptions, TypeId, StackQuery, QueryResult, FileId, StackType, Association } from '@haverstack/core';
2
2
  import { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';
3
3
  import { StackTokenStore, TokenInfo } from '@haverstack/core/wire';
4
4
 
@@ -114,7 +114,7 @@ declare class NativeSQLiteRecordAdapter implements StackRecordAdapter {
114
114
  static open(opts: NativeRecordOpenOptions): Promise<NativeSQLiteRecordAdapter>;
115
115
  createRecord(record: StackRecord): Promise<StackRecord>;
116
116
  getRecord(id: string): Promise<StackRecord | null>;
117
- patchContent(id: string, patch: Record<string, unknown | null>, opts?: {
117
+ mutateRecord(id: string, changes: RecordChanges, opts?: {
118
118
  expectedVersion?: number;
119
119
  snapshot?: RecordVersion;
120
120
  } & ActorOptions): Promise<StackRecord>;
@@ -127,17 +127,11 @@ declare class NativeSQLiteRecordAdapter implements StackRecordAdapter {
127
127
  expectedVersion?: number;
128
128
  snapshot?: RecordVersion;
129
129
  } & ActorOptions): Promise<StackRecord>;
130
- setPermissions(id: string, permissions: Permission[], opts?: {
131
- expectedVersion?: number;
132
- snapshot?: RecordVersion;
133
- } & ActorOptions): Promise<StackRecord>;
134
- setUnlisted(id: string, unlisted: boolean, opts?: {
135
- expectedVersion?: number;
136
- snapshot?: RecordVersion;
137
- } & ActorOptions): Promise<StackRecord>;
138
130
  restoreVersion(id: string, version: number, opts?: {
139
131
  expectedVersion?: number;
140
132
  snapshot?: RecordVersion;
133
+ } & {
134
+ restoreAssociations?: boolean;
141
135
  } & ActorOptions): Promise<StackRecord>;
142
136
  commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
143
137
  expectedVersion?: number;
package/dist/index.js CHANGED
@@ -48,6 +48,9 @@ var RECORD_SCHEMA_SQL = `
48
48
  PRIMARY KEY (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack)
49
49
  ) STRICT;
50
50
 
51
+ -- parent_id holds the container the record sat in, NULL for the root --
52
+ -- the same encoding records.parent_id uses, since a snapshot states
53
+ -- where the record was rather than an instruction to apply.
51
54
  CREATE TABLE IF NOT EXISTS versions (
52
55
  record_id TEXT NOT NULL REFERENCES records(id),
53
56
  version INTEGER NOT NULL,
@@ -57,6 +60,7 @@ var RECORD_SCHEMA_SQL = `
57
60
  entity_id TEXT,
58
61
  updated_by TEXT,
59
62
  updated_via TEXT,
63
+ parent_id TEXT,
60
64
  associations TEXT CHECK (associations IS NULL OR json_valid(associations)),
61
65
  permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions)),
62
66
  PRIMARY KEY (record_id, version)
@@ -556,23 +560,23 @@ var rowToRecord = (row, associations) => {
556
560
  content: JSON.parse(row.content),
557
561
  version: row.version
558
562
  };
559
- if (row.parent_id)
563
+ if (row.parent_id != null)
560
564
  record.parentId = row.parent_id;
561
- if (row.entity_id)
565
+ if (row.entity_id != null)
562
566
  record.entityId = row.entity_id;
563
- if (row.app_id)
567
+ if (row.app_id != null)
564
568
  record.appId = row.app_id;
565
- if (row.principal_id)
569
+ if (row.principal_id != null)
566
570
  record.principalId = row.principal_id;
567
- if (row.updated_by)
571
+ if (row.updated_by != null)
568
572
  record.updatedBy = row.updated_by;
569
- if (row.updated_via)
573
+ if (row.updated_via != null)
570
574
  record.updatedVia = row.updated_via;
571
- if (row.deleted_at)
575
+ if (row.deleted_at != null)
572
576
  record.deletedAt = fromMs(row.deleted_at);
573
- if (row.unlisted_at)
577
+ if (row.unlisted_at != null)
574
578
  record.unlistedAt = fromMs(row.unlisted_at);
575
- if (row.permissions)
579
+ if (row.permissions != null)
576
580
  record.permissions = JSON.parse(row.permissions);
577
581
  if (associations.length)
578
582
  record.associations = associations;
@@ -638,15 +642,17 @@ var rowToVersion = (row) => {
638
642
  content: JSON.parse(row.content),
639
643
  updatedAt: fromMs(row.updated_at)
640
644
  };
641
- if (row.entity_id)
645
+ if (row.entity_id != null)
642
646
  v.entityId = row.entity_id;
643
- if (row.updated_by)
647
+ if (row.updated_by != null)
644
648
  v.updatedBy = row.updated_by;
645
- if (row.updated_via)
649
+ if (row.updated_via != null)
646
650
  v.updatedVia = row.updated_via;
647
- if (row.associations)
651
+ if (row.parent_id != null)
652
+ v.parentId = row.parent_id;
653
+ if (row.associations != null)
648
654
  v.associations = JSON.parse(row.associations);
649
- if (row.permissions)
655
+ if (row.permissions != null)
650
656
  v.permissions = JSON.parse(row.permissions);
651
657
  return v;
652
658
  };
@@ -751,7 +757,7 @@ var SharedSqlRecordLogic = class {
751
757
  }
752
758
  /**
753
759
  * Precondition check for mutations that can't fold expectedVersion into
754
- * their primary UPDATE's WHERE clause: patchContent/restoreVersion need
760
+ * their primary UPDATE's WHERE clause: mutateRecord/restoreVersion need
755
761
  * fts.remove() to run *before* the records-table content changes, so
756
762
  * the check happens first, standalone.
757
763
  */
@@ -842,29 +848,65 @@ var SharedSqlRecordLogic = class {
842
848
  const associations = this.getAssociationsForRecord(id);
843
849
  return rowToRecord(row, associations);
844
850
  }
845
- async patchContent(id, patch, opts = {}) {
851
+ /**
852
+ * Apply a change set in one transaction: one UPDATE over the record row
853
+ * with only the columns the change set names, the association set
854
+ * replaced when it carries one, and the snapshot written alongside — so
855
+ * one call is one version whatever it moved.
856
+ *
857
+ * The expectedVersion check is standalone rather than folded into the
858
+ * UPDATE alone, because a content change needs fts.remove() to run
859
+ * *before* the records-table content changes. The guard is still in the
860
+ * UPDATE's WHERE, so a writer that slipped in between the read and the
861
+ * write is caught there rather than overwriting.
862
+ */
863
+ async mutateRecord(id, changes, opts = {}) {
846
864
  const existing = await this.getRecord(id);
847
865
  if (!existing)
848
- throw new Error(`Record not found: "${id}"`);
866
+ throw new StackNotFoundError(`Record not found: "${id}"`);
849
867
  this.checkExpectedVersion(existing, opts.expectedVersion);
850
- const merged = applyMergePatch(existing.content, patch);
868
+ const merged = changes.contentPatch ? applyMergePatch(existing.content, changes.contentPatch) : void 0;
869
+ const sets = [];
870
+ const values = [];
871
+ if (merged !== void 0) {
872
+ sets.push("content = ?");
873
+ values.push(JSON.stringify(merged));
874
+ }
875
+ if (changes.parentId !== void 0) {
876
+ sets.push("parent_id = ?");
877
+ values.push(changes.parentId);
878
+ }
879
+ if (changes.permissions !== void 0) {
880
+ sets.push("permissions = ?");
881
+ values.push(changes.permissions.length ? JSON.stringify(changes.permissions) : null);
882
+ }
883
+ const now = toMs(/* @__PURE__ */ new Date());
884
+ if (changes.unlisted !== void 0) {
885
+ sets.push("unlisted_at = ?");
886
+ values.push(changes.unlisted ? now : null);
887
+ }
851
888
  this.exec.transaction(() => {
852
889
  if (opts.snapshot)
853
890
  this.snapshotBeforeMutation(id, opts.snapshot);
854
- fts5Strategy.remove(this.exec, id);
855
- this.exec.run(`UPDATE records SET content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?`, [
856
- JSON.stringify(merged),
857
- toMs(/* @__PURE__ */ new Date()),
858
- opts.updatedBy ?? null,
859
- opts.updatedVia ?? null,
860
- id
861
- ]);
862
- fts5Strategy.insert(this.exec, id, JSON.stringify(merged));
863
- this.syncContentIndex(id, existing.typeId, merged);
891
+ if (merged !== void 0)
892
+ fts5Strategy.remove(this.exec, id);
893
+ const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);
894
+ const changed = this.exec.run(`UPDATE records SET ${sets.concat(["version = version + 1", "updated_at = ?", "updated_by = ?", "updated_via = ?"]).join(", ")} WHERE id = ?${clause}`, [...values, now, opts.updatedBy ?? null, opts.updatedVia ?? null, id, ...verParams]);
895
+ if (changed === 0)
896
+ this.throwVersionConflict(id, opts.expectedVersion);
897
+ if (changes.associations !== void 0) {
898
+ this.exec.run("DELETE FROM associations WHERE record_id = ?", [id]);
899
+ if (changes.associations.length)
900
+ this.insertAssociations(id, changes.associations);
901
+ }
902
+ if (merged !== void 0) {
903
+ fts5Strategy.insert(this.exec, id, JSON.stringify(merged));
904
+ this.syncContentIndex(id, existing.typeId, merged);
905
+ }
864
906
  });
865
907
  const updated = await this.getRecord(id);
866
908
  if (!updated)
867
- throw new Error(`Record not found after patchContent: "${id}"`);
909
+ throw new Error(`Record not found after mutateRecord: "${id}"`);
868
910
  return updated;
869
911
  }
870
912
  async deleteRecord(id, opts = {}) {
@@ -933,49 +975,6 @@ var SharedSqlRecordLogic = class {
933
975
  throw new Error(`Record not found after undelete: "${id}"`);
934
976
  return updated;
935
977
  }
936
- async setPermissions(id, permissions, opts = {}) {
937
- this.exec.transaction(() => {
938
- if (opts.snapshot)
939
- this.snapshotBeforeMutation(id, opts.snapshot);
940
- const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);
941
- const changed = this.exec.run(`UPDATE records SET permissions = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`, [
942
- permissions.length ? JSON.stringify(permissions) : null,
943
- toMs(/* @__PURE__ */ new Date()),
944
- opts.updatedBy ?? null,
945
- opts.updatedVia ?? null,
946
- id,
947
- ...verParams
948
- ]);
949
- if (changed === 0)
950
- this.throwVersionConflict(id, opts.expectedVersion);
951
- });
952
- const updated = await this.getRecord(id);
953
- if (!updated)
954
- throw new Error(`Record not found after setPermissions: "${id}"`);
955
- return updated;
956
- }
957
- async setUnlisted(id, unlisted, opts = {}) {
958
- this.exec.transaction(() => {
959
- if (opts.snapshot)
960
- this.snapshotBeforeMutation(id, opts.snapshot);
961
- const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);
962
- const now = toMs(/* @__PURE__ */ new Date());
963
- const changed = this.exec.run(`UPDATE records SET unlisted_at = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`, [
964
- unlisted ? now : null,
965
- now,
966
- opts.updatedBy ?? null,
967
- opts.updatedVia ?? null,
968
- id,
969
- ...verParams
970
- ]);
971
- if (changed === 0)
972
- this.throwVersionConflict(id, opts.expectedVersion);
973
- });
974
- const updated = await this.getRecord(id);
975
- if (!updated)
976
- throw new Error(`Record not found after setUnlisted: "${id}"`);
977
- return updated;
978
- }
979
978
  async restoreVersion(id, version, opts = {}) {
980
979
  const existing = await this.getRecord(id);
981
980
  if (!existing)
@@ -988,18 +987,20 @@ var SharedSqlRecordLogic = class {
988
987
  if (opts.snapshot)
989
988
  this.snapshotBeforeMutation(id, opts.snapshot);
990
989
  fts5Strategy.remove(this.exec, id);
991
- this.exec.run(`UPDATE records SET type_id = ?, content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?`, [
990
+ this.exec.run(`UPDATE records SET type_id = ?, content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ?, parent_id = ? WHERE id = ?`, [
992
991
  target.typeId,
993
992
  JSON.stringify(target.content),
994
993
  toMs(/* @__PURE__ */ new Date()),
995
994
  opts.updatedBy ?? null,
996
995
  opts.updatedVia ?? null,
996
+ target.parentId ?? null,
997
997
  id
998
998
  ]);
999
- if (target.associations !== void 0) {
999
+ const applied = opts.restoreAssociations === false ? void 0 : target.associations;
1000
+ if (applied !== void 0) {
1000
1001
  this.exec.run("DELETE FROM associations WHERE record_id = ?", [id]);
1001
- if (target.associations.length)
1002
- this.insertAssociations(id, target.associations);
1002
+ if (applied.length)
1003
+ this.insertAssociations(id, applied);
1003
1004
  }
1004
1005
  fts5Strategy.insert(this.exec, id, JSON.stringify(target.content));
1005
1006
  this.syncContentIndex(id, target.typeId, target.content);
@@ -1106,8 +1107,8 @@ var SharedSqlRecordLogic = class {
1106
1107
  try {
1107
1108
  this.exec.run(`INSERT INTO versions
1108
1109
  (record_id, version, type_id, content, updated_at, entity_id,
1109
- updated_by, updated_via, associations, permissions)
1110
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1110
+ updated_by, updated_via, parent_id, associations, permissions)
1111
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1111
1112
  id,
1112
1113
  version.version,
1113
1114
  version.typeId,
@@ -1116,6 +1117,7 @@ var SharedSqlRecordLogic = class {
1116
1117
  version.entityId ?? null,
1117
1118
  version.updatedBy ?? null,
1118
1119
  version.updatedVia ?? null,
1120
+ version.parentId ?? null,
1119
1121
  version.associations ? JSON.stringify(version.associations) : null,
1120
1122
  version.permissions ? JSON.stringify(version.permissions) : null
1121
1123
  ]);
@@ -1126,14 +1128,24 @@ var SharedSqlRecordLogic = class {
1126
1128
  throw err;
1127
1129
  }
1128
1130
  }
1131
+ /**
1132
+ * Replaces every column insertVersionRow writes, so an overwritten row is
1133
+ * indistinguishable from a freshly inserted one — a column left out here
1134
+ * would keep the replaced row's value and surface later as a restore
1135
+ * putting back something the snapshot never said.
1136
+ */
1129
1137
  overwriteVersionRow(id, version) {
1130
1138
  this.exec.run(`UPDATE versions
1131
- SET type_id = ?, content = ?, updated_at = ?, entity_id = ?, associations = ?, permissions = ?
1139
+ SET type_id = ?, content = ?, updated_at = ?, entity_id = ?,
1140
+ updated_by = ?, updated_via = ?, parent_id = ?, associations = ?, permissions = ?
1132
1141
  WHERE record_id = ? AND version = ?`, [
1133
1142
  version.typeId,
1134
1143
  JSON.stringify(version.content),
1135
1144
  toMs(version.updatedAt),
1136
1145
  version.entityId ?? null,
1146
+ version.updatedBy ?? null,
1147
+ version.updatedVia ?? null,
1148
+ version.parentId ?? null,
1137
1149
  version.associations ? JSON.stringify(version.associations) : null,
1138
1150
  version.permissions ? JSON.stringify(version.permissions) : null,
1139
1151
  id,
@@ -1510,8 +1522,8 @@ var NativeSQLiteRecordAdapter = class _NativeSQLiteRecordAdapter {
1510
1522
  getRecord(id) {
1511
1523
  return this.record.getRecord(id);
1512
1524
  }
1513
- patchContent(id, patch, opts) {
1514
- return this.record.patchContent(id, patch, opts);
1525
+ mutateRecord(id, changes, opts) {
1526
+ return this.record.mutateRecord(id, changes, opts);
1515
1527
  }
1516
1528
  deleteRecord(id, opts) {
1517
1529
  return this.record.deleteRecord(id, opts);
@@ -1519,12 +1531,6 @@ var NativeSQLiteRecordAdapter = class _NativeSQLiteRecordAdapter {
1519
1531
  undeleteRecord(id, opts) {
1520
1532
  return this.record.undeleteRecord(id, opts);
1521
1533
  }
1522
- setPermissions(id, permissions, opts) {
1523
- return this.record.setPermissions(id, permissions, opts);
1524
- }
1525
- setUnlisted(id, unlisted, opts) {
1526
- return this.record.setUnlisted(id, unlisted, opts);
1527
- }
1528
1534
  restoreVersion(id, version, opts) {
1529
1535
  return this.record.restoreVersion(id, version, opts);
1530
1536
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/node-sqlite.ts","../../sqlite-shared/src/schema.ts","../../sqlite-shared/src/cursor.ts","../../sqlite-shared/src/fts5.ts","../../sqlite-shared/src/query.ts","../../sqlite-shared/src/mappers.ts","../../sqlite-shared/src/lock.ts","../../sqlite-shared/src/executor.ts","../../sqlite-shared/src/config.ts","../../sqlite-shared/src/record-logic.ts","../../sqlite-shared/src/token-logic.ts","../src/executor.ts","../src/token-store.ts","../src/index.ts"],"names":["StackQueryError","cursor","page","statement","contentSortEntry","existsSync"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Loads node:sqlite via process.getBuiltinModule() rather than a static\n * `import ... from 'node:sqlite'`. node:sqlite is new enough that some\n * bundler/test-runner toolchains (Vite/Vitest as of this writing —\n * https://github.com/vitest-dev/vitest/issues/7177) mis-resolve it,\n * stripping the \"node:\" prefix and trying to load a package literally\n * named \"sqlite\". process.getBuiltinModule() is the Node-sanctioned way\n * to reach a builtin module without going through import resolution at\n * all, so it's immune to that class of bug — useful here since a\n * consuming app's own toolchain could hit the same issue, not just ours.\n */\nexport const { DatabaseSync } = process.getBuiltinModule(\n 'node:sqlite',\n) as typeof import('node:sqlite');\n\nexport type DatabaseSync = InstanceType<typeof DatabaseSync>;\n","/**\n * Schema DDL shared by SQLite-backed record adapters. TOKENS_SCHEMA_SQL\n * is split out from RECORD_SCHEMA_SQL because token storage lives in its\n * own file, never bundled with records — see NativeTokenStore in\n * record-adapter-sqlite and the StackTokenStore portability rationale\n * (docs/spec/wire-format.md § Authentication).\n */\n\nexport const RECORD_SCHEMA_SQL = `\n CREATE TABLE IF NOT EXISTS records (\n id TEXT PRIMARY KEY,\n type_id TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n content TEXT NOT NULL CHECK (json_valid(content)),\n version INTEGER NOT NULL DEFAULT 1,\n parent_id TEXT,\n entity_id TEXT,\n app_id TEXT,\n principal_id TEXT,\n updated_by TEXT,\n updated_via TEXT,\n deleted_at INTEGER,\n unlisted_at INTEGER,\n permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions))\n ) STRICT;\n\n -- A relationship's target is (related_scope, related_id) plus one\n -- qualifier: related_stack for a record in another stack, related_ns for\n -- a foreign namespace. All four are in the primary key, so two targets\n -- differing only by namespace are two associations.\n --\n -- The primary key leads with record_id, so its implicit index already\n -- serves every by-record read and delete; the indexes below exist only\n -- for the filter directions it cannot answer.\n CREATE TABLE IF NOT EXISTS associations (\n record_id TEXT NOT NULL REFERENCES records(id),\n kind TEXT NOT NULL CHECK (kind IN ('tag', 'attachment', 'relationship')),\n label TEXT NOT NULL,\n file_id TEXT NOT NULL DEFAULT '',\n related_scope TEXT NOT NULL DEFAULT '' CHECK (related_scope IN ('', 'record', 'entity', 'external')),\n related_id TEXT NOT NULL DEFAULT '',\n related_ns TEXT NOT NULL DEFAULT '',\n related_stack TEXT NOT NULL DEFAULT '',\n PRIMARY KEY (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack)\n ) STRICT;\n\n CREATE TABLE IF NOT EXISTS versions (\n record_id TEXT NOT NULL REFERENCES records(id),\n version INTEGER NOT NULL,\n type_id TEXT NOT NULL,\n content TEXT NOT NULL CHECK (json_valid(content)),\n updated_at INTEGER NOT NULL,\n entity_id TEXT,\n updated_by TEXT,\n updated_via TEXT,\n associations TEXT CHECK (associations IS NULL OR json_valid(associations)),\n permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions)),\n PRIMARY KEY (record_id, version)\n ) STRICT;\n\n CREATE TABLE IF NOT EXISTS types (\n id TEXT PRIMARY KEY,\n base_id TEXT NOT NULL,\n version INTEGER NOT NULL,\n name TEXT NOT NULL,\n schema TEXT NOT NULL CHECK (json_valid(schema)),\n schema_hash TEXT NOT NULL,\n migrates_from TEXT,\n created_at INTEGER NOT NULL\n ) STRICT;\n\n -- One row per top-level scalar content field a record holds a value at:\n -- what it orders as, and the file it references. See\n -- docs/spec/adapters.md § Adapter backends and\n -- docs/spec/data-model.md § Sorting by a content field.\n --\n -- value_rank is 0 for a numeric value and 1 for a text one — the same\n -- split as \"num_value IS NULL\", but non-null, so one index can carry\n -- the whole ordering. file_id is set only for a 'file-ref' field.\n CREATE TABLE IF NOT EXISTS content_index (\n record_id TEXT NOT NULL REFERENCES records(id),\n field TEXT NOT NULL,\n value_rank INTEGER NOT NULL CHECK (value_rank IN (0, 1)),\n num_value REAL,\n text_key TEXT,\n text_value TEXT,\n file_id TEXT,\n PRIMARY KEY (record_id, field)\n ) STRICT;\n\n -- Indexes. Each filter column continues into (created_at, id) — the\n -- default ordering and its tiebreak — because these columns are\n -- low-cardinality by design, and a narrow index on one leaves the\n -- planner sorting the whole matching set before it can honor LIMIT.\n --\n -- deleted_at and unlisted_at deliberately have none: nothing searches\n -- for the rows they mark, and an index on a column every query tests\n -- for IS NULL only lures the planner off the one answering ORDER BY.\n CREATE INDEX IF NOT EXISTS idx_records_type_id ON records(type_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_parent_id ON records(parent_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_entity_id ON records(entity_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_app_id ON records(app_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_principal_id ON records(principal_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_created_at ON records(created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_updated_at ON records(updated_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_version ON records(version, id);\n\n -- Each association index ends with record_id so the semi-joins in\n -- query.ts read the matching record ids out of the index alone.\n -- idx_assoc_related carries related_ns ahead of related_id because a\n -- record- or entity-scoped target always stores '' there, which\n -- buildQueryPlan states explicitly to keep the prefix contiguous.\n CREATE INDEX IF NOT EXISTS idx_assoc_kind_label ON associations(kind, label, record_id);\n CREATE INDEX IF NOT EXISTS idx_assoc_kind_file_id ON associations(kind, file_id, record_id);\n CREATE INDEX IF NOT EXISTS idx_assoc_related ON associations(kind, related_scope, related_ns, related_id, record_id);\n CREATE INDEX IF NOT EXISTS idx_types_base_id ON types(base_id, version);\n\n -- The whole content ordering, in index order, for one field at a time:\n -- a content-sorted page is an ordered walk of a slice of this index.\n CREATE INDEX IF NOT EXISTS idx_content_index_sort\n ON content_index(field, value_rank, num_value, text_key, text_value, record_id);\n CREATE INDEX IF NOT EXISTS idx_content_index_file\n ON content_index(file_id, record_id) WHERE file_id IS NOT NULL;\n`;\n\n/**\n * Bearer-token storage backing StackTokenStore. Kept separate from\n * RECORD_SCHEMA_SQL so an adapter can put it in its own file — the\n * portable stack file shouldn't also carry a server's auth material.\n */\nexport const TOKENS_SCHEMA_SQL = `\n CREATE TABLE IF NOT EXISTS tokens (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n principal_id TEXT NOT NULL,\n subject_id TEXT NOT NULL,\n label TEXT,\n created_at INTEGER NOT NULL,\n expires_at INTEGER\n ) STRICT;\n`;\n\n/**\n * The full-text index behind `StackQuery.filter.search` (see fts5.ts).\n * What `columnsize=0` costs a reader is in docs/spec/adapters.md\n * § Adapter backends. `detail` stays at its default: sanitizeFts5Query\n * passes phrase queries through, and a phrase needs the token positions\n * only that setting records.\n */\nexport const FTS5_SCHEMA_SQL = `\n CREATE VIRTUAL TABLE IF NOT EXISTS records_fts USING fts5(\n content,\n content='records',\n content_rowid='rowid',\n columnsize=0\n );\n`;\n\n/**\n * Enforces the REFERENCES constraints declared above (off by default per\n * SQLite connection), so touching a nonexistent record fails loudly\n * instead of creating an orphan row. Adapters map the constraint\n * violation to StackNotFoundError at the call sites that can trigger it.\n */\nexport const PRAGMA_FOREIGN_KEYS_ON = `PRAGMA foreign_keys = ON;`;\n\n/**\n * WAL journaling: page-level writes, crash-safe without our own\n * temp-file-and-rename dance, and real SQLite file locking. Only\n * meaningful for a real file (a :memory: database silently ignores it).\n */\nexport const PRAGMA_JOURNAL_MODE_WAL = `PRAGMA journal_mode = WAL;`;\n","/**\n * Cursor encode/decode for keyset pagination. The payload is JSON in\n * base64 rather than delimited fields: a content sort carries the field's\n * stored text, and neither a field name nor a text value is constrained\n * to avoid whatever character a delimiter would claim. Uses btoa/atob\n * with an explicit UTF-8 step rather than Node's Buffer, so the codec\n * stays runtime-agnostic and a non-Latin-1 value survives the round trip.\n */\n\nimport { StackQueryError } from '@haverstack/core';\nimport { contentSortEntry } from '@haverstack/core/adapter';\nimport type { StackRecord, NativeSortField, QuerySort, ScalarFieldKind } from '@haverstack/core';\n\nexport type SortField = NativeSortField;\n\nexport const SORT_FIELDS: readonly SortField[] = ['createdAt', 'updatedAt', 'version'];\n\n/**\n * Which partition of the ordering a cursor sits in: a native column or a\n * content field's number, text, or absent value. The three content cases\n * are the boundary keyset pagination has to hold — a page that ended on\n * the last numeric value resumes at the first text one, not back at the\n * top of either.\n */\nexport type CursorKind = 'native' | 'num' | 'text' | 'absent';\n\nexport type DecodedCursor =\n | { kind: 'native'; field: SortField; value: number; id: string }\n | { kind: 'num'; field: string; value: number; id: string }\n | { kind: 'text'; field: string; value: string; id: string }\n | { kind: 'absent'; field: string; id: string };\n\nconst toBase64 = (text: string): string => {\n const bytes = new TextEncoder().encode(text);\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n};\n\nconst fromBase64 = (cursor: string): string => {\n const binary = atob(cursor);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n};\n\nexport const encodeCursor = (decoded: DecodedCursor): string =>\n toBase64(\n JSON.stringify(\n decoded.kind === 'absent'\n ? { k: decoded.kind, f: decoded.field, i: decoded.id }\n : { k: decoded.kind, f: decoded.field, i: decoded.id, v: decoded.value },\n ),\n );\n\n/** Throws StackQueryError for any malformed, corrupt, or unrecognized cursor. */\nexport const decodeCursor = (cursor: string): DecodedCursor => {\n const malformed = () => new StackQueryError(`Invalid cursor: malformed \"${cursor}\"`);\n let parsed: unknown;\n try {\n parsed = JSON.parse(fromBase64(cursor));\n } catch {\n throw malformed();\n }\n if (!parsed || typeof parsed !== 'object') throw malformed();\n const { k, f, i, v } = parsed as Record<string, unknown>;\n if (typeof f !== 'string' || !f || typeof i !== 'string') throw malformed();\n\n if (k === 'absent') return { kind: 'absent', field: f, id: i };\n if (k === 'text') {\n if (typeof v !== 'string') throw malformed();\n return { kind: 'text', field: f, value: v, id: i };\n }\n if (k === 'num' || k === 'native') {\n if (typeof v !== 'number' || !isFinite(v)) {\n throw new StackQueryError(`Invalid cursor: non-numeric sort value`);\n }\n if (k === 'native') {\n if (!SORT_FIELDS.includes(f as SortField)) {\n throw new StackQueryError(`Invalid cursor: unknown sort field \"${f}\"`);\n }\n return { kind: 'native', field: f as SortField, value: v, id: i };\n }\n return { kind: 'num', field: f, value: v, id: i };\n }\n throw malformed();\n};\n\nexport const getSortField = (query: { sort?: QuerySort }): SortField =>\n query.sort?.field ?? 'createdAt';\n\nexport const getSortColumn = (field: SortField): string =>\n field === 'createdAt' ? 'created_at' : field === 'updatedAt' ? 'updated_at' : 'version';\n\n/**\n * The cursor naming the last record of a page: where the next page\n * resumes. A content sort re-derives the record's ordered value from its\n * stored content rather than reading it back out of the index, so the\n * cursor and the index can only ever disagree if the write path is\n * broken — in which case pagination is not the thing to paper over.\n */\nexport const makeCursor = (\n record: StackRecord,\n sort: QuerySort | undefined,\n kindOf?: (record: StackRecord, field: string) => ScalarFieldKind | undefined,\n): string => {\n const contentField = sort?.contentField;\n if (contentField === undefined) {\n const field = getSortField({ sort });\n const value =\n field === 'updatedAt'\n ? record.updatedAt.getTime()\n : field === 'version'\n ? record.version\n : record.createdAt.getTime();\n return encodeCursor({ kind: 'native', field, value, id: record.id });\n }\n\n const kind = kindOf?.(record, contentField);\n const entry = kind ? contentSortEntry(kind, record.content[contentField]) : null;\n if (!entry) return encodeCursor({ kind: 'absent', field: contentField, id: record.id });\n return entry.kind === 'num'\n ? encodeCursor({ kind: 'num', field: contentField, value: entry.num, id: record.id })\n : encodeCursor({ kind: 'text', field: contentField, value: entry.text, id: record.id });\n};\n","/**\n * FTS5 query sanitization and indexing — the full-text search half of the\n * shared record logic.\n *\n * FTS5's query language supports operators (AND/OR/NOT, phrases, NEAR,\n * wildcards) that can be expensive or cause parse errors with untrusted\n * input, so a search string is rewritten before it reaches the engine:\n *\n * kept: AND/OR/NOT (with a left operand), phrase queries (\"…\"), implicit AND\n * removed: wildcards (*), NEAR(...) (terms kept, wrapper dropped), every\n * character outside the allow-list, bare NOT (no left operand),\n * operators with no right operand\n * capped: parenthesis nesting depth (default: 2)\n * closed: an odd trailing quote\n *\n * Two FTS5 grammar details drive the less obvious rules:\n *\n * - FTS5's NEAR is a function-call form — `NEAR(a b, 10)`. Deleting the\n * whole span would also delete the terms inside it, which can leave a\n * dangling operator when NEAR(...) sits next to AND/OR (e.g. \"x AND\n * NEAR(a b, 5)\" -> \"x AND\" — a syntax error). So this keeps the terms\n * and drops only the NEAR(...) wrapper and distance.\n * - FTS5 has real column-filter syntax and errors hard on any name but\n * the table's actual columns. It is wider than `colname:term`: `-name`\n * and `{a b}` filter columns with no colon at all, so an ordinary\n * hyphenated word is read as one. Since records_fts has exactly one\n * column and callers have no business targeting columns explicitly,\n * everything outside a phrase is reduced to an allow-list rather than\n * validated — see OUTSIDE_PHRASE_DISALLOWED.\n *\n * The rewrite is best-effort and makes no completeness claim against\n * FTS5's grammar — which is why the caller wraps execution as well, so a\n * case this misses surfaces as a StackQueryError rather than a raw engine\n * error. Search text is what a person typed into a box: an unbalanced\n * quote or a trailing \"AND\" is ordinary input, not a malformed request.\n *\n * What this bounds is the grammar, not the cost: a syntactically modest\n * search over a large index can still be expensive, and node:sqlite blocks\n * the calling thread, so there is no timeout to set from inside the call.\n * Bounding execution time belongs to whoever drives the engine under load\n * — see docs/spec/wire-format.md § Bounding query cost for the server-side\n * expectation and the `timeout` wire error that goes with it.\n */\n\nimport type { SqlExecutor } from './executor.js';\n\n/**\n * Outside a phrase, everything but a letter, digit, mark, underscore,\n * whitespace, paren or quote becomes a separator.\n *\n * An allow-list rather than a list of metacharacters to strip, because\n * FTS5's column-filter syntax is wider than it looks and an enumeration\n * kept missing parts of it: `-name` and `{a b}` filter columns with no\n * colon in sight, so `-cats` and even the hyphen inside `cats-dogs` were\n * read as column names and answered \"no such column\". `+` is a bare\n * syntax error. Listing what may pass means the next piece of syntax\n * nobody here anticipated arrives as a separator instead of as an error.\n *\n * Nothing is lost by it: FTS5's default tokenizer already splits terms on\n * these characters, so `cats-dogs` searches for the same two tokens it was\n * indexed as.\n */\nconst OUTSIDE_PHRASE_DISALLOWED = /[^\\p{L}\\p{N}\\p{M}_\\s()\"]/gu;\n\n/**\n * Collapse every whitespace run to a single space.\n *\n * Every rule below that reasons about what sits beside an operator or a\n * paren spells \"beside\" as `\\s*`, and a `\\s*` with a long run to chew on\n * re-tries the whole run once per starting position — quadratic in the\n * run, on text a caller types into a box. Keeping runs collapsed bounds\n * what any of them can backtrack over to a single character. What it does\n * not buy is linearity overall: the rewrite still costs a little more\n * than proportionally as the text grows, which is why bounding a search's\n * length belongs to whoever accepts it.\n *\n * Nothing is lost by it: FTS5's tokenizer splits on whitespace, so a\n * collapsed run tokenizes exactly as the original did — inside a phrase\n * as well as outside, since a phrase matches a sequence of tokens rather\n * than the spacing between them.\n */\nconst collapseWhitespace = (query: string): string => query.replace(/\\s+/g, ' ');\n\nexport const sanitizeFts5Query = (query: string, maxDepth = 2): string => {\n if (!query) return '';\n\n // Drop control characters, inside a phrase as well as outside. A NUL in\n // particular ends the string early for SQLite's C API, so the engine sees\n // an opening quote whose closing one is past the truncation and reports an\n // unterminated string; none of them can match an indexed token anyway.\n let clean = query.replace(/[\\p{Cc}\\p{Cf}]/gu, ' ');\n\n // Remove wildcards\n clean = clean.replace(/\\*/g, '');\n\n // Replace NEAR(terms, distance) with just its terms — see note above.\n //\n // Two things here are about what the engine does when the closing paren\n // never arrives, on text a caller types into a box.\n //\n // No `\\s*` after the paren, though the terms it captures may open with\n // whitespace: `[^,()]*` already matches a space, so a second quantifier\n // that also does gives the engine an ambiguous split to backtrack\n // through. The captured space is collapsed with all the rest on the way\n // out.\n //\n // And neither run crosses a `(`, which bounds where the failure is\n // found: `NEAR(NEAR(NEAR(` used to re-scan to the end of the string once\n // per occurrence. A NEAR's terms are words and phrases and its distance\n // is a number, so neither holds a paren in any input FTS5 would accept.\n clean = clean.replace(/\\bNEAR\\s*\\(([^,()]*)(?:,[^()]*)?\\)/gi, '$1');\n\n // A NEAR( with no closing paren isn't the function-call form the rule\n // above rewrites, and the paren auto-close below would hand FTS5 a NEAR\n // call it never meant to write. Keep the grouping, drop the keyword.\n clean = clean.replace(/\\bNEAR\\s*\\(/gi, '(');\n\n // Reduce to the characters that carry meaning outside a phrase — see note\n // above. Everything else becomes a separator, which is what FTS5's own\n // tokenizer would do with it anyway.\n clean = clean.replace(/\"[^\"]*\"|[^\"]+/g, (span) =>\n span.startsWith('\"') ? span : span.replace(OUTSIDE_PHRASE_DISALLOWED, ' '),\n );\n\n // The reduction above turns every disallowed character into a space, so\n // this is where the long runs come from.\n clean = collapseWhitespace(clean);\n\n // Strip bare NOT with no left operand — FTS5 requires \"term NOT term\", not \"NOT term\"\n clean = clean.replace(/(?:^|\\(\\s*)NOT\\s+/gi, (m) => m.replace(/NOT\\s+/i, ''));\n\n // Enforce max nesting depth; replace excess ( with a space and discard\n // unmatched ). Parens inside a phrase are literal text to FTS5, so the\n // depth count steps over quoted spans rather than reading them as syntax.\n let currentDepth = 0;\n let inPhrase = false;\n let result = '';\n for (const char of clean) {\n if (char === '\"') {\n inPhrase = !inPhrase;\n result += char;\n } else if (inPhrase) {\n result += char;\n } else if (char === '(') {\n if (currentDepth < maxDepth) {\n currentDepth++;\n result += char;\n } else result += ' ';\n } else if (char === ')') {\n if (currentDepth > 0) {\n currentDepth--;\n result += char;\n } else result += ' ';\n } else {\n result += char;\n }\n }\n\n // Close an odd trailing quote before the parens, so the added ) lands\n // outside the phrase rather than becoming a literal character in it.\n if (inPhrase) result += '\"';\n\n // Auto-close any unclosed parens\n if (currentDepth > 0) result += ')'.repeat(currentDepth);\n\n // Remove empty paren pairs left behind by NEAR/NOT stripping, and drop\n // operators left without an operand. Looped together because each can\n // create work for the other: removing an operator can empty a paren pair\n // (\"(a AND)\" -> \"(a)\" -> ...), and removing a pair can strand the\n // operator that pointed at it (\"x AND ()\" -> \"x AND\").\n let prev: string;\n do {\n prev = result;\n // Empty phrases and empty paren pairs, each of which matches nothing,\n // in one rule so that a run of them collapses to a single space\n // rather than to one space apiece — a run of spaces is what the rules\n // below would have to backtrack over. An empty phrase is also the one\n // construct whose quotes can re-pair differently once a rule inserts\n // text beside it, which is why this repeats to a fixpoint.\n result = result.replace(/(?:\"\"|\\(\\s*\\))+/g, ' ');\n result = dropDanglingOperators(result);\n result = restoreImplicitAnd(result);\n } while (result !== prev);\n\n return collapseWhitespace(result).replace(/\\(\\s+/g, '(').replace(/\\s+\\)/g, ')').trim();\n};\n\n/** An operator FTS5 requires a term on both sides of. */\nconst OPERATOR = String.raw`(?:AND|OR|NOT)`;\n\n/**\n * Stands in for a phrase while operators are rewritten. A private-use\n * character is not a word character, so `\\b` still sees the operator\n * beside it, while `^`, `$` and the paren anchors see an operand — which\n * is what a phrase is. Nothing reaches here holding one: `clean` is\n * rewritten from the caller's string, and a literal U+E000 in it would be\n * a term character FTS5 tokenizes away regardless.\n */\nconst PHRASE_HOLE = '\\u{E000}';\n\n/**\n * Drop operators left without an operand — the shape a person types on\n * their way to a longer query (\"cats AND\") or lands on once the rules\n * above have removed a term. FTS5 rejects each of them outright, so what\n * would otherwise be a syntax error becomes the search for the terms that\n * are actually there.\n *\n * Phrases are masked out first: `\"cats AND dogs\"` is literal text to FTS5,\n * and rewriting inside it would change what the user asked for.\n */\nconst dropDanglingOperators = (query: string): string =>\n withPhrasesMasked(query, (masked) => {\n let out = masked;\n let prev: string;\n do {\n prev = out;\n // A run of operators keeps only the first — the rest have no left operand.\n out = out.replace(\n new RegExp(String.raw`\\b(${OPERATOR})\\b(?:\\s+\\b${OPERATOR}\\b)+`, 'gi'),\n '$1',\n );\n // No left operand: at the start of the query or straight after \"(\".\n out = out.replace(new RegExp(String.raw`(^|\\()\\s*\\b${OPERATOR}\\b\\s*`, 'gi'), '$1');\n // No right operand: at the end of the query or straight before \")\".\n // No leading `\\s*` — whitespace before the operator is left for the\n // collapse on the way out, and a `\\s*` here would re-scan a run of\n // spaces once per position in it before failing to find an operator.\n out = out.replace(new RegExp(String.raw`\\b${OPERATOR}\\b\\s*($|\\))`, 'gi'), '$1');\n } while (out !== prev);\n return out;\n });\n\n/**\n * Run `rewrite` over the query with each phrase swapped for PHRASE_HOLE,\n * then put the phrases back in order. Every rule that reasons about what\n * sits beside an operator or a paren needs this: inside a phrase the text\n * is literal, and from outside one the phrase is a single operand.\n */\nconst withPhrasesMasked = (query: string, rewrite: (masked: string) => string): string => {\n const phrases: string[] = [];\n const masked = query.replace(/\"[^\"]*\"/g, (phrase) => {\n phrases.push(phrase);\n return PHRASE_HOLE;\n });\n let i = 0;\n return rewrite(masked).replace(new RegExp(PHRASE_HOLE, 'g'), () => phrases[i++]);\n};\n\n/**\n * Put back the operator FTS5 requires around a group. Two terms side by\n * side are an implicit AND (`cats dogs`), but a group is not: `cats (dogs)`\n * is a syntax error where `cats AND (dogs)` is fine. Grouping survives a\n * rewrite that strips the operator which used to sit beside it, so this\n * names the AND that was always meant rather than dropping the parens.\n *\n * Both rules test a *whole* word against the operator list. Matching a\n * prefix instead reads the `D` of a preceding `AND` as a term and inserts\n * a second one beside it.\n *\n * Phrases are masked for the same reason as elsewhere — a phrase is an\n * operand, so `\"a b\" (c)` needs the operator too.\n */\nconst OPERAND_CHAR = String.raw`[\\p{L}\\p{N}_${PHRASE_HOLE}]`;\n\nconst isOperator = (word: string): boolean => /^(?:AND|OR|NOT)$/i.test(word);\n\nconst restoreImplicitAnd = (query: string): string =>\n withPhrasesMasked(query, (masked) =>\n masked\n // \")\" then an operand — \") cats\", \") (\", \") <phrase>\" — unless that\n // operand is an operator, which already joins the two sides.\n .replace(\n new RegExp(String.raw`\\)\\s*(?=${OPERAND_CHAR}|\\()`, 'gu'),\n (match, offset: number, whole: string) => {\n const next = /^\\s*([\\p{L}\\p{N}_]+)/u.exec(whole.slice(offset + match.length));\n return next && isOperator(next[1]) ? match : ') AND ';\n },\n )\n // An operand then \"(\", unless the operand is an operator.\n // The lookbehind pins the match to the start of the operand run.\n // Without it every position inside a run is a start position, and\n // each one re-scans the rest of the run before the lookahead\n // rejects it — quadratic in a long word before a paren.\n .replace(\n new RegExp(String.raw`(?<!${OPERAND_CHAR})(${OPERAND_CHAR}+)\\s*(?=\\()`, 'gu'),\n (match, word: string) => (isOperator(word) ? match : `${word} AND `),\n ),\n );\n\n// -------------------------------------------------------\n// Indexing strategy\n// -------------------------------------------------------\n\n/**\n * FTS5's external-content table needs its special `('delete', rowid,\n * content)` command — with the *old* content — to unindex a row; a plain\n * DELETE leaves stale, still-searchable entries. remove() therefore reads\n * the current rowid/content and must run before the caller's change.\n */\nexport const fts5Strategy = {\n /** Index a record that has no FTS entry yet (used right after an INSERT). */\n insert(exec: SqlExecutor, recordId: string, content: string): void {\n exec.run(`INSERT INTO records_fts(rowid, content) SELECT rowid, ? FROM records WHERE id = ?`, [\n content,\n recordId,\n ]);\n },\n /**\n * Remove a record's FTS entry. MUST be called before the records row's\n * content changes or the row is deleted — see the note above.\n */\n remove(exec: SqlExecutor, recordId: string): void {\n const row = exec.get<{ rowid: number; content: string }>(\n 'SELECT rowid, content FROM records WHERE id = ?',\n [recordId],\n );\n if (!row) return;\n exec.run(`INSERT INTO records_fts(records_fts, rowid, content) VALUES('delete', ?, ?)`, [\n row.rowid,\n row.content,\n ]);\n },\n};\n","/**\n * StackQuery -> SQL. Engine-independent — shared verbatim between\n * SQLite-backed record adapters. The one caveat is full-text search:\n * `f.search` here assumes a `records_fts` virtual table with a MATCH-able\n * `content` column (see fts5.ts).\n *\n * A query compiles to a plan rather than to a single statement, because a\n * content-field sort is two orderings end to end: the records that hold a\n * value at the field, ordered by it, then the records that hold none,\n * ordered by id. Reading them as two statements lets each one walk an\n * index in order and stop at the page boundary; the LEFT JOIN that would\n * express the same result in one statement can only be sorted after the\n * fact, which costs a full pass over the table for every page.\n */\n\nimport { StackQueryError, type StackQuery } from '@haverstack/core';\nimport { contentSortKey, parseContentFilterKey } from '@haverstack/core/adapter';\nimport {\n decodeCursor,\n getSortColumn,\n getSortField,\n type DecodedCursor,\n type SortField,\n} from './cursor.js';\nimport { sanitizeFts5Query } from './fts5.js';\n\nexport { getSortField, getSortColumn };\nexport type { SortField };\n\n/** One statement plus the parameters it binds, in textual order. */\nexport type QueryStatement = { sql: string; params: unknown[] };\n\n/**\n * A page statement with the row budget its trailing `LIMIT ?` binds.\n * buildQueryPlan leaves that parameter off, so the budget is never one of\n * the parameters the query itself bound: the second partition of a\n * content sort asks for what the first left of the page rather than for a\n * page it then discards.\n */\nexport const atBudget = (page: QueryStatement, rows: number): QueryStatement => ({\n sql: page.sql,\n params: [...page.params, rows],\n});\n\n/**\n * Reduce `sort.direction` to a keyword this module may interpolate into\n * SQL. Core's assertValidSort() already rejects anything else at the\n * invariant layer, but this builder writes the value straight into\n * `ORDER BY` and the cursor comparison, so it re-checks rather than trust\n * an upstream that a future refactor could bypass: an unvalidated direction\n * here is a SQL-injection sink. Anything but \"asc\" is treated as the\n * default \"desc\" only after passing the guard.\n */\nconst sqlDirection = (query: StackQuery): 'ASC' | 'DESC' => {\n const dir = query.sort?.direction ?? 'desc';\n if (dir !== 'asc' && dir !== 'desc') {\n throw new StackQueryError(`Invalid sort direction \"${dir}\": expected \"asc\" or \"desc\".`);\n }\n return dir === 'asc' ? 'ASC' : 'DESC';\n};\n\n/**\n * Normalize a json_each row's value so one array element and one bare\n * value are walked by the same clause: an array stands for its elements,\n * anything else for itself. See docs/spec/data-model.md § Nested content\n * paths.\n */\nconst spread = (alias: string): string =>\n `CASE WHEN ${alias}.type = 'array' THEN ${alias}.value ` +\n `WHEN ${alias}.type = 'object' THEN json_array(json(${alias}.value)) ` +\n `ELSE json_array(${alias}.value) END`;\n\n/**\n * The members of a json_each row that is an object, and none for a row\n * that is not — a path descending through a scalar reaches no value rather\n * than reaching an error.\n */\nconst members = (alias: string): string =>\n `CASE WHEN ${alias}.type = 'object' THEN ${alias}.value ELSE '{}' END`;\n\n/**\n * True when some value reached by `segments` satisfies `leaf`, applied to\n * the alias holding that value. Each segment is a bound parameter matched\n * against json_each's `key`, never interpolated into a path expression, so\n * no key can be read as syntax. See docs/spec/data-model.md § Filter.\n */\nconst contentPathExists = (\n segments: string[],\n leaf: (alias: string) => string,\n params: unknown[],\n nextAlias: () => string,\n): string => {\n // The walk is a flat join list, not a subquery per segment: both SQLite\n // builds cap expression-tree depth far below the segment cap, and\n // nesting spends that budget while a join list spends the join budget\n // the cap is actually sized against.\n let key = nextAlias();\n let value = nextAlias();\n const from = [`json_each(r.content) AS ${key}`, `json_each(${spread(key)}) AS ${value}`];\n const where = [`${key}.key = ?`];\n params.push(segments[0]);\n\n for (const segment of segments.slice(1)) {\n key = nextAlias();\n from.push(`json_each(${members(value)}) AS ${key}`);\n value = nextAlias();\n from.push(`json_each(${spread(key)}) AS ${value}`);\n where.push(`${key}.key = ?`);\n params.push(segment);\n }\n\n where.push(leaf(value));\n return `EXISTS (SELECT 1 FROM ${from.join(', ')} WHERE ${where.join(' AND ')})`;\n};\n\n/**\n * A scalar filter value never matches an object or array stored at the\n * path: json_each exposes those as their JSON text, which would compare\n * equal to a string spelling the same document while every non-SQL adapter\n * compares the value itself.\n */\nconst equalsLeaf = (alias: string): string =>\n `${alias}.type NOT IN ('object', 'array') AND ${alias}.value = ?`;\n\n/** The join alias the content index is read through. */\nconst SORT_ALIAS = 'cs';\n\n/**\n * A cursor names a position in one ordering; carrying it into another\n * would silently resume somewhere arbitrary. The partition matters as\n * much as the field name — a cursor from a numeric page can't be read\n * against text values — so each ordering below accepts only the cursors\n * it can read, and rejecting the rest is what narrows the cursor to them.\n */\nconst cursorMismatch = (cursor: DecodedCursor, field: string): StackQueryError =>\n new StackQueryError(\n `Cursor sort field \"${cursor.field}\" does not match query sort field \"${field}\"`,\n );\n\nconst nativeCursor = (cursor: DecodedCursor, field: SortField) => {\n if (cursor.kind !== 'native' || cursor.field !== field) throw cursorMismatch(cursor, field);\n return cursor;\n};\n\nconst contentCursor = (cursor: DecodedCursor, field: string) => {\n if (cursor.kind === 'native' || cursor.field !== field) throw cursorMismatch(cursor, field);\n return cursor;\n};\n\n/**\n * Every condition on the records row itself — everything a query asks\n * except where in the ordering to resume, which is per-partition and gets\n * appended by the caller.\n */\nconst recordConditions = (query: StackQuery): { conditions: string[]; params: unknown[] } => {\n const conditions: string[] = [\"r.id != '_config'\"];\n const params: unknown[] = [];\n const f = query.filter ?? {};\n\n if (!f.includeDeleted) {\n conditions.push('r.deleted_at IS NULL');\n }\n\n if (!f.includeUnlisted) {\n conditions.push('r.unlisted_at IS NULL');\n }\n\n if (f.typeId !== undefined) {\n const ids = Array.isArray(f.typeId) ? f.typeId : [f.typeId];\n conditions.push(`r.type_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.parentId !== undefined) {\n if (f.parentId === null) {\n conditions.push('r.parent_id IS NULL');\n } else {\n conditions.push('r.parent_id = ?');\n params.push(f.parentId);\n }\n }\n\n if (f.appId !== undefined) {\n const ids = Array.isArray(f.appId) ? f.appId : [f.appId];\n conditions.push(`r.app_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.entityId !== undefined) {\n const ids = Array.isArray(f.entityId) ? f.entityId : [f.entityId];\n conditions.push(`r.entity_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.principalId !== undefined) {\n const ids = Array.isArray(f.principalId) ? f.principalId : [f.principalId];\n conditions.push(`r.principal_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.createdAt?.after) {\n conditions.push('r.created_at > ?');\n params.push(f.createdAt.after.getTime());\n }\n if (f.createdAt?.before) {\n conditions.push('r.created_at < ?');\n params.push(f.createdAt.before.getTime());\n }\n if (f.updatedAt?.after) {\n conditions.push('r.updated_at > ?');\n params.push(f.updatedAt.after.getTime());\n }\n if (f.updatedAt?.before) {\n conditions.push('r.updated_at < ?');\n params.push(f.updatedAt.before.getTime());\n }\n\n // Every association filter below is a semi-join rather than a correlated\n // EXISTS, so the planner drives from the association side — reading the\n // matching record ids straight out of idx_assoc_kind_label /\n // idx_assoc_kind_file_id / idx_assoc_related / idx_content_index_file,\n // each of which ends with record_id and so needs no table read at all —\n // instead of scanning every record and probing for each. The work is\n // proportional to how many records match, not to how many the stack\n // holds.\n\n // Tag filter — record must have ALL specified tags\n if (f.tags?.length) {\n for (const tag of f.tags) {\n conditions.push(\n `r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'tag' AND a.label = ?)`,\n );\n params.push(tag);\n }\n }\n\n // Attachment label filter\n if (f.hasAttachment) {\n conditions.push(\n `r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'attachment' AND a.label = ?)`,\n );\n params.push(f.hasAttachment);\n }\n\n // Attachment file ID filter — find records that reference a specific file,\n // either via an attachment association or a top-level file-ref content field\n if (f.attachmentFileId) {\n conditions.push(\n `(r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'attachment' AND a.file_id = ?)\n OR r.id IN (SELECT ci.record_id FROM content_index ci WHERE ci.file_id = ?))`,\n );\n params.push(f.attachmentFileId, f.attachmentFileId);\n }\n\n // Relationship filter — each clause is an optional pattern, so a bare\n // label matches every target under it and an external target with no\n // `id` matches its whole namespace (docs/spec/data-model.md § Filter).\n //\n // A record- or entity-scoped target always stores '' in related_ns\n // (see associationKeyColumns). Saying so turns idx_assoc_related's\n // (scope, ns, id) prefix into a contiguous equality run for those\n // scopes instead of leaving the planner to seek on scope alone.\n if (f.relatedTo) {\n const clauses: string[] = [];\n const target = f.relatedTo.target;\n if (f.relatedTo.label !== undefined) {\n clauses.push('a.label = ?');\n params.push(f.relatedTo.label);\n }\n if (target) {\n clauses.push('a.related_scope = ?');\n params.push(target.scope);\n if (target.scope === 'record') {\n clauses.push('a.related_ns = ?', 'a.related_id = ?', 'a.related_stack = ?');\n params.push('', target.recordId, target.stackUrl ?? '');\n } else if (target.scope === 'entity') {\n clauses.push('a.related_ns = ?', 'a.related_id = ?');\n params.push('', target.entityId);\n } else {\n clauses.push('a.related_ns = ?');\n params.push(target.ns);\n if (target.id !== undefined) {\n clauses.push('a.related_id = ?');\n params.push(target.id);\n }\n }\n }\n conditions.push(\n `r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'relationship'` +\n clauses.map((c) => ` AND ${c}`).join('') +\n `)`,\n );\n }\n\n // Content field filters — a dot-separated path, matched element-wise\n // through arrays. A `null` value means \"no value at the path, or a value\n // that is null\" (docs/spec/data-model.md § Filter), which is the second\n // arm below: \"some value there is null\" OR \"nothing is there at all\".\n if (f.content) {\n let aliasSeq = 0;\n const nextAlias = () => `cp${aliasSeq++}`;\n for (const [key, value] of Object.entries(f.content)) {\n const segments = parseContentFilterKey(key);\n // undefined is the same absence null names — JSON drops it on the\n // way over the wire, so treating it as a value would make an\n // in-process query and its wire equivalent disagree.\n if (value === null || value === undefined) {\n const isNull = contentPathExists(segments, (a) => `${a}.value IS NULL`, params, nextAlias);\n const anyValue = contentPathExists(\n segments,\n (a) => `${a}.value IS NOT NULL`,\n params,\n nextAlias,\n );\n conditions.push(`(${isNull} OR NOT ${anyValue})`);\n } else {\n const match = contentPathExists(segments, equalsLeaf, params, nextAlias);\n params.push(value);\n conditions.push(match);\n }\n }\n }\n\n // Presence — the question an exact-match value cannot ask. Element-wise\n // like the content filter above: a path holds a value when at least one\n // non-null value is reachable at it. See docs/spec/data-model.md\n // § Filter.\n if (f.contentPresent?.length) {\n let aliasSeq = 0;\n const nextAlias = () => `pp${aliasSeq++}`;\n for (const key of f.contentPresent) {\n conditions.push(\n contentPathExists(\n parseContentFilterKey(key),\n (a) => `${a}.value IS NOT NULL`,\n params,\n nextAlias,\n ),\n );\n }\n }\n\n if (f.search) {\n const sanitized = sanitizeFts5Query(f.search);\n if (sanitized) {\n conditions.push(`r.rowid IN (SELECT rowid FROM records_fts WHERE records_fts MATCH ?)`);\n params.push(sanitized);\n } else {\n // A search that sanitizes to nothing (e.g. \"*\", punctuation-only) is\n // an honest zero-match result, not \"no filter\" — omitting the clause\n // would silently return the full table as the \"search result\".\n conditions.push('0');\n }\n }\n\n return { conditions, params };\n};\n\n/**\n * Everything ordered after the cursor's record within the partition that\n * holds a value: a later value rank, or the same rank past the cursor's\n * value and id. `op` runs the comparison the same way the ORDER BY does,\n * so one form covers both directions — in the direction where the\n * cursor's rank is the last one, the leading disjunct is simply never\n * true.\n */\nconst presentCursorCondition = (\n cursor: Extract<DecodedCursor, { kind: 'num' | 'text' }>,\n op: '>' | '<',\n params: unknown[],\n): string => {\n if (cursor.kind === 'num') {\n params.push(cursor.value, cursor.value, cursor.id);\n return (\n `(${SORT_ALIAS}.value_rank ${op} 0 OR (${SORT_ALIAS}.value_rank = 0 AND ` +\n `(${SORT_ALIAS}.num_value ${op} ? OR (${SORT_ALIAS}.num_value = ? AND ` +\n `${SORT_ALIAS}.record_id ${op} ?))))`\n );\n }\n const key = contentSortKey(cursor.value);\n params.push(key, key, cursor.value, cursor.value, cursor.id);\n return (\n `(${SORT_ALIAS}.value_rank ${op} 1 OR (${SORT_ALIAS}.value_rank = 1 AND ` +\n `(${SORT_ALIAS}.text_key ${op} ? OR (${SORT_ALIAS}.text_key = ? AND ` +\n `(${SORT_ALIAS}.text_value ${op} ? OR (${SORT_ALIAS}.text_value = ? AND ` +\n `${SORT_ALIAS}.record_id ${op} ?))))))`\n );\n};\n\nconst statement = (\n select: string,\n from: string,\n conditions: string[],\n params: unknown[],\n tail = '',\n): QueryStatement => ({\n sql: `SELECT ${select} FROM ${from} WHERE ${conditions.join(' AND ')}${tail}`,\n params,\n});\n\n/**\n * Compile a query into the statements that answer it, read in order and\n * their rows concatenated: the first that fills the page ends the read,\n * so the second is never run for a page that doesn't reach it. Each still\n * needs the row budget its `LIMIT ?` binds — see atBudget.\n */\nexport const buildQueryPlan = (query: StackQuery): QueryStatement[] => {\n const dir = sqlDirection(query);\n const op = dir === 'ASC' ? '>' : '<';\n const decoded = query.cursor ? decodeCursor(query.cursor) : null;\n const contentField = query.sort?.contentField;\n\n if (contentField === undefined) {\n const { conditions, params } = recordConditions(query);\n const col = getSortColumn(getSortField(query));\n if (decoded) {\n const cursor = nativeCursor(decoded, getSortField(query));\n conditions.push(`(r.${col} ${op} ? OR (r.${col} = ? AND r.id ${op} ?))`);\n params.push(cursor.value, cursor.value, cursor.id);\n }\n return [\n statement(\n 'r.*',\n 'records r',\n conditions,\n params,\n ` ORDER BY r.${col} ${dir}, r.id ${dir} LIMIT ?`,\n ),\n ];\n }\n\n const cursor = decoded ? contentCursor(decoded, contentField) : null;\n const pages: QueryStatement[] = [];\n\n // Records holding a value at the field, in that value's order. A cursor\n // sitting in the absent partition has already passed every one of them.\n if (cursor?.kind !== 'absent') {\n const { conditions, params } = recordConditions(query);\n const from = `content_index ${SORT_ALIAS} JOIN records r ON r.id = ${SORT_ALIAS}.record_id`;\n conditions.push(`${SORT_ALIAS}.field = ?`);\n params.push(contentField);\n if (cursor) conditions.push(presentCursorCondition(cursor, op, params));\n // Ordered exactly as idx_content_index_sort stores it, so the page is\n // an ordered walk of the index rather than a sort of the matches.\n const order =\n `${SORT_ALIAS}.value_rank ${dir}, ${SORT_ALIAS}.num_value ${dir}, ` +\n `${SORT_ALIAS}.text_key ${dir}, ${SORT_ALIAS}.text_value ${dir}, ` +\n `${SORT_ALIAS}.record_id ${dir}`;\n pages.push(statement('r.*', from, conditions, params, ` ORDER BY ${order} LIMIT ?`));\n }\n\n // Records holding nothing there, which trail the others whichever way\n // the sort runs (docs/spec/data-model.md § Sorting by a content field).\n {\n const { conditions, params } = recordConditions(query);\n conditions.push(\n `NOT EXISTS (SELECT 1 FROM content_index ${SORT_ALIAS} ` +\n `WHERE ${SORT_ALIAS}.record_id = r.id AND ${SORT_ALIAS}.field = ?)`,\n );\n params.push(contentField);\n if (cursor?.kind === 'absent') {\n conditions.push(`r.id ${op} ?`);\n params.push(cursor.id);\n }\n pages.push(statement('r.*', 'records r', conditions, params, ` ORDER BY r.id ${dir} LIMIT ?`));\n }\n\n return pages;\n};\n","/**\n * Row <-> domain object mappers shared by every SQLite-backed record\n * adapter. Column names and JSON-encoding choices are the storage\n * contract; keeping one copy means the adapters can't drift on them.\n */\n\nimport type {\n StackRecord,\n StackType,\n RecordVersion,\n Association,\n RelationshipTarget,\n} from '@haverstack/core';\n\nexport const toMs = (d: Date): number => d.getTime();\nexport const fromMs = (ms: number): Date => new Date(ms);\n\nexport const rowToRecord = (\n row: Record<string, unknown>,\n associations: Association[],\n): StackRecord => {\n const record: StackRecord = {\n id: row.id as string,\n typeId: row.type_id as string,\n createdAt: fromMs(row.created_at as number),\n updatedAt: fromMs(row.updated_at as number),\n content: JSON.parse(row.content as string),\n version: row.version as number,\n };\n if (row.parent_id) record.parentId = row.parent_id as string;\n if (row.entity_id) record.entityId = row.entity_id as string;\n if (row.app_id) record.appId = row.app_id as string;\n if (row.principal_id) record.principalId = row.principal_id as string;\n if (row.updated_by) record.updatedBy = row.updated_by as string;\n if (row.updated_via) record.updatedVia = row.updated_via as string;\n if (row.deleted_at) record.deletedAt = fromMs(row.deleted_at as number);\n if (row.unlisted_at) record.unlistedAt = fromMs(row.unlisted_at as number);\n if (row.permissions) record.permissions = JSON.parse(row.permissions as string);\n if (associations.length) record.associations = associations;\n return record;\n};\n\nexport const rowToAssociation = (row: Record<string, unknown>): Association => {\n if (row.kind === 'tag') {\n return { kind: 'tag', label: row.label as string };\n }\n if (row.kind === 'attachment') {\n return {\n kind: 'attachment',\n label: row.label as string,\n fileId: row.file_id as string,\n };\n }\n // relationship\n return {\n kind: 'relationship',\n label: row.label as string,\n target: rowToTarget(row),\n };\n};\n\n/**\n * The five columns that identify an association, in the order every\n * INSERT and DELETE below binds them. One helper because the two must\n * agree exactly — a dissociate that bound them differently would delete\n * nothing and report success.\n */\nexport const associationKeyColumns = (a: Association): [string, string, string, string, string] => {\n if (a.kind === 'attachment') return [a.fileId, '', '', '', ''];\n if (a.kind !== 'relationship') return ['', '', '', '', ''];\n const t = a.target;\n if (t.scope === 'entity') return ['', 'entity', t.entityId, '', ''];\n if (t.scope === 'external') return ['', 'external', t.id, t.ns, ''];\n return ['', 'record', t.recordId, '', t.stackUrl ?? ''];\n};\n\nconst rowToTarget = (row: Record<string, unknown>): RelationshipTarget => {\n const id = row.related_id as string;\n if (row.related_scope === 'entity') return { scope: 'entity', entityId: id };\n if (row.related_scope === 'external') {\n return { scope: 'external', ns: row.related_ns as string, id };\n }\n const stackUrl = row.related_stack as string;\n return { scope: 'record', recordId: id, ...(stackUrl && { stackUrl }) };\n};\n\nexport const rowToType = (row: Record<string, unknown>): StackType => {\n const t: StackType = {\n id: row.id as string,\n baseId: row.base_id as string,\n version: row.version as number,\n name: row.name as string,\n schema: JSON.parse(row.schema as string),\n schemaHash: row.schema_hash as string,\n createdAt: fromMs(row.created_at as number),\n };\n if (row.migrates_from) t.migratesFrom = row.migrates_from as string;\n return t;\n};\n\nexport const rowToVersion = (row: Record<string, unknown>): RecordVersion => {\n const v: RecordVersion = {\n version: row.version as number,\n typeId: row.type_id as string,\n content: JSON.parse(row.content as string),\n updatedAt: fromMs(row.updated_at as number),\n };\n if (row.entity_id) v.entityId = row.entity_id as string;\n if (row.updated_by) v.updatedBy = row.updated_by as string;\n if (row.updated_via) v.updatedVia = row.updated_via as string;\n if (row.associations) v.associations = JSON.parse(row.associations as string);\n if (row.permissions) v.permissions = JSON.parse(row.permissions as string);\n return v;\n};\n","/**\n * Storage-ownership lock file. A stack file is owned by exactly one\n * process at a time (see docs/spec/adapters.md § Concurrency & storage ownership).\n * The lock file sits beside the database and records the PID of its\n * opener — pure Node fs/process logic, no SQLite engine involved, so\n * every Node-backed record adapter shares one implementation and one\n * error message.\n */\n\nimport { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';\n\ntype LockInfo = { pid: number };\n\nconst lockPathFor = (dbPath: string): string => `${dbPath}.lock`;\n\nconst isProcessAlive = (pid: number): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // ESRCH: no such process — safe to reclaim. Anything else (e.g. EPERM,\n // meaning the process exists but we can't signal it) — assume alive.\n return (err as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n};\n\n/**\n * Acquire the lock, throwing a clear error if another live process\n * already holds it. Reclaims stale locks (owning process no longer\n * running) automatically. `force` bypasses the check entirely, for the\n * rare case of PID reuse across a reboot.\n */\nexport const acquireLock = (dbPath: string, force?: boolean): void => {\n const lockPath = lockPathFor(dbPath);\n const info = existsSync(lockPath) ? readLock(lockPath) : null;\n if (info) {\n const ownedBySelf = info.pid === process.pid;\n if (!ownedBySelf && !force && isProcessAlive(info.pid)) {\n throw new Error(\n `Stack database at \"${dbPath}\" is in use by another process (pid ${info.pid}). ` +\n `Connect via its server instead, or pass { force: true } to override. ` +\n `If no such process is running, remove \"${lockPath}\".`,\n );\n }\n }\n writeFileSync(lockPath, JSON.stringify({ pid: process.pid } satisfies LockInfo));\n};\n\n/**\n * Read the lock, treating an unreadable one as absent. A lock file is\n * written non-atomically, so a crash mid-write leaves a torn one — and a\n * lock naming no live process is exactly what acquireLock() reclaims. The\n * alternative is a database that cannot be opened again by any means the\n * error message mentions, which is the worse failure: the lock exists to\n * prevent a second writer, not to outrank the data it guards.\n * releaseLock() takes the same view.\n */\nconst readLock = (lockPath: string): LockInfo | null => {\n try {\n const info = JSON.parse(readFileSync(lockPath, 'utf-8')) as LockInfo;\n return typeof info?.pid === 'number' ? info : null;\n } catch {\n return null;\n }\n};\n\n/** Release the lock, if it's still owned by this process. */\nexport const releaseLock = (dbPath: string): void => {\n const lockPath = lockPathFor(dbPath);\n if (!existsSync(lockPath)) return;\n try {\n const info = JSON.parse(readFileSync(lockPath, 'utf-8')) as LockInfo;\n if (info.pid === process.pid) unlinkSync(lockPath);\n } catch {\n // Corrupt lock file — remove it rather than leaving storage permanently unopenable.\n unlinkSync(lockPath);\n }\n};\n","/**\n * Normalizes a SQLite binding's call convention (array-bind-then-step,\n * spread-args run/get/all, cursor-returning exec) behind one interface,\n * so the actual CRUD/query/version/type/association/token logic can live\n * once in this package instead of being re-typed per engine. Each adapter\n * package provides a thin SqlExecutor implementation over its own\n * database handle.\n *\n * Deliberately synchronous: every binding this targets executes queries\n * in-process without yielding, and an async interface would force the\n * shared logic's transaction sequences to interleave.\n */\nexport interface SqlExecutor {\n /** Run DDL, pragmas, or any statement(s) with no bound params and no results needed. */\n exec(sql: string): void;\n /** Run a single parameterized statement. Returns the number of rows affected (INSERT/UPDATE/DELETE). */\n run(sql: string, params?: readonly unknown[]): number;\n /** Run a parameterized statement and return the first result row, or undefined. */\n get<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T | undefined;\n /** Run a parameterized statement and return all result rows. */\n all<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T[];\n /**\n * Run `fn` atomically: every statement it issues commits together, or\n * none do if it throws. `fn` must be synchronous and call back into\n * this executor only — not every engine's transaction primitive can\n * straddle a suspended call. A binding with a real multi-statement\n * transaction (BEGIN/COMMIT/ROLLBACK) implements this with one; a\n * binding whose transaction primitive is itself a callback wrapper\n * (e.g. a Durable Object's storage.transactionSync) can pass `fn`\n * straight through to it.\n */\n transaction<T>(fn: () => T): T;\n}\n\n/** SQLite reports FK violations with this exact message; verify it holds when adding an engine. */\nexport const isForeignKeyViolation = (err: unknown): boolean =>\n err instanceof Error && err.message.includes('FOREIGN KEY constraint failed');\n\n/** SQLite reports a PRIMARY KEY/UNIQUE collision with this exact message. */\nexport const isUniqueConstraintViolation = (err: unknown): boolean =>\n err instanceof Error && err.message.includes('UNIQUE constraint failed');\n","import type { SqlExecutor } from './executor.js';\n\n/** timezone is optional passthrough app metadata — no default. */\nexport type StackConfig = { entityId: string; timezone: string | undefined };\n\n/** Inserts the singleton _config@1 record. Only valid on a freshly-created database. */\nexport const insertConfigRecord = (\n exec: SqlExecutor,\n entityId: string,\n timezone: string | undefined,\n): void => {\n const now = Date.now();\n exec.run(\n `INSERT INTO records (id, type_id, created_at, updated_at, content, version)\n VALUES ('_config', '_config@1', ?, ?, ?, 1)`,\n [now, now, JSON.stringify({ entityId, timezone })],\n );\n};\n\n/** Reads the singleton _config@1 record. Throws if the database is missing it. */\nexport const readStackConfig = (exec: SqlExecutor): StackConfig => {\n const row = exec.get<{ content: string }>(`SELECT content FROM records WHERE id = '_config'`);\n if (!row) throw new Error('Stack database is missing its config record.');\n const content = JSON.parse(row.content) as { entityId: string; timezone?: string };\n return { entityId: content.entityId, timezone: content.timezone };\n};\n","/**\n * The actual StackRecordAdapter logic (minus lifecycle: initialize/open/\n * close/flush stay per-engine, since schema setup, pragmas, locking, and\n * durability differ there). Every CRUD/query/version/type/association\n * operation lives here exactly once, parametrized over a SqlExecutor\n * (which normalizes each engine's call conventions). Concrete adapters\n * construct one of these and delegate their StackRecordAdapter methods\n * to it — see record-adapter-sqlite.\n */\n\nimport {\n StackConflictError,\n StackVersionConflictError,\n StackNotFoundError,\n StackQueryError,\n applyMergePatch,\n} from '@haverstack/core';\nimport type {\n StackRecord,\n StackType,\n TypeId,\n FileId,\n RecordVersion,\n StackQuery,\n QueryResult,\n Association,\n Permission,\n ActorOptions,\n ExpectedVersionOptions,\n} from '@haverstack/core';\nimport type { SqlExecutor } from './executor.js';\nimport { isForeignKeyViolation, isUniqueConstraintViolation } from './executor.js';\nimport { buildQueryPlan, atBudget } from './query.js';\nimport { fts5Strategy } from './fts5.js';\nimport {\n rowToRecord,\n rowToAssociation,\n rowToType,\n rowToVersion,\n toMs,\n associationKeyColumns,\n} from './mappers.js';\nimport { makeCursor } from './cursor.js';\nimport { contentSortEntry } from '@haverstack/core/adapter';\nimport type { ScalarFieldKind } from '@haverstack/core';\n\n/**\n * Run a query statement, restating an engine parse error as the wire\n * taxonomy's `bad_request` rather than letting it escape as a raw engine\n * error (which a server has no code to map, so it becomes a 500).\n *\n * Scoped to the one filter that carries a query language: `filter.search`\n * is FTS5 source text, and sanitizeFts5Query() repairs the common shapes\n * but claims no completeness against the grammar. Every other clause here\n * is built from parameters, so a parse error in one is this module's bug —\n * rethrown untouched, where it belongs.\n */\nconst asStackQueryError = <T>(query: StackQuery, run: () => T): T => {\n try {\n return run();\n } catch (err) {\n if (!query.filter?.search) throw err;\n throw new StackQueryError(\n `Search text could not be parsed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n};\n\nexport type SharedSqlRecordLogicDeps = {\n exec: SqlExecutor;\n};\n\n/**\n * Top-level scalar fields, by name and declared kind — what content_index\n * covers. Arrays and objects are left out: a value inside one has no\n * single position to order its record by, and no file reference the\n * attachment filter can see.\n */\nconst indexedFieldKinds = (\n schema: Record<string, { kind?: string }>,\n): Map<string, ScalarFieldKind> => {\n const kinds = new Map<string, ScalarFieldKind>();\n for (const [field, def] of Object.entries(schema)) {\n if (!def.kind || def.kind === 'array' || def.kind === 'object') continue;\n kinds.set(field, def.kind as ScalarFieldKind);\n }\n return kinds;\n};\n\nexport class SharedSqlRecordLogic {\n /**\n * Per-typeId cache of the fields content_index covers and their declared\n * kinds, so syncContentIndex() doesn't hit the `types` table and\n * re-parse schema JSON on every write. Populated eagerly in saveType()\n * and lazily via getIndexedFields().\n */\n private readonly indexedFieldsByType = new Map<string, Map<string, ScalarFieldKind>>();\n\n constructor(private readonly deps: SharedSqlRecordLogicDeps) {}\n\n private get exec(): SqlExecutor {\n return this.deps.exec;\n }\n\n /**\n * SQL fragment (plus its bind params) that gates a records-table\n * UPDATE/DELETE on the opt-in `expectedVersion` precondition. Empty when\n * expectedVersion is omitted, keeping today's unconditional behavior.\n */\n private versionGuard(expectedVersion: number | undefined): { clause: string; params: unknown[] } {\n return expectedVersion === undefined\n ? { clause: '', params: [] }\n : { clause: ' AND version = ?', params: [expectedVersion] };\n }\n\n /**\n * Precondition check for mutations that can't fold expectedVersion into\n * their primary UPDATE's WHERE clause: patchContent/restoreVersion need\n * fts.remove() to run *before* the records-table content changes, so\n * the check happens first, standalone.\n */\n private checkExpectedVersion(record: StackRecord, expectedVersion: number | undefined): void {\n if (expectedVersion === undefined || record.version === expectedVersion) return;\n throw new StackVersionConflictError(\n `Record \"${record.id}\" is at version ${record.version}, expected ${expectedVersion}`,\n record.id,\n expectedVersion,\n record.version,\n );\n }\n\n /**\n * Called after a versionGuard()-gated statement affected zero rows.\n * Distinguishes \"record doesn't exist\" (StackNotFoundError) from \"it\n * exists but isn't at expectedVersion\" (StackVersionConflictError, with\n * the actual current version for the caller to act on).\n */\n private throwVersionConflict(id: string, expectedVersion: number | undefined): never {\n const row = this.exec.get<{ version: number }>('SELECT version FROM records WHERE id = ?', [\n id,\n ]);\n if (!row) throw new StackNotFoundError(`Record not found: \"${id}\"`);\n throw new StackVersionConflictError(\n `Record \"${id}\" is at version ${row.version}, expected ${expectedVersion}`,\n id,\n expectedVersion as number,\n row.version,\n );\n }\n\n // -------------------------------------------------------\n // Records\n // -------------------------------------------------------\n\n /**\n * A duplicate id hits the `records` PK — mapped to StackConflictError\n * instead of surfacing the raw engine exception, mirroring saveVersion's\n * collision mapping below.\n */\n async createRecord(record: StackRecord): Promise<StackRecord> {\n try {\n this.exec.run(\n `INSERT INTO records\n (id, type_id, created_at, updated_at, content, version,\n parent_id, entity_id, app_id, principal_id, updated_by, updated_via,\n deleted_at, unlisted_at, permissions)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n record.id,\n record.typeId,\n toMs(record.createdAt),\n toMs(record.updatedAt),\n JSON.stringify(record.content),\n record.version,\n record.parentId ?? null,\n record.entityId ?? null,\n record.appId ?? null,\n record.principalId ?? null,\n record.updatedBy ?? null,\n record.updatedVia ?? null,\n record.deletedAt ? toMs(record.deletedAt) : null,\n record.unlistedAt ? toMs(record.unlistedAt) : null,\n record.permissions ? JSON.stringify(record.permissions) : null,\n ],\n );\n } catch (err) {\n if (isUniqueConstraintViolation(err)) {\n throw new StackConflictError(`Record already exists: \"${record.id}\"`);\n }\n throw err;\n }\n\n if (record.associations?.length) {\n this.insertAssociations(record.id, record.associations);\n }\n\n fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));\n this.syncContentIndex(record.id, record.typeId, record.content);\n return record;\n }\n\n async getRecord(id: string): Promise<StackRecord | null> {\n return this.readRecord(id);\n }\n\n /**\n * The synchronous read behind getRecord(). Callers inside exec.transaction()\n * use this one: an `await` inside that callback yields the microtask queue\n * mid-transaction, and the next operation to run would try to open one of\n * its own — transaction() requires a synchronous callback for exactly this\n * reason (see SqlExecutor.transaction).\n *\n * The same synchrony is what lets the post-commit reads below report the\n * version their own mutation produced: `getRecord()` runs its body before\n * the `await` yields, so nothing interleaves between the commit and the\n * read. A backend that made these reads genuinely asynchronous would open\n * that window, and a concurrent mutation could be reported under this\n * one's verb.\n */\n private readRecord(id: string): StackRecord | null {\n const row = this.exec.get<Record<string, unknown>>('SELECT * FROM records WHERE id = ?', [id]);\n if (!row) return null;\n const associations = this.getAssociationsForRecord(id);\n return rowToRecord(row, associations);\n }\n\n async patchContent(\n id: string,\n patch: Record<string, unknown | null>,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n const existing = await this.getRecord(id);\n if (!existing) throw new Error(`Record not found: \"${id}\"`);\n this.checkExpectedVersion(existing, opts.expectedVersion);\n\n const merged = applyMergePatch(existing.content, patch);\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n fts5Strategy.remove(this.exec, id);\n this.exec.run(\n `UPDATE records SET content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?`,\n [\n JSON.stringify(merged),\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ],\n );\n fts5Strategy.insert(this.exec, id, JSON.stringify(merged));\n this.syncContentIndex(id, existing.typeId, merged);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after patchContent: \"${id}\"`);\n return updated;\n }\n\n async deleteRecord(\n id: string,\n opts: {\n hard?: boolean;\n expectedVersion?: number;\n snapshot?: RecordVersion;\n } & ActorOptions = {},\n ): Promise<StackRecord | null> {\n if (opts.hard) {\n return this.exec.transaction(() => this.hardDeleteRecord(id, opts.expectedVersion));\n } else {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET deleted_at = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [\n toMs(new Date()),\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ...verParams,\n ],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n });\n }\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after deleteRecord: \"${id}\"`);\n return updated;\n }\n\n /**\n * Deletes a record's FTS entry, associations, versions, file-refs, sort\n * index rows, and row, returning the record as it stood — the last copy that will ever\n * exist, read here so that nothing can overtake it between the read and\n * the deletes. Null when there was no such record. No write() call —\n * callers batch it. expectedVersion is checked first so a lost CAS race\n * leaves nothing touched (children reference the row, so the check can't\n * fold into the final DELETE).\n */\n private hardDeleteRecord(id: string, expectedVersion?: number): StackRecord | null {\n const purged = this.readRecord(id);\n if (expectedVersion !== undefined) {\n if (!purged) throw new StackNotFoundError(`Record not found: \"${id}\"`);\n if (purged.version !== expectedVersion) {\n throw new StackVersionConflictError(\n `Record \"${id}\" is at version ${purged.version}, expected ${expectedVersion}`,\n id,\n expectedVersion,\n purged.version,\n );\n }\n }\n if (!purged) return null;\n fts5Strategy.remove(this.exec, id);\n this.exec.run('DELETE FROM associations WHERE record_id = ?', [id]);\n this.exec.run('DELETE FROM versions WHERE record_id = ?', [id]);\n this.exec.run('DELETE FROM content_index WHERE record_id = ?', [id]);\n this.exec.run('DELETE FROM records WHERE id = ?', [id]);\n return purged;\n }\n\n async undeleteRecord(\n id: string,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET deleted_at = NULL, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [toMs(new Date()), opts.updatedBy ?? null, opts.updatedVia ?? null, id, ...verParams],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after undelete: \"${id}\"`);\n return updated;\n }\n\n async setPermissions(\n id: string,\n permissions: Permission[],\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET permissions = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [\n permissions.length ? JSON.stringify(permissions) : null,\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ...verParams,\n ],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after setPermissions: \"${id}\"`);\n return updated;\n }\n\n async setUnlisted(\n id: string,\n unlisted: boolean,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const now = toMs(new Date());\n const changed = this.exec.run(\n `UPDATE records SET unlisted_at = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [\n unlisted ? now : null,\n now,\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ...verParams,\n ],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after setUnlisted: \"${id}\"`);\n return updated;\n }\n\n async restoreVersion(\n id: string,\n version: number,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n const existing = await this.getRecord(id);\n if (!existing) throw new Error(`Record not found: \"${id}\"`);\n this.checkExpectedVersion(existing, opts.expectedVersion);\n\n const target = await this.getVersion(id, version);\n if (!target) throw new Error(`Version not found: ${id}@${version}`);\n\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n fts5Strategy.remove(this.exec, id);\n this.exec.run(\n `UPDATE records SET type_id = ?, content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?`,\n [\n target.typeId,\n JSON.stringify(target.content),\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ],\n );\n if (target.associations !== undefined) {\n this.exec.run('DELETE FROM associations WHERE record_id = ?', [id]);\n if (target.associations.length) this.insertAssociations(id, target.associations);\n }\n fts5Strategy.insert(this.exec, id, JSON.stringify(target.content));\n this.syncContentIndex(id, target.typeId, target.content);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after restoreVersion: \"${id}\"`);\n return updated;\n }\n\n async commitMigration(\n id: string,\n toTypeId: TypeId,\n content: Record<string, unknown>,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n // Checked here rather than folded into the UPDATE's WHERE clause:\n // fts5Strategy.remove() has to run before the content changes, so the\n // precondition has to settle first — same shape as patchContent() and\n // restoreVersion().\n const existing = await this.getRecord(id);\n if (!existing) throw new Error(`Record not found: \"${id}\"`);\n this.checkExpectedVersion(existing, opts.expectedVersion);\n\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n fts5Strategy.remove(this.exec, id);\n this.exec.run(\n `UPDATE records SET type_id = ?, content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?`,\n [\n toTypeId,\n JSON.stringify(content),\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ],\n );\n fts5Strategy.insert(this.exec, id, JSON.stringify(content));\n this.syncContentIndex(id, toTypeId, content);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after commitMigration: \"${id}\"`);\n return updated;\n }\n\n async queryRecords(query: StackQuery): Promise<QueryResult> {\n const limit = query.limit ?? 50;\n // One extra row, to learn whether a further page follows.\n const fetch = limit + 1;\n\n // A content-field sort answers from two statements, the records that\n // hold a value at the field and then the records that hold none. They\n // read in order, each asking only for the rows the ones before it left\n // of the page, and the second is skipped once the first has filled it —\n // the common case, since a page rarely straddles the boundary.\n const rows: Record<string, unknown>[] = [];\n for (const page of buildQueryPlan(query)) {\n const budget = fetch - rows.length;\n if (budget <= 0) break;\n const statement = atBudget(page, budget);\n rows.push(\n ...asStackQueryError(query, () =>\n this.exec.all<Record<string, unknown>>(statement.sql, statement.params),\n ),\n );\n }\n\n const hasMore = rows.length > limit;\n const page = hasMore ? rows.slice(0, limit) : rows;\n\n const records = page.map((row) => {\n const associations = this.getAssociationsForRecord(row.id as string);\n return rowToRecord(row, associations);\n });\n\n const lastRecord = records[records.length - 1];\n const cursor =\n hasMore && lastRecord\n ? makeCursor(lastRecord, query.sort, (record, field) => this.sortFieldKind(record, field))\n : null;\n\n return { records, cursor };\n }\n\n /**\n * Atomically verify fileId is unreferenced, then hard-delete its\n * metadata records across every typeId in `metadataTypeIds` (see\n * deleteUnreferencedAttachmentRecords on the adapter contract). A real\n * SQL transaction of synchronous calls — no `await` between the check\n * and the deletes, so nothing interleaves.\n */\n async deleteUnreferencedAttachmentRecords(\n fileId: FileId,\n metadataTypeIds: TypeId[],\n ): Promise<StackRecord[]> {\n return this.exec.transaction(() => {\n const referenced = this.exec.all<{ found: number }>(\n `SELECT 1 as found FROM associations WHERE kind = 'attachment' AND file_id = ?\n UNION ALL\n SELECT 1 FROM content_index WHERE file_id = ?\n LIMIT 1`,\n [fileId, fileId],\n );\n if (referenced.length) {\n throw new StackConflictError('Attachment is still referenced by one or more records');\n }\n\n // An empty family still owes the caller the reference check above —\n // the conflict is a fact about the file, not about which metadata\n // types happen to be defined.\n if (metadataTypeIds.length === 0) return [];\n\n const placeholders = metadataTypeIds.map(() => '?').join(', ');\n const metaRows = this.exec.all<{ id: string }>(\n `SELECT id FROM records\n WHERE type_id IN (${placeholders}) AND json_extract(content, '$.fileId') = ?`,\n [...metadataTypeIds, fileId],\n );\n const deleted: StackRecord[] = [];\n for (const row of metaRows) {\n const purged = this.hardDeleteRecord(row.id);\n if (purged) deleted.push(purged);\n }\n\n return deleted;\n });\n }\n\n // -------------------------------------------------------\n // Versions\n // -------------------------------------------------------\n\n async getVersions(id: string): Promise<RecordVersion[]> {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM versions WHERE record_id = ? ORDER BY version DESC',\n [id],\n );\n return rows.map(rowToVersion);\n }\n\n async getVersion(id: string, version: number): Promise<RecordVersion | null> {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM versions WHERE record_id = ? AND version = ?',\n [id, version],\n );\n return rows.length ? rowToVersion(rows[0]) : null;\n }\n\n /**\n * A (record_id, version) collision is rejected loudly via the UNIQUE\n * constraint, mapped to StackConflictError — never a silently discarded\n * snapshot leaving a hole in rollback history. See\n * docs/spec/versioning.md § Optimistic concurrency (`ifVersion`).\n */\n private insertVersionRow(id: string, version: RecordVersion): void {\n try {\n this.exec.run(\n `INSERT INTO versions\n (record_id, version, type_id, content, updated_at, entity_id,\n updated_by, updated_via, associations, permissions)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n id,\n version.version,\n version.typeId,\n JSON.stringify(version.content),\n toMs(version.updatedAt),\n version.entityId ?? null,\n version.updatedBy ?? null,\n version.updatedVia ?? null,\n version.associations ? JSON.stringify(version.associations) : null,\n version.permissions ? JSON.stringify(version.permissions) : null,\n ],\n );\n } catch (err) {\n if (isUniqueConstraintViolation(err)) {\n throw new StackConflictError(\n `Version ${version.version} already exists for record \"${id}\" — a concurrent ` +\n `writer raced past this version. Use ifVersion to detect this before it happens.`,\n );\n }\n throw err;\n }\n }\n\n private overwriteVersionRow(id: string, version: RecordVersion): void {\n this.exec.run(\n `UPDATE versions\n SET type_id = ?, content = ?, updated_at = ?, entity_id = ?, associations = ?, permissions = ?\n WHERE record_id = ? AND version = ?`,\n [\n version.typeId,\n JSON.stringify(version.content),\n toMs(version.updatedAt),\n version.entityId ?? null,\n version.associations ? JSON.stringify(version.associations) : null,\n version.permissions ? JSON.stringify(version.permissions) : null,\n id,\n version.version,\n ],\n );\n }\n\n /**\n * Standalone snapshot write for tooling and tests — loud on any\n * collision, since nothing here is about to bump the version. Mutating\n * methods take a `snapshot` option instead; see snapshotBeforeMutation.\n */\n async saveVersion(id: string, version: RecordVersion): Promise<void> {\n this.insertVersionRow(id, version);\n }\n\n /**\n * The snapshot half of a mutating method's atomic snapshot-then-mutate\n * transaction. A collision is a racing-writer conflict, except an orphan\n * row at the record's *current* version — recognized by version number\n * alone, never content — which is overwritten so the interrupted write\n * can complete. See docs/spec/versioning.md § Snapshot atomicity.\n */\n private snapshotBeforeMutation(id: string, version: RecordVersion): void {\n try {\n this.insertVersionRow(id, version);\n } catch (err) {\n if (!(err instanceof StackConflictError)) throw err;\n\n const row = this.exec.get<{ version: number }>('SELECT version FROM records WHERE id = ?', [\n id,\n ]);\n if (!row || row.version !== version.version) throw err;\n\n this.overwriteVersionRow(id, version);\n }\n }\n\n // -------------------------------------------------------\n // Types\n // -------------------------------------------------------\n\n async saveType(type: StackType): Promise<void> {\n this.exec.run(\n `INSERT OR REPLACE INTO types\n (id, base_id, version, name, schema, schema_hash, migrates_from, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n type.id,\n type.baseId,\n type.version,\n type.name,\n JSON.stringify(type.schema),\n type.schemaHash,\n type.migratesFrom ?? null,\n toMs(type.createdAt),\n ],\n );\n const schema = type.schema as Record<string, { kind?: string }>;\n this.indexedFieldsByType.set(type.id, indexedFieldKinds(schema));\n }\n\n async getType(id: TypeId): Promise<StackType | null> {\n const rows = this.exec.all<Record<string, unknown>>('SELECT * FROM types WHERE id = ?', [id]);\n return rows.length ? rowToType(rows[0]) : null;\n }\n\n async listTypes(): Promise<StackType[]> {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM types ORDER BY base_id, version',\n );\n return rows.map(rowToType);\n }\n\n // -------------------------------------------------------\n // Associations\n // -------------------------------------------------------\n\n async associate(\n recordId: string,\n association: Association,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(recordId, opts.snapshot);\n // Bump (and CAS-check) first, before the associations-table write, so\n // a lost race never partially applies.\n this.bumpVersion(recordId, opts);\n this.insertAssociations(recordId, [association]);\n });\n\n const updated = await this.getRecord(recordId);\n if (!updated) throw new Error(`Record not found after associate: \"${recordId}\"`);\n return updated;\n }\n\n async dissociate(\n recordId: string,\n association: Association,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(recordId, opts.snapshot);\n this.bumpVersion(recordId, opts);\n this.exec.run(\n `DELETE FROM associations\n WHERE record_id = ?\n AND kind = ?\n AND label = ?\n AND file_id = ?\n AND related_scope = ?\n AND related_id = ?\n AND related_ns = ?\n AND related_stack = ?`,\n [recordId, association.kind, association.label, ...associationKeyColumns(association)],\n );\n });\n\n const updated = await this.getRecord(recordId);\n if (!updated) throw new Error(`Record not found after dissociate: \"${recordId}\"`);\n return updated;\n }\n\n private bumpVersion(id: string, opts: ExpectedVersionOptions & ActorOptions = {}): void {\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [toMs(new Date()), opts.updatedBy ?? null, opts.updatedVia ?? null, id, ...verParams],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n }\n\n /**\n * FK enforcement (PRAGMA_FOREIGN_KEYS_ON) means inserting an\n * association against a record that doesn't exist throws — mapped\n * here to StackNotFoundError so associate() on a nonexistent record\n * fails loudly instead of silently creating an orphan row.\n */\n private insertAssociations(recordId: string, associations: Association[]): void {\n for (const assoc of associations) {\n try {\n this.exec.run(\n `INSERT OR IGNORE INTO associations\n (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n [recordId, assoc.kind, assoc.label, ...associationKeyColumns(assoc)],\n );\n } catch (err) {\n if (isForeignKeyViolation(err)) {\n throw new StackNotFoundError(`Record not found: \"${recordId}\"`);\n }\n throw err;\n }\n }\n }\n\n private getAssociationsForRecord(recordId: string): Association[] {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM associations WHERE record_id = ?',\n [recordId],\n );\n return rows.map(rowToAssociation);\n }\n\n /**\n * The fields content_index covers for typeId, from the in-memory cache\n * — falling back to a `types` lookup (and populating the cache) only for\n * a typeId this process hasn't seen via saveType() yet, e.g. right after\n * open().\n */\n private getIndexedFields(typeId: string): Map<string, ScalarFieldKind> {\n const cached = this.indexedFieldsByType.get(typeId);\n if (cached) return cached;\n\n const typeRow = this.exec.get<{ schema: string }>('SELECT schema FROM types WHERE id = ?', [\n typeId,\n ]);\n const fields = typeRow\n ? indexedFieldKinds(JSON.parse(typeRow.schema) as Record<string, { kind?: string }>)\n : new Map<string, ScalarFieldKind>();\n this.indexedFieldsByType.set(typeId, fields);\n return fields;\n }\n\n /**\n * Replace a record's content_index rows with what its content currently\n * holds, on every write that can change content or typeId, so neither\n * the ordering nor the file-reference lookup can drift from the record.\n * A field holding no orderable value gets no row at all, which is what\n * puts the record at the end of a sort on that field.\n */\n private syncContentIndex(\n recordId: string,\n typeId: string,\n content: Record<string, unknown>,\n ): void {\n this.exec.run('DELETE FROM content_index WHERE record_id = ?', [recordId]);\n\n for (const [field, kind] of this.getIndexedFields(typeId)) {\n const entry = contentSortEntry(kind, content[field]);\n if (!entry) continue;\n const sql = `INSERT OR IGNORE INTO content_index\n (record_id, field, value_rank, num_value, text_key, text_value, file_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)`;\n if (entry.kind === 'num') {\n this.exec.run(sql, [recordId, field, 0, entry.num, null, null, null]);\n } else {\n // A file-ref field's value is its file id, so the row it already\n // needs for ordering also answers the attachment filter — one\n // field is one row, whatever a query reads it for.\n const fileId = kind === 'file-ref' ? entry.text : null;\n this.exec.run(sql, [recordId, field, 1, null, entry.key, entry.text, fileId]);\n }\n }\n }\n\n /** The declared kind of a record's top-level `field`, if it has one. */\n private sortFieldKind(record: StackRecord, field: string): ScalarFieldKind | undefined {\n return this.getIndexedFields(record.typeId).get(field);\n }\n}\n","/**\n * The actual StackTokenStore logic (createToken/lookupToken/listTokens/\n * revokeToken), parametrized over a SqlExecutor. Currently consumed by\n * record-adapter-sqlite's NativeTokenStore, which points it at a file\n * separate from records — see TOKENS_SCHEMA_SQL in schema.ts.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { TokenSession } from '@haverstack/core';\nimport type { TokenInfo } from '@haverstack/core/wire';\nimport type { SqlExecutor } from './executor.js';\nimport { toMs, fromMs } from './mappers.js';\n\nexport type SharedTokenLogicDeps = {\n exec: SqlExecutor;\n};\n\nexport class SharedTokenLogic {\n constructor(private readonly deps: SharedTokenLogicDeps) {}\n\n private get exec(): SqlExecutor {\n return this.deps.exec;\n }\n\n async createToken(\n principalId: string,\n opts: { onBehalfOf?: string; label?: string; expiresAt?: Date } = {},\n ): Promise<{ id: string; token: string }> {\n const id = randomBytes(8).toString('hex');\n const token = randomBytes(32).toString('hex');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n this.exec.run(\n 'INSERT INTO tokens (id, token_hash, principal_id, subject_id, label, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)',\n [\n id,\n tokenHash,\n principalId,\n // Stored rather than left null for an undelegated token, so a\n // reader never has to know which column stands in for the other.\n opts.onBehalfOf ?? principalId,\n opts.label ?? null,\n toMs(new Date()),\n opts.expiresAt ? toMs(opts.expiresAt) : null,\n ],\n );\n return { id, token };\n }\n\n async lookupToken(token: string): Promise<TokenSession | null> {\n const hash = createHash('sha256').update(token).digest('hex');\n const row = this.exec.get<{\n principal_id: string;\n subject_id: string;\n expires_at: number | null;\n }>('SELECT principal_id, subject_id, expires_at FROM tokens WHERE token_hash = ?', [hash]);\n if (!row) return null;\n if (row.expires_at !== null && Date.now() > row.expires_at) return null;\n return { principalId: row.principal_id, subjectId: row.subject_id };\n }\n\n async listTokens(): Promise<TokenInfo[]> {\n const rows = this.exec.all<{\n id: string;\n principal_id: string;\n subject_id: string;\n label: string | null;\n created_at: number;\n expires_at: number | null;\n }>(\n 'SELECT id, principal_id, subject_id, label, created_at, expires_at FROM tokens ORDER BY created_at DESC',\n );\n return rows.map((row) => ({\n id: row.id,\n principalId: row.principal_id,\n subjectId: row.subject_id,\n ...(row.label && { label: row.label }),\n createdAt: fromMs(row.created_at),\n ...(row.expires_at !== null && { expiresAt: fromMs(row.expires_at) }),\n }));\n }\n\n async revokeToken(id: string): Promise<void> {\n this.exec.run('DELETE FROM tokens WHERE id = ?', [id]);\n }\n}\n","import type { SqlExecutor } from '@haverstack/sqlite-shared';\nimport { DatabaseSync } from './node-sqlite.js';\n\n/** Normalizes node:sqlite's spread-args run/get/all calls to SqlExecutor. */\nexport class NativeSqliteExecutor implements SqlExecutor {\n constructor(private readonly db: DatabaseSync) {}\n\n exec(sql: string): void {\n this.db.exec(sql);\n }\n\n run(sql: string, params: readonly unknown[] = []): number {\n const result = this.db.prepare(sql).run(...(params as (string | number | null)[]));\n return Number(result.changes);\n }\n\n get<T = Record<string, unknown>>(sql: string, params: readonly unknown[] = []): T | undefined {\n return this.db.prepare(sql).get(...(params as (string | number | null)[])) as T | undefined;\n }\n\n all<T = Record<string, unknown>>(sql: string, params: readonly unknown[] = []): T[] {\n return this.db.prepare(sql).all(...(params as (string | number | null)[])) as T[];\n }\n\n transaction<T>(fn: () => T): T {\n this.db.exec('BEGIN');\n try {\n const result = fn();\n this.db.exec('COMMIT');\n return result;\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n }\n}\n","/**\n * Bearer-token storage backing @haverstack/core's StackTokenStore,\n * implemented against its own file — deliberately separate from\n * NativeSQLiteRecordAdapter's records database. The portable stack file\n * is \"your data, take it with you\"; auth material shouldn't travel with\n * it, and restoring a backup shouldn't resurrect revoked tokens. Server\n * implementations wire these up as separate parts\n * (`{ adapter, tokens }`), the same philosophy as combineAdapters().\n *\n * The actual createToken/lookupToken/listTokens/revokeToken logic lives\n * in @haverstack/sqlite-shared's SharedTokenLogic, reached through the\n * SqlExecutor interface.\n */\n\nimport type { TokenSession } from '@haverstack/core';\nimport type { StackTokenStore, TokenInfo } from '@haverstack/core/wire';\nimport {\n TOKENS_SCHEMA_SQL,\n PRAGMA_JOURNAL_MODE_WAL,\n acquireLock,\n releaseLock,\n SharedTokenLogic,\n} from '@haverstack/sqlite-shared';\nimport { DatabaseSync } from './node-sqlite.js';\nimport { NativeSqliteExecutor } from './executor.js';\n\n/** Conventional sibling path for a token store beside a stack's main .db file. */\nexport const defaultTokenStorePath = (dbPath: string): string => `${dbPath}.tokens`;\n\nexport type NativeTokenStoreOptions = {\n /** Absolute path to the token store file. Created if it doesn't exist. */\n path: string;\n /** Bypass the storage-ownership lock check. See docs/spec/adapters.md § Concurrency & storage ownership. */\n force?: boolean;\n};\n\nexport class NativeTokenStore implements StackTokenStore {\n private db!: DatabaseSync;\n private tokens!: SharedTokenLogic;\n\n private constructor(private readonly path: string) {}\n\n /** Opens the token store, creating the file and schema if needed. */\n static async open(opts: NativeTokenStoreOptions): Promise<NativeTokenStore> {\n acquireLock(opts.path, opts.force);\n const store = new NativeTokenStore(opts.path);\n store.db = new DatabaseSync(opts.path);\n store.db.exec(PRAGMA_JOURNAL_MODE_WAL);\n store.db.exec(TOKENS_SCHEMA_SQL);\n store.tokens = new SharedTokenLogic({ exec: new NativeSqliteExecutor(store.db) });\n return store;\n }\n\n createToken(\n principalId: string,\n opts?: { onBehalfOf?: string; label?: string; expiresAt?: Date },\n ): Promise<{ id: string; token: string }> {\n return this.tokens.createToken(principalId, opts);\n }\n\n lookupToken(token: string): Promise<TokenSession | null> {\n return this.tokens.lookupToken(token);\n }\n\n listTokens(): Promise<TokenInfo[]> {\n return this.tokens.listTokens();\n }\n\n revokeToken(id: string): Promise<void> {\n return this.tokens.revokeToken(id);\n }\n\n async close(): Promise<void> {\n this.db.close();\n releaseLock(this.path);\n }\n}\n","/**\n * Haverstack — Native SQLite Record Adapter\n * -------------------------------------------------------\n * Implements StackRecordAdapter using node:sqlite (Node's built-in\n * SQLite binding, Node >= 22.5). No native compilation, no node-gyp,\n * no prebuilt binaries, and writes go straight to the file via normal\n * SQLite journaling (WAL mode) — no whole-database rewrite per write,\n * no in-memory copy of the whole store, real OS-level file locking.\n * Full-text search uses FTS5.\n *\n * A stack file is owned by exactly one process at a time (see\n * docs/spec/adapters.md § Concurrency & storage ownership). open()/initialize()\n * acquire a PID-stamped lock file beside the database and reject if\n * another live process already holds it; close() releases it.\n *\n * Token storage is a separate concern — see NativeTokenStore in this\n * package, which implements @haverstack/core's StackTokenStore against\n * its own file rather than this adapter's records database.\n *\n * This class itself is a thin node:sqlite binding: schema setup,\n * pragmas, and the storage-ownership lock are genuinely engine-specific\n * and live here. The actual StackRecordAdapter logic lives in\n * @haverstack/sqlite-shared's SharedSqlRecordLogic, reached through the\n * SqlExecutor interface (NativeSqliteExecutor here normalizes\n * node:sqlite's spread-args run/get/all calls to it).\n */\n\nimport { DatabaseSync } from './node-sqlite.js';\nimport { existsSync } from 'fs';\nimport type { StackType, TypeId, FileId, RecordVersion, ActorOptions } from '@haverstack/core';\nimport type {\n StackRecord,\n StackQuery,\n QueryResult,\n Association,\n Permission,\n} from '@haverstack/core';\nimport type { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';\nimport {\n RECORD_SCHEMA_SQL,\n FTS5_SCHEMA_SQL,\n PRAGMA_FOREIGN_KEYS_ON,\n PRAGMA_JOURNAL_MODE_WAL,\n acquireLock,\n releaseLock,\n insertConfigRecord,\n readStackConfig,\n SharedSqlRecordLogic,\n} from '@haverstack/sqlite-shared';\nimport { NativeSqliteExecutor } from './executor.js';\n\n// -------------------------------------------------------\n// Types\n// -------------------------------------------------------\n\nexport type NativeRecordInitializeOptions = {\n /** Absolute path to the .db file. Must not already exist. */\n path: string;\n /** IANA timezone string e.g. \"America/New_York\". Optional passthrough app metadata — no default. */\n timezone?: string;\n /** Entity ID of the stack owner. */\n entityId: string;\n /** Bypass the storage-ownership lock check. See NativeRecordOpenOptions.force. */\n force?: boolean;\n};\n\nexport type NativeRecordOpenOptions = {\n /** Absolute path to an existing .db file. */\n path: string;\n /**\n * Open even if a lock file from another live process is present.\n * Only needed if that process is gone but its PID was reused by\n * something else (the automatic stale-lock check already reclaims\n * locks whose owning process is no longer running).\n */\n force?: boolean;\n};\n\n// -------------------------------------------------------\n// NativeSQLiteRecordAdapter\n// -------------------------------------------------------\n\nexport class NativeSQLiteRecordAdapter implements StackRecordAdapter {\n readonly capabilities: AdapterCapabilities = {\n filter: {\n content: 'path',\n contentPresent: true,\n search: true,\n },\n sort: {\n fields: ['createdAt', 'updatedAt', 'version'],\n contentField: true,\n },\n limits: {\n attachmentBytes: null,\n contentBytes: null,\n },\n };\n\n ownerEntityId!: string;\n timezone: string | undefined;\n\n private db!: DatabaseSync;\n private record!: SharedSqlRecordLogic;\n\n private constructor(private readonly path: string) {}\n\n private wire(): void {\n const exec = new NativeSqliteExecutor(this.db);\n this.record = new SharedSqlRecordLogic({ exec });\n }\n\n /**\n * Initialize a new stack database. Fails if the file already exists —\n * use open() for existing databases.\n */\n static async initialize(opts: NativeRecordInitializeOptions): Promise<NativeSQLiteRecordAdapter> {\n if (existsSync(opts.path)) {\n throw new Error(\n `Cannot initialize: database already exists at \"${opts.path}\". ` +\n `Use NativeSQLiteRecordAdapter.open() instead.`,\n );\n }\n acquireLock(opts.path, opts.force);\n const adapter = new NativeSQLiteRecordAdapter(opts.path);\n adapter.db = new DatabaseSync(opts.path);\n adapter.db.exec(PRAGMA_FOREIGN_KEYS_ON);\n adapter.db.exec(PRAGMA_JOURNAL_MODE_WAL);\n adapter.db.exec(RECORD_SCHEMA_SQL);\n adapter.db.exec(FTS5_SCHEMA_SQL);\n adapter.wire();\n insertConfigRecord(new NativeSqliteExecutor(adapter.db), opts.entityId, opts.timezone);\n adapter.ownerEntityId = opts.entityId;\n adapter.timezone = opts.timezone;\n return adapter;\n }\n\n /**\n * Open an existing stack database. Fails if the file does not exist —\n * use initialize() for new databases.\n */\n static async open(opts: NativeRecordOpenOptions): Promise<NativeSQLiteRecordAdapter> {\n if (!existsSync(opts.path)) {\n throw new Error(\n `Cannot open: no database found at \"${opts.path}\". ` +\n `Use NativeSQLiteRecordAdapter.initialize() to create one.`,\n );\n }\n acquireLock(opts.path, opts.force);\n const adapter = new NativeSQLiteRecordAdapter(opts.path);\n adapter.db = new DatabaseSync(opts.path);\n adapter.db.exec(PRAGMA_FOREIGN_KEYS_ON);\n adapter.db.exec(PRAGMA_JOURNAL_MODE_WAL);\n adapter.db.exec(RECORD_SCHEMA_SQL);\n adapter.db.exec(FTS5_SCHEMA_SQL);\n adapter.wire();\n const config = readStackConfig(new NativeSqliteExecutor(adapter.db));\n adapter.ownerEntityId = config.entityId;\n adapter.timezone = config.timezone;\n return adapter;\n }\n\n // -------------------------------------------------------\n // Records\n // -------------------------------------------------------\n\n createRecord(record: StackRecord): Promise<StackRecord> {\n return this.record.createRecord(record);\n }\n\n getRecord(id: string): Promise<StackRecord | null> {\n return this.record.getRecord(id);\n }\n\n patchContent(\n id: string,\n patch: Record<string, unknown | null>,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.patchContent(id, patch, opts);\n }\n\n deleteRecord(\n id: string,\n opts?: { hard?: boolean; expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord | null> {\n return this.record.deleteRecord(id, opts);\n }\n\n undeleteRecord(\n id: string,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.undeleteRecord(id, opts);\n }\n\n setPermissions(\n id: string,\n permissions: Permission[],\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.setPermissions(id, permissions, opts);\n }\n\n setUnlisted(\n id: string,\n unlisted: boolean,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.setUnlisted(id, unlisted, opts);\n }\n\n restoreVersion(\n id: string,\n version: number,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.restoreVersion(id, version, opts);\n }\n\n commitMigration(\n id: string,\n toTypeId: TypeId,\n content: Record<string, unknown>,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.commitMigration(id, toTypeId, content, opts);\n }\n\n queryRecords(query: StackQuery): Promise<QueryResult> {\n return this.record.queryRecords(query);\n }\n\n deleteUnreferencedAttachmentRecords(\n fileId: FileId,\n metadataTypeIds: TypeId[],\n ): Promise<StackRecord[]> {\n return this.record.deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds);\n }\n\n // -------------------------------------------------------\n // Versions\n // -------------------------------------------------------\n\n getVersions(id: string): Promise<RecordVersion[]> {\n return this.record.getVersions(id);\n }\n\n getVersion(id: string, version: number): Promise<RecordVersion | null> {\n return this.record.getVersion(id, version);\n }\n\n saveVersion(id: string, version: RecordVersion): Promise<void> {\n return this.record.saveVersion(id, version);\n }\n\n // -------------------------------------------------------\n // Types\n // -------------------------------------------------------\n\n saveType(type: StackType): Promise<void> {\n return this.record.saveType(type);\n }\n\n getType(id: TypeId): Promise<StackType | null> {\n return this.record.getType(id);\n }\n\n listTypes(): Promise<StackType[]> {\n return this.record.listTypes();\n }\n\n // -------------------------------------------------------\n // Associations\n // -------------------------------------------------------\n\n associate(\n recordId: string,\n association: Association,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.associate(recordId, association, opts);\n }\n\n dissociate(\n recordId: string,\n association: Association,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.dissociate(recordId, association, opts);\n }\n\n // -------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------\n\n /** Folds the WAL back into the main file — useful before copying/backing up the database. */\n async flush(): Promise<void> {\n this.db.exec('PRAGMA wal_checkpoint(TRUNCATE);');\n }\n\n async close(): Promise<void> {\n this.db.close();\n releaseLock(this.path);\n }\n}\n\nexport {\n NativeTokenStore,\n defaultTokenStorePath,\n type NativeTokenStoreOptions,\n} from './token-store.js';\n"]}
1
+ {"version":3,"sources":["../src/node-sqlite.ts","../../sqlite-shared/src/schema.ts","../../sqlite-shared/src/cursor.ts","../../sqlite-shared/src/fts5.ts","../../sqlite-shared/src/query.ts","../../sqlite-shared/src/mappers.ts","../../sqlite-shared/src/lock.ts","../../sqlite-shared/src/executor.ts","../../sqlite-shared/src/config.ts","../../sqlite-shared/src/record-logic.ts","../../sqlite-shared/src/token-logic.ts","../src/executor.ts","../src/token-store.ts","../src/index.ts"],"names":["StackQueryError","cursor","page","statement","contentSortEntry","existsSync"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Loads node:sqlite via process.getBuiltinModule() rather than a static\n * `import ... from 'node:sqlite'`. node:sqlite is new enough that some\n * bundler/test-runner toolchains (Vite/Vitest as of this writing —\n * https://github.com/vitest-dev/vitest/issues/7177) mis-resolve it,\n * stripping the \"node:\" prefix and trying to load a package literally\n * named \"sqlite\". process.getBuiltinModule() is the Node-sanctioned way\n * to reach a builtin module without going through import resolution at\n * all, so it's immune to that class of bug — useful here since a\n * consuming app's own toolchain could hit the same issue, not just ours.\n */\nexport const { DatabaseSync } = process.getBuiltinModule(\n 'node:sqlite',\n) as typeof import('node:sqlite');\n\nexport type DatabaseSync = InstanceType<typeof DatabaseSync>;\n","/**\n * Schema DDL shared by SQLite-backed record adapters. TOKENS_SCHEMA_SQL\n * is split out from RECORD_SCHEMA_SQL because token storage lives in its\n * own file, never bundled with records — see NativeTokenStore in\n * record-adapter-sqlite and the StackTokenStore portability rationale\n * (docs/spec/wire-format.md § Authentication).\n */\n\nexport const RECORD_SCHEMA_SQL = `\n CREATE TABLE IF NOT EXISTS records (\n id TEXT PRIMARY KEY,\n type_id TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n content TEXT NOT NULL CHECK (json_valid(content)),\n version INTEGER NOT NULL DEFAULT 1,\n parent_id TEXT,\n entity_id TEXT,\n app_id TEXT,\n principal_id TEXT,\n updated_by TEXT,\n updated_via TEXT,\n deleted_at INTEGER,\n unlisted_at INTEGER,\n permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions))\n ) STRICT;\n\n -- A relationship's target is (related_scope, related_id) plus one\n -- qualifier: related_stack for a record in another stack, related_ns for\n -- a foreign namespace. All four are in the primary key, so two targets\n -- differing only by namespace are two associations.\n --\n -- The primary key leads with record_id, so its implicit index already\n -- serves every by-record read and delete; the indexes below exist only\n -- for the filter directions it cannot answer.\n CREATE TABLE IF NOT EXISTS associations (\n record_id TEXT NOT NULL REFERENCES records(id),\n kind TEXT NOT NULL CHECK (kind IN ('tag', 'attachment', 'relationship')),\n label TEXT NOT NULL,\n file_id TEXT NOT NULL DEFAULT '',\n related_scope TEXT NOT NULL DEFAULT '' CHECK (related_scope IN ('', 'record', 'entity', 'external')),\n related_id TEXT NOT NULL DEFAULT '',\n related_ns TEXT NOT NULL DEFAULT '',\n related_stack TEXT NOT NULL DEFAULT '',\n PRIMARY KEY (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack)\n ) STRICT;\n\n -- parent_id holds the container the record sat in, NULL for the root --\n -- the same encoding records.parent_id uses, since a snapshot states\n -- where the record was rather than an instruction to apply.\n CREATE TABLE IF NOT EXISTS versions (\n record_id TEXT NOT NULL REFERENCES records(id),\n version INTEGER NOT NULL,\n type_id TEXT NOT NULL,\n content TEXT NOT NULL CHECK (json_valid(content)),\n updated_at INTEGER NOT NULL,\n entity_id TEXT,\n updated_by TEXT,\n updated_via TEXT,\n parent_id TEXT,\n associations TEXT CHECK (associations IS NULL OR json_valid(associations)),\n permissions TEXT CHECK (permissions IS NULL OR json_valid(permissions)),\n PRIMARY KEY (record_id, version)\n ) STRICT;\n\n CREATE TABLE IF NOT EXISTS types (\n id TEXT PRIMARY KEY,\n base_id TEXT NOT NULL,\n version INTEGER NOT NULL,\n name TEXT NOT NULL,\n schema TEXT NOT NULL CHECK (json_valid(schema)),\n schema_hash TEXT NOT NULL,\n migrates_from TEXT,\n created_at INTEGER NOT NULL\n ) STRICT;\n\n -- One row per top-level scalar content field a record holds a value at:\n -- what it orders as, and the file it references. See\n -- docs/spec/adapters.md § Adapter backends and\n -- docs/spec/data-model.md § Sorting by a content field.\n --\n -- value_rank is 0 for a numeric value and 1 for a text one — the same\n -- split as \"num_value IS NULL\", but non-null, so one index can carry\n -- the whole ordering. file_id is set only for a 'file-ref' field.\n CREATE TABLE IF NOT EXISTS content_index (\n record_id TEXT NOT NULL REFERENCES records(id),\n field TEXT NOT NULL,\n value_rank INTEGER NOT NULL CHECK (value_rank IN (0, 1)),\n num_value REAL,\n text_key TEXT,\n text_value TEXT,\n file_id TEXT,\n PRIMARY KEY (record_id, field)\n ) STRICT;\n\n -- Indexes. Each filter column continues into (created_at, id) — the\n -- default ordering and its tiebreak — because these columns are\n -- low-cardinality by design, and a narrow index on one leaves the\n -- planner sorting the whole matching set before it can honor LIMIT.\n --\n -- deleted_at and unlisted_at deliberately have none: nothing searches\n -- for the rows they mark, and an index on a column every query tests\n -- for IS NULL only lures the planner off the one answering ORDER BY.\n CREATE INDEX IF NOT EXISTS idx_records_type_id ON records(type_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_parent_id ON records(parent_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_entity_id ON records(entity_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_app_id ON records(app_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_principal_id ON records(principal_id, created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_created_at ON records(created_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_updated_at ON records(updated_at, id);\n CREATE INDEX IF NOT EXISTS idx_records_version ON records(version, id);\n\n -- Each association index ends with record_id so the semi-joins in\n -- query.ts read the matching record ids out of the index alone.\n -- idx_assoc_related carries related_ns ahead of related_id because a\n -- record- or entity-scoped target always stores '' there, which\n -- buildQueryPlan states explicitly to keep the prefix contiguous.\n CREATE INDEX IF NOT EXISTS idx_assoc_kind_label ON associations(kind, label, record_id);\n CREATE INDEX IF NOT EXISTS idx_assoc_kind_file_id ON associations(kind, file_id, record_id);\n CREATE INDEX IF NOT EXISTS idx_assoc_related ON associations(kind, related_scope, related_ns, related_id, record_id);\n CREATE INDEX IF NOT EXISTS idx_types_base_id ON types(base_id, version);\n\n -- The whole content ordering, in index order, for one field at a time:\n -- a content-sorted page is an ordered walk of a slice of this index.\n CREATE INDEX IF NOT EXISTS idx_content_index_sort\n ON content_index(field, value_rank, num_value, text_key, text_value, record_id);\n CREATE INDEX IF NOT EXISTS idx_content_index_file\n ON content_index(file_id, record_id) WHERE file_id IS NOT NULL;\n`;\n\n/**\n * Bearer-token storage backing StackTokenStore. Kept separate from\n * RECORD_SCHEMA_SQL so an adapter can put it in its own file — the\n * portable stack file shouldn't also carry a server's auth material.\n */\nexport const TOKENS_SCHEMA_SQL = `\n CREATE TABLE IF NOT EXISTS tokens (\n id TEXT PRIMARY KEY,\n token_hash TEXT NOT NULL UNIQUE,\n principal_id TEXT NOT NULL,\n subject_id TEXT NOT NULL,\n label TEXT,\n created_at INTEGER NOT NULL,\n expires_at INTEGER\n ) STRICT;\n`;\n\n/**\n * The full-text index behind `StackQuery.filter.search` (see fts5.ts).\n * What `columnsize=0` costs a reader is in docs/spec/adapters.md\n * § Adapter backends. `detail` stays at its default: sanitizeFts5Query\n * passes phrase queries through, and a phrase needs the token positions\n * only that setting records.\n */\nexport const FTS5_SCHEMA_SQL = `\n CREATE VIRTUAL TABLE IF NOT EXISTS records_fts USING fts5(\n content,\n content='records',\n content_rowid='rowid',\n columnsize=0\n );\n`;\n\n/**\n * Enforces the REFERENCES constraints declared above (off by default per\n * SQLite connection), so touching a nonexistent record fails loudly\n * instead of creating an orphan row. Adapters map the constraint\n * violation to StackNotFoundError at the call sites that can trigger it.\n */\nexport const PRAGMA_FOREIGN_KEYS_ON = `PRAGMA foreign_keys = ON;`;\n\n/**\n * WAL journaling: page-level writes, crash-safe without our own\n * temp-file-and-rename dance, and real SQLite file locking. Only\n * meaningful for a real file (a :memory: database silently ignores it).\n */\nexport const PRAGMA_JOURNAL_MODE_WAL = `PRAGMA journal_mode = WAL;`;\n","/**\n * Cursor encode/decode for keyset pagination. The payload is JSON in\n * base64 rather than delimited fields: a content sort carries the field's\n * stored text, and neither a field name nor a text value is constrained\n * to avoid whatever character a delimiter would claim. Uses btoa/atob\n * with an explicit UTF-8 step rather than Node's Buffer, so the codec\n * stays runtime-agnostic and a non-Latin-1 value survives the round trip.\n */\n\nimport { StackQueryError } from '@haverstack/core';\nimport { contentSortEntry } from '@haverstack/core/adapter';\nimport type { StackRecord, NativeSortField, QuerySort, ScalarFieldKind } from '@haverstack/core';\n\nexport type SortField = NativeSortField;\n\nexport const SORT_FIELDS: readonly SortField[] = ['createdAt', 'updatedAt', 'version'];\n\n/**\n * Which partition of the ordering a cursor sits in: a native column or a\n * content field's number, text, or absent value. The three content cases\n * are the boundary keyset pagination has to hold — a page that ended on\n * the last numeric value resumes at the first text one, not back at the\n * top of either.\n */\nexport type CursorKind = 'native' | 'num' | 'text' | 'absent';\n\nexport type DecodedCursor =\n | { kind: 'native'; field: SortField; value: number; id: string }\n | { kind: 'num'; field: string; value: number; id: string }\n | { kind: 'text'; field: string; value: string; id: string }\n | { kind: 'absent'; field: string; id: string };\n\nconst toBase64 = (text: string): string => {\n const bytes = new TextEncoder().encode(text);\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary);\n};\n\nconst fromBase64 = (cursor: string): string => {\n const binary = atob(cursor);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n};\n\nexport const encodeCursor = (decoded: DecodedCursor): string =>\n toBase64(\n JSON.stringify(\n decoded.kind === 'absent'\n ? { k: decoded.kind, f: decoded.field, i: decoded.id }\n : { k: decoded.kind, f: decoded.field, i: decoded.id, v: decoded.value },\n ),\n );\n\n/** Throws StackQueryError for any malformed, corrupt, or unrecognized cursor. */\nexport const decodeCursor = (cursor: string): DecodedCursor => {\n const malformed = () => new StackQueryError(`Invalid cursor: malformed \"${cursor}\"`);\n let parsed: unknown;\n try {\n parsed = JSON.parse(fromBase64(cursor));\n } catch {\n throw malformed();\n }\n if (!parsed || typeof parsed !== 'object') throw malformed();\n const { k, f, i, v } = parsed as Record<string, unknown>;\n if (typeof f !== 'string' || !f || typeof i !== 'string') throw malformed();\n\n if (k === 'absent') return { kind: 'absent', field: f, id: i };\n if (k === 'text') {\n if (typeof v !== 'string') throw malformed();\n return { kind: 'text', field: f, value: v, id: i };\n }\n if (k === 'num' || k === 'native') {\n if (typeof v !== 'number' || !isFinite(v)) {\n throw new StackQueryError(`Invalid cursor: non-numeric sort value`);\n }\n if (k === 'native') {\n if (!SORT_FIELDS.includes(f as SortField)) {\n throw new StackQueryError(`Invalid cursor: unknown sort field \"${f}\"`);\n }\n return { kind: 'native', field: f as SortField, value: v, id: i };\n }\n return { kind: 'num', field: f, value: v, id: i };\n }\n throw malformed();\n};\n\nexport const getSortField = (query: { sort?: QuerySort }): SortField =>\n query.sort?.field ?? 'createdAt';\n\nexport const getSortColumn = (field: SortField): string =>\n field === 'createdAt' ? 'created_at' : field === 'updatedAt' ? 'updated_at' : 'version';\n\n/**\n * The cursor naming the last record of a page: where the next page\n * resumes. A content sort re-derives the record's ordered value from its\n * stored content rather than reading it back out of the index, so the\n * cursor and the index can only ever disagree if the write path is\n * broken — in which case pagination is not the thing to paper over.\n */\nexport const makeCursor = (\n record: StackRecord,\n sort: QuerySort | undefined,\n kindOf?: (record: StackRecord, field: string) => ScalarFieldKind | undefined,\n): string => {\n const contentField = sort?.contentField;\n if (contentField === undefined) {\n const field = getSortField({ sort });\n const value =\n field === 'updatedAt'\n ? record.updatedAt.getTime()\n : field === 'version'\n ? record.version\n : record.createdAt.getTime();\n return encodeCursor({ kind: 'native', field, value, id: record.id });\n }\n\n const kind = kindOf?.(record, contentField);\n const entry = kind ? contentSortEntry(kind, record.content[contentField]) : null;\n if (!entry) return encodeCursor({ kind: 'absent', field: contentField, id: record.id });\n return entry.kind === 'num'\n ? encodeCursor({ kind: 'num', field: contentField, value: entry.num, id: record.id })\n : encodeCursor({ kind: 'text', field: contentField, value: entry.text, id: record.id });\n};\n","/**\n * FTS5 query sanitization and indexing — the full-text search half of the\n * shared record logic.\n *\n * FTS5's query language supports operators (AND/OR/NOT, phrases, NEAR,\n * wildcards) that can be expensive or cause parse errors with untrusted\n * input, so a search string is rewritten before it reaches the engine:\n *\n * kept: AND/OR/NOT (with a left operand), phrase queries (\"…\"), implicit AND\n * removed: wildcards (*), NEAR(...) (terms kept, wrapper dropped), every\n * character outside the allow-list, bare NOT (no left operand),\n * operators with no right operand\n * capped: parenthesis nesting depth (default: 2)\n * closed: an odd trailing quote\n *\n * Two FTS5 grammar details drive the less obvious rules:\n *\n * - FTS5's NEAR is a function-call form — `NEAR(a b, 10)`. Deleting the\n * whole span would also delete the terms inside it, which can leave a\n * dangling operator when NEAR(...) sits next to AND/OR (e.g. \"x AND\n * NEAR(a b, 5)\" -> \"x AND\" — a syntax error). So this keeps the terms\n * and drops only the NEAR(...) wrapper and distance.\n * - FTS5 has real column-filter syntax and errors hard on any name but\n * the table's actual columns. It is wider than `colname:term`: `-name`\n * and `{a b}` filter columns with no colon at all, so an ordinary\n * hyphenated word is read as one. Since records_fts has exactly one\n * column and callers have no business targeting columns explicitly,\n * everything outside a phrase is reduced to an allow-list rather than\n * validated — see OUTSIDE_PHRASE_DISALLOWED.\n *\n * The rewrite is best-effort and makes no completeness claim against\n * FTS5's grammar — which is why the caller wraps execution as well, so a\n * case this misses surfaces as a StackQueryError rather than a raw engine\n * error. Search text is what a person typed into a box: an unbalanced\n * quote or a trailing \"AND\" is ordinary input, not a malformed request.\n *\n * What this bounds is the grammar, not the cost: a syntactically modest\n * search over a large index can still be expensive, and node:sqlite blocks\n * the calling thread, so there is no timeout to set from inside the call.\n * Bounding execution time belongs to whoever drives the engine under load\n * — see docs/spec/wire-format.md § Bounding query cost for the server-side\n * expectation and the `timeout` wire error that goes with it.\n */\n\nimport type { SqlExecutor } from './executor.js';\n\n/**\n * Outside a phrase, everything but a letter, digit, mark, underscore,\n * whitespace, paren or quote becomes a separator.\n *\n * An allow-list rather than a list of metacharacters to strip, because\n * FTS5's column-filter syntax is wider than it looks and an enumeration\n * kept missing parts of it: `-name` and `{a b}` filter columns with no\n * colon in sight, so `-cats` and even the hyphen inside `cats-dogs` were\n * read as column names and answered \"no such column\". `+` is a bare\n * syntax error. Listing what may pass means the next piece of syntax\n * nobody here anticipated arrives as a separator instead of as an error.\n *\n * Nothing is lost by it: FTS5's default tokenizer already splits terms on\n * these characters, so `cats-dogs` searches for the same two tokens it was\n * indexed as.\n */\nconst OUTSIDE_PHRASE_DISALLOWED = /[^\\p{L}\\p{N}\\p{M}_\\s()\"]/gu;\n\n/**\n * Collapse every whitespace run to a single space.\n *\n * Every rule below that reasons about what sits beside an operator or a\n * paren spells \"beside\" as `\\s*`, and a `\\s*` with a long run to chew on\n * re-tries the whole run once per starting position — quadratic in the\n * run, on text a caller types into a box. Keeping runs collapsed bounds\n * what any of them can backtrack over to a single character. What it does\n * not buy is linearity overall: the rewrite still costs a little more\n * than proportionally as the text grows, which is why bounding a search's\n * length belongs to whoever accepts it.\n *\n * Nothing is lost by it: FTS5's tokenizer splits on whitespace, so a\n * collapsed run tokenizes exactly as the original did — inside a phrase\n * as well as outside, since a phrase matches a sequence of tokens rather\n * than the spacing between them.\n */\nconst collapseWhitespace = (query: string): string => query.replace(/\\s+/g, ' ');\n\nexport const sanitizeFts5Query = (query: string, maxDepth = 2): string => {\n if (!query) return '';\n\n // Drop control characters, inside a phrase as well as outside. A NUL in\n // particular ends the string early for SQLite's C API, so the engine sees\n // an opening quote whose closing one is past the truncation and reports an\n // unterminated string; none of them can match an indexed token anyway.\n let clean = query.replace(/[\\p{Cc}\\p{Cf}]/gu, ' ');\n\n // Remove wildcards\n clean = clean.replace(/\\*/g, '');\n\n // Replace NEAR(terms, distance) with just its terms — see note above.\n //\n // Two things here are about what the engine does when the closing paren\n // never arrives, on text a caller types into a box.\n //\n // No `\\s*` after the paren, though the terms it captures may open with\n // whitespace: `[^,()]*` already matches a space, so a second quantifier\n // that also does gives the engine an ambiguous split to backtrack\n // through. The captured space is collapsed with all the rest on the way\n // out.\n //\n // And neither run crosses a `(`, which bounds where the failure is\n // found: `NEAR(NEAR(NEAR(` used to re-scan to the end of the string once\n // per occurrence. A NEAR's terms are words and phrases and its distance\n // is a number, so neither holds a paren in any input FTS5 would accept.\n clean = clean.replace(/\\bNEAR\\s*\\(([^,()]*)(?:,[^()]*)?\\)/gi, '$1');\n\n // A NEAR( with no closing paren isn't the function-call form the rule\n // above rewrites, and the paren auto-close below would hand FTS5 a NEAR\n // call it never meant to write. Keep the grouping, drop the keyword.\n clean = clean.replace(/\\bNEAR\\s*\\(/gi, '(');\n\n // Reduce to the characters that carry meaning outside a phrase — see note\n // above. Everything else becomes a separator, which is what FTS5's own\n // tokenizer would do with it anyway.\n clean = clean.replace(/\"[^\"]*\"|[^\"]+/g, (span) =>\n span.startsWith('\"') ? span : span.replace(OUTSIDE_PHRASE_DISALLOWED, ' '),\n );\n\n // The reduction above turns every disallowed character into a space, so\n // this is where the long runs come from.\n clean = collapseWhitespace(clean);\n\n // Strip bare NOT with no left operand — FTS5 requires \"term NOT term\", not \"NOT term\"\n clean = clean.replace(/(?:^|\\(\\s*)NOT\\s+/gi, (m) => m.replace(/NOT\\s+/i, ''));\n\n // Enforce max nesting depth; replace excess ( with a space and discard\n // unmatched ). Parens inside a phrase are literal text to FTS5, so the\n // depth count steps over quoted spans rather than reading them as syntax.\n let currentDepth = 0;\n let inPhrase = false;\n let result = '';\n for (const char of clean) {\n if (char === '\"') {\n inPhrase = !inPhrase;\n result += char;\n } else if (inPhrase) {\n result += char;\n } else if (char === '(') {\n if (currentDepth < maxDepth) {\n currentDepth++;\n result += char;\n } else result += ' ';\n } else if (char === ')') {\n if (currentDepth > 0) {\n currentDepth--;\n result += char;\n } else result += ' ';\n } else {\n result += char;\n }\n }\n\n // Close an odd trailing quote before the parens, so the added ) lands\n // outside the phrase rather than becoming a literal character in it.\n if (inPhrase) result += '\"';\n\n // Auto-close any unclosed parens\n if (currentDepth > 0) result += ')'.repeat(currentDepth);\n\n // Remove empty paren pairs left behind by NEAR/NOT stripping, and drop\n // operators left without an operand. Looped together because each can\n // create work for the other: removing an operator can empty a paren pair\n // (\"(a AND)\" -> \"(a)\" -> ...), and removing a pair can strand the\n // operator that pointed at it (\"x AND ()\" -> \"x AND\").\n let prev: string;\n do {\n prev = result;\n // Empty phrases and empty paren pairs, each of which matches nothing,\n // in one rule so that a run of them collapses to a single space\n // rather than to one space apiece — a run of spaces is what the rules\n // below would have to backtrack over. An empty phrase is also the one\n // construct whose quotes can re-pair differently once a rule inserts\n // text beside it, which is why this repeats to a fixpoint.\n result = result.replace(/(?:\"\"|\\(\\s*\\))+/g, ' ');\n result = dropDanglingOperators(result);\n result = restoreImplicitAnd(result);\n } while (result !== prev);\n\n return collapseWhitespace(result).replace(/\\(\\s+/g, '(').replace(/\\s+\\)/g, ')').trim();\n};\n\n/** An operator FTS5 requires a term on both sides of. */\nconst OPERATOR = String.raw`(?:AND|OR|NOT)`;\n\n/**\n * Stands in for a phrase while operators are rewritten. A private-use\n * character is not a word character, so `\\b` still sees the operator\n * beside it, while `^`, `$` and the paren anchors see an operand — which\n * is what a phrase is. Nothing reaches here holding one: `clean` is\n * rewritten from the caller's string, and a literal U+E000 in it would be\n * a term character FTS5 tokenizes away regardless.\n */\nconst PHRASE_HOLE = '\\u{E000}';\n\n/**\n * Drop operators left without an operand — the shape a person types on\n * their way to a longer query (\"cats AND\") or lands on once the rules\n * above have removed a term. FTS5 rejects each of them outright, so what\n * would otherwise be a syntax error becomes the search for the terms that\n * are actually there.\n *\n * Phrases are masked out first: `\"cats AND dogs\"` is literal text to FTS5,\n * and rewriting inside it would change what the user asked for.\n */\nconst dropDanglingOperators = (query: string): string =>\n withPhrasesMasked(query, (masked) => {\n let out = masked;\n let prev: string;\n do {\n prev = out;\n // A run of operators keeps only the first — the rest have no left operand.\n out = out.replace(\n new RegExp(String.raw`\\b(${OPERATOR})\\b(?:\\s+\\b${OPERATOR}\\b)+`, 'gi'),\n '$1',\n );\n // No left operand: at the start of the query or straight after \"(\".\n out = out.replace(new RegExp(String.raw`(^|\\()\\s*\\b${OPERATOR}\\b\\s*`, 'gi'), '$1');\n // No right operand: at the end of the query or straight before \")\".\n // No leading `\\s*` — whitespace before the operator is left for the\n // collapse on the way out, and a `\\s*` here would re-scan a run of\n // spaces once per position in it before failing to find an operator.\n out = out.replace(new RegExp(String.raw`\\b${OPERATOR}\\b\\s*($|\\))`, 'gi'), '$1');\n } while (out !== prev);\n return out;\n });\n\n/**\n * Run `rewrite` over the query with each phrase swapped for PHRASE_HOLE,\n * then put the phrases back in order. Every rule that reasons about what\n * sits beside an operator or a paren needs this: inside a phrase the text\n * is literal, and from outside one the phrase is a single operand.\n */\nconst withPhrasesMasked = (query: string, rewrite: (masked: string) => string): string => {\n const phrases: string[] = [];\n const masked = query.replace(/\"[^\"]*\"/g, (phrase) => {\n phrases.push(phrase);\n return PHRASE_HOLE;\n });\n let i = 0;\n return rewrite(masked).replace(new RegExp(PHRASE_HOLE, 'g'), () => phrases[i++]);\n};\n\n/**\n * Put back the operator FTS5 requires around a group. Two terms side by\n * side are an implicit AND (`cats dogs`), but a group is not: `cats (dogs)`\n * is a syntax error where `cats AND (dogs)` is fine. Grouping survives a\n * rewrite that strips the operator which used to sit beside it, so this\n * names the AND that was always meant rather than dropping the parens.\n *\n * Both rules test a *whole* word against the operator list. Matching a\n * prefix instead reads the `D` of a preceding `AND` as a term and inserts\n * a second one beside it.\n *\n * Phrases are masked for the same reason as elsewhere — a phrase is an\n * operand, so `\"a b\" (c)` needs the operator too.\n */\nconst OPERAND_CHAR = String.raw`[\\p{L}\\p{N}_${PHRASE_HOLE}]`;\n\nconst isOperator = (word: string): boolean => /^(?:AND|OR|NOT)$/i.test(word);\n\nconst restoreImplicitAnd = (query: string): string =>\n withPhrasesMasked(query, (masked) =>\n masked\n // \")\" then an operand — \") cats\", \") (\", \") <phrase>\" — unless that\n // operand is an operator, which already joins the two sides.\n .replace(\n new RegExp(String.raw`\\)\\s*(?=${OPERAND_CHAR}|\\()`, 'gu'),\n (match, offset: number, whole: string) => {\n const next = /^\\s*([\\p{L}\\p{N}_]+)/u.exec(whole.slice(offset + match.length));\n return next && isOperator(next[1]) ? match : ') AND ';\n },\n )\n // An operand then \"(\", unless the operand is an operator.\n // The lookbehind pins the match to the start of the operand run.\n // Without it every position inside a run is a start position, and\n // each one re-scans the rest of the run before the lookahead\n // rejects it — quadratic in a long word before a paren.\n .replace(\n new RegExp(String.raw`(?<!${OPERAND_CHAR})(${OPERAND_CHAR}+)\\s*(?=\\()`, 'gu'),\n (match, word: string) => (isOperator(word) ? match : `${word} AND `),\n ),\n );\n\n// -------------------------------------------------------\n// Indexing strategy\n// -------------------------------------------------------\n\n/**\n * FTS5's external-content table needs its special `('delete', rowid,\n * content)` command — with the *old* content — to unindex a row; a plain\n * DELETE leaves stale, still-searchable entries. remove() therefore reads\n * the current rowid/content and must run before the caller's change.\n */\nexport const fts5Strategy = {\n /** Index a record that has no FTS entry yet (used right after an INSERT). */\n insert(exec: SqlExecutor, recordId: string, content: string): void {\n exec.run(`INSERT INTO records_fts(rowid, content) SELECT rowid, ? FROM records WHERE id = ?`, [\n content,\n recordId,\n ]);\n },\n /**\n * Remove a record's FTS entry. MUST be called before the records row's\n * content changes or the row is deleted — see the note above.\n */\n remove(exec: SqlExecutor, recordId: string): void {\n const row = exec.get<{ rowid: number; content: string }>(\n 'SELECT rowid, content FROM records WHERE id = ?',\n [recordId],\n );\n if (!row) return;\n exec.run(`INSERT INTO records_fts(records_fts, rowid, content) VALUES('delete', ?, ?)`, [\n row.rowid,\n row.content,\n ]);\n },\n};\n","/**\n * StackQuery -> SQL. Engine-independent — shared verbatim between\n * SQLite-backed record adapters. The one caveat is full-text search:\n * `f.search` here assumes a `records_fts` virtual table with a MATCH-able\n * `content` column (see fts5.ts).\n *\n * A query compiles to a plan rather than to a single statement, because a\n * content-field sort is two orderings end to end: the records that hold a\n * value at the field, ordered by it, then the records that hold none,\n * ordered by id. Reading them as two statements lets each one walk an\n * index in order and stop at the page boundary; the LEFT JOIN that would\n * express the same result in one statement can only be sorted after the\n * fact, which costs a full pass over the table for every page.\n */\n\nimport { StackQueryError, type StackQuery } from '@haverstack/core';\nimport { contentSortKey, parseContentFilterKey } from '@haverstack/core/adapter';\nimport {\n decodeCursor,\n getSortColumn,\n getSortField,\n type DecodedCursor,\n type SortField,\n} from './cursor.js';\nimport { sanitizeFts5Query } from './fts5.js';\n\nexport { getSortField, getSortColumn };\nexport type { SortField };\n\n/** One statement plus the parameters it binds, in textual order. */\nexport type QueryStatement = { sql: string; params: unknown[] };\n\n/**\n * A page statement with the row budget its trailing `LIMIT ?` binds.\n * buildQueryPlan leaves that parameter off, so the budget is never one of\n * the parameters the query itself bound: the second partition of a\n * content sort asks for what the first left of the page rather than for a\n * page it then discards.\n */\nexport const atBudget = (page: QueryStatement, rows: number): QueryStatement => ({\n sql: page.sql,\n params: [...page.params, rows],\n});\n\n/**\n * Reduce `sort.direction` to a keyword this module may interpolate into\n * SQL. Core's assertValidSort() already rejects anything else at the\n * invariant layer, but this builder writes the value straight into\n * `ORDER BY` and the cursor comparison, so it re-checks rather than trust\n * an upstream that a future refactor could bypass: an unvalidated direction\n * here is a SQL-injection sink. Anything but \"asc\" is treated as the\n * default \"desc\" only after passing the guard.\n */\nconst sqlDirection = (query: StackQuery): 'ASC' | 'DESC' => {\n const dir = query.sort?.direction ?? 'desc';\n if (dir !== 'asc' && dir !== 'desc') {\n throw new StackQueryError(`Invalid sort direction \"${dir}\": expected \"asc\" or \"desc\".`);\n }\n return dir === 'asc' ? 'ASC' : 'DESC';\n};\n\n/**\n * Normalize a json_each row's value so one array element and one bare\n * value are walked by the same clause: an array stands for its elements,\n * anything else for itself. See docs/spec/data-model.md § Nested content\n * paths.\n */\nconst spread = (alias: string): string =>\n `CASE WHEN ${alias}.type = 'array' THEN ${alias}.value ` +\n `WHEN ${alias}.type = 'object' THEN json_array(json(${alias}.value)) ` +\n `ELSE json_array(${alias}.value) END`;\n\n/**\n * The members of a json_each row that is an object, and none for a row\n * that is not — a path descending through a scalar reaches no value rather\n * than reaching an error.\n */\nconst members = (alias: string): string =>\n `CASE WHEN ${alias}.type = 'object' THEN ${alias}.value ELSE '{}' END`;\n\n/**\n * True when some value reached by `segments` satisfies `leaf`, applied to\n * the alias holding that value. Each segment is a bound parameter matched\n * against json_each's `key`, never interpolated into a path expression, so\n * no key can be read as syntax. See docs/spec/data-model.md § Filter.\n */\nconst contentPathExists = (\n segments: string[],\n leaf: (alias: string) => string,\n params: unknown[],\n nextAlias: () => string,\n): string => {\n // The walk is a flat join list, not a subquery per segment: both SQLite\n // builds cap expression-tree depth far below the segment cap, and\n // nesting spends that budget while a join list spends the join budget\n // the cap is actually sized against.\n let key = nextAlias();\n let value = nextAlias();\n const from = [`json_each(r.content) AS ${key}`, `json_each(${spread(key)}) AS ${value}`];\n const where = [`${key}.key = ?`];\n params.push(segments[0]);\n\n for (const segment of segments.slice(1)) {\n key = nextAlias();\n from.push(`json_each(${members(value)}) AS ${key}`);\n value = nextAlias();\n from.push(`json_each(${spread(key)}) AS ${value}`);\n where.push(`${key}.key = ?`);\n params.push(segment);\n }\n\n where.push(leaf(value));\n return `EXISTS (SELECT 1 FROM ${from.join(', ')} WHERE ${where.join(' AND ')})`;\n};\n\n/**\n * A scalar filter value never matches an object or array stored at the\n * path: json_each exposes those as their JSON text, which would compare\n * equal to a string spelling the same document while every non-SQL adapter\n * compares the value itself.\n */\nconst equalsLeaf = (alias: string): string =>\n `${alias}.type NOT IN ('object', 'array') AND ${alias}.value = ?`;\n\n/** The join alias the content index is read through. */\nconst SORT_ALIAS = 'cs';\n\n/**\n * A cursor names a position in one ordering; carrying it into another\n * would silently resume somewhere arbitrary. The partition matters as\n * much as the field name — a cursor from a numeric page can't be read\n * against text values — so each ordering below accepts only the cursors\n * it can read, and rejecting the rest is what narrows the cursor to them.\n */\nconst cursorMismatch = (cursor: DecodedCursor, field: string): StackQueryError =>\n new StackQueryError(\n `Cursor sort field \"${cursor.field}\" does not match query sort field \"${field}\"`,\n );\n\nconst nativeCursor = (cursor: DecodedCursor, field: SortField) => {\n if (cursor.kind !== 'native' || cursor.field !== field) throw cursorMismatch(cursor, field);\n return cursor;\n};\n\nconst contentCursor = (cursor: DecodedCursor, field: string) => {\n if (cursor.kind === 'native' || cursor.field !== field) throw cursorMismatch(cursor, field);\n return cursor;\n};\n\n/**\n * Every condition on the records row itself — everything a query asks\n * except where in the ordering to resume, which is per-partition and gets\n * appended by the caller.\n */\nconst recordConditions = (query: StackQuery): { conditions: string[]; params: unknown[] } => {\n const conditions: string[] = [\"r.id != '_config'\"];\n const params: unknown[] = [];\n const f = query.filter ?? {};\n\n if (!f.includeDeleted) {\n conditions.push('r.deleted_at IS NULL');\n }\n\n if (!f.includeUnlisted) {\n conditions.push('r.unlisted_at IS NULL');\n }\n\n if (f.typeId !== undefined) {\n const ids = Array.isArray(f.typeId) ? f.typeId : [f.typeId];\n conditions.push(`r.type_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.parentId !== undefined) {\n if (f.parentId === null) {\n conditions.push('r.parent_id IS NULL');\n } else {\n conditions.push('r.parent_id = ?');\n params.push(f.parentId);\n }\n }\n\n if (f.appId !== undefined) {\n const ids = Array.isArray(f.appId) ? f.appId : [f.appId];\n conditions.push(`r.app_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.entityId !== undefined) {\n const ids = Array.isArray(f.entityId) ? f.entityId : [f.entityId];\n conditions.push(`r.entity_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.principalId !== undefined) {\n const ids = Array.isArray(f.principalId) ? f.principalId : [f.principalId];\n conditions.push(`r.principal_id IN (${ids.map(() => '?').join(',')})`);\n params.push(...ids);\n }\n\n if (f.createdAt?.after) {\n conditions.push('r.created_at > ?');\n params.push(f.createdAt.after.getTime());\n }\n if (f.createdAt?.before) {\n conditions.push('r.created_at < ?');\n params.push(f.createdAt.before.getTime());\n }\n if (f.updatedAt?.after) {\n conditions.push('r.updated_at > ?');\n params.push(f.updatedAt.after.getTime());\n }\n if (f.updatedAt?.before) {\n conditions.push('r.updated_at < ?');\n params.push(f.updatedAt.before.getTime());\n }\n\n // Every association filter below is a semi-join rather than a correlated\n // EXISTS, so the planner drives from the association side — reading the\n // matching record ids straight out of idx_assoc_kind_label /\n // idx_assoc_kind_file_id / idx_assoc_related / idx_content_index_file,\n // each of which ends with record_id and so needs no table read at all —\n // instead of scanning every record and probing for each. The work is\n // proportional to how many records match, not to how many the stack\n // holds.\n\n // Tag filter — record must have ALL specified tags\n if (f.tags?.length) {\n for (const tag of f.tags) {\n conditions.push(\n `r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'tag' AND a.label = ?)`,\n );\n params.push(tag);\n }\n }\n\n // Attachment label filter\n if (f.hasAttachment) {\n conditions.push(\n `r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'attachment' AND a.label = ?)`,\n );\n params.push(f.hasAttachment);\n }\n\n // Attachment file ID filter — find records that reference a specific file,\n // either via an attachment association or a top-level file-ref content field\n if (f.attachmentFileId) {\n conditions.push(\n `(r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'attachment' AND a.file_id = ?)\n OR r.id IN (SELECT ci.record_id FROM content_index ci WHERE ci.file_id = ?))`,\n );\n params.push(f.attachmentFileId, f.attachmentFileId);\n }\n\n // Relationship filter — each clause is an optional pattern, so a bare\n // label matches every target under it and an external target with no\n // `id` matches its whole namespace (docs/spec/data-model.md § Filter).\n //\n // A record- or entity-scoped target always stores '' in related_ns\n // (see associationKeyColumns). Saying so turns idx_assoc_related's\n // (scope, ns, id) prefix into a contiguous equality run for those\n // scopes instead of leaving the planner to seek on scope alone.\n if (f.relatedTo) {\n const clauses: string[] = [];\n const target = f.relatedTo.target;\n if (f.relatedTo.label !== undefined) {\n clauses.push('a.label = ?');\n params.push(f.relatedTo.label);\n }\n if (target) {\n clauses.push('a.related_scope = ?');\n params.push(target.scope);\n if (target.scope === 'record') {\n clauses.push('a.related_ns = ?', 'a.related_id = ?', 'a.related_stack = ?');\n params.push('', target.recordId, target.stackUrl ?? '');\n } else if (target.scope === 'entity') {\n clauses.push('a.related_ns = ?', 'a.related_id = ?');\n params.push('', target.entityId);\n } else {\n clauses.push('a.related_ns = ?');\n params.push(target.ns);\n if (target.id !== undefined) {\n clauses.push('a.related_id = ?');\n params.push(target.id);\n }\n }\n }\n conditions.push(\n `r.id IN (SELECT a.record_id FROM associations a WHERE a.kind = 'relationship'` +\n clauses.map((c) => ` AND ${c}`).join('') +\n `)`,\n );\n }\n\n // Content field filters — a dot-separated path, matched element-wise\n // through arrays. A `null` value means \"no value at the path, or a value\n // that is null\" (docs/spec/data-model.md § Filter), which is the second\n // arm below: \"some value there is null\" OR \"nothing is there at all\".\n if (f.content) {\n let aliasSeq = 0;\n const nextAlias = () => `cp${aliasSeq++}`;\n for (const [key, value] of Object.entries(f.content)) {\n const segments = parseContentFilterKey(key);\n // undefined is the same absence null names — JSON drops it on the\n // way over the wire, so treating it as a value would make an\n // in-process query and its wire equivalent disagree.\n if (value === null || value === undefined) {\n const isNull = contentPathExists(segments, (a) => `${a}.value IS NULL`, params, nextAlias);\n const anyValue = contentPathExists(\n segments,\n (a) => `${a}.value IS NOT NULL`,\n params,\n nextAlias,\n );\n conditions.push(`(${isNull} OR NOT ${anyValue})`);\n } else {\n const match = contentPathExists(segments, equalsLeaf, params, nextAlias);\n params.push(value);\n conditions.push(match);\n }\n }\n }\n\n // Presence — the question an exact-match value cannot ask. Element-wise\n // like the content filter above: a path holds a value when at least one\n // non-null value is reachable at it. See docs/spec/data-model.md\n // § Filter.\n if (f.contentPresent?.length) {\n let aliasSeq = 0;\n const nextAlias = () => `pp${aliasSeq++}`;\n for (const key of f.contentPresent) {\n conditions.push(\n contentPathExists(\n parseContentFilterKey(key),\n (a) => `${a}.value IS NOT NULL`,\n params,\n nextAlias,\n ),\n );\n }\n }\n\n if (f.search) {\n const sanitized = sanitizeFts5Query(f.search);\n if (sanitized) {\n conditions.push(`r.rowid IN (SELECT rowid FROM records_fts WHERE records_fts MATCH ?)`);\n params.push(sanitized);\n } else {\n // A search that sanitizes to nothing (e.g. \"*\", punctuation-only) is\n // an honest zero-match result, not \"no filter\" — omitting the clause\n // would silently return the full table as the \"search result\".\n conditions.push('0');\n }\n }\n\n return { conditions, params };\n};\n\n/**\n * Everything ordered after the cursor's record within the partition that\n * holds a value: a later value rank, or the same rank past the cursor's\n * value and id. `op` runs the comparison the same way the ORDER BY does,\n * so one form covers both directions — in the direction where the\n * cursor's rank is the last one, the leading disjunct is simply never\n * true.\n */\nconst presentCursorCondition = (\n cursor: Extract<DecodedCursor, { kind: 'num' | 'text' }>,\n op: '>' | '<',\n params: unknown[],\n): string => {\n if (cursor.kind === 'num') {\n params.push(cursor.value, cursor.value, cursor.id);\n return (\n `(${SORT_ALIAS}.value_rank ${op} 0 OR (${SORT_ALIAS}.value_rank = 0 AND ` +\n `(${SORT_ALIAS}.num_value ${op} ? OR (${SORT_ALIAS}.num_value = ? AND ` +\n `${SORT_ALIAS}.record_id ${op} ?))))`\n );\n }\n const key = contentSortKey(cursor.value);\n params.push(key, key, cursor.value, cursor.value, cursor.id);\n return (\n `(${SORT_ALIAS}.value_rank ${op} 1 OR (${SORT_ALIAS}.value_rank = 1 AND ` +\n `(${SORT_ALIAS}.text_key ${op} ? OR (${SORT_ALIAS}.text_key = ? AND ` +\n `(${SORT_ALIAS}.text_value ${op} ? OR (${SORT_ALIAS}.text_value = ? AND ` +\n `${SORT_ALIAS}.record_id ${op} ?))))))`\n );\n};\n\nconst statement = (\n select: string,\n from: string,\n conditions: string[],\n params: unknown[],\n tail = '',\n): QueryStatement => ({\n sql: `SELECT ${select} FROM ${from} WHERE ${conditions.join(' AND ')}${tail}`,\n params,\n});\n\n/**\n * Compile a query into the statements that answer it, read in order and\n * their rows concatenated: the first that fills the page ends the read,\n * so the second is never run for a page that doesn't reach it. Each still\n * needs the row budget its `LIMIT ?` binds — see atBudget.\n */\nexport const buildQueryPlan = (query: StackQuery): QueryStatement[] => {\n const dir = sqlDirection(query);\n const op = dir === 'ASC' ? '>' : '<';\n const decoded = query.cursor ? decodeCursor(query.cursor) : null;\n const contentField = query.sort?.contentField;\n\n if (contentField === undefined) {\n const { conditions, params } = recordConditions(query);\n const col = getSortColumn(getSortField(query));\n if (decoded) {\n const cursor = nativeCursor(decoded, getSortField(query));\n conditions.push(`(r.${col} ${op} ? OR (r.${col} = ? AND r.id ${op} ?))`);\n params.push(cursor.value, cursor.value, cursor.id);\n }\n return [\n statement(\n 'r.*',\n 'records r',\n conditions,\n params,\n ` ORDER BY r.${col} ${dir}, r.id ${dir} LIMIT ?`,\n ),\n ];\n }\n\n const cursor = decoded ? contentCursor(decoded, contentField) : null;\n const pages: QueryStatement[] = [];\n\n // Records holding a value at the field, in that value's order. A cursor\n // sitting in the absent partition has already passed every one of them.\n if (cursor?.kind !== 'absent') {\n const { conditions, params } = recordConditions(query);\n const from = `content_index ${SORT_ALIAS} JOIN records r ON r.id = ${SORT_ALIAS}.record_id`;\n conditions.push(`${SORT_ALIAS}.field = ?`);\n params.push(contentField);\n if (cursor) conditions.push(presentCursorCondition(cursor, op, params));\n // Ordered exactly as idx_content_index_sort stores it, so the page is\n // an ordered walk of the index rather than a sort of the matches.\n const order =\n `${SORT_ALIAS}.value_rank ${dir}, ${SORT_ALIAS}.num_value ${dir}, ` +\n `${SORT_ALIAS}.text_key ${dir}, ${SORT_ALIAS}.text_value ${dir}, ` +\n `${SORT_ALIAS}.record_id ${dir}`;\n pages.push(statement('r.*', from, conditions, params, ` ORDER BY ${order} LIMIT ?`));\n }\n\n // Records holding nothing there, which trail the others whichever way\n // the sort runs (docs/spec/data-model.md § Sorting by a content field).\n {\n const { conditions, params } = recordConditions(query);\n conditions.push(\n `NOT EXISTS (SELECT 1 FROM content_index ${SORT_ALIAS} ` +\n `WHERE ${SORT_ALIAS}.record_id = r.id AND ${SORT_ALIAS}.field = ?)`,\n );\n params.push(contentField);\n if (cursor?.kind === 'absent') {\n conditions.push(`r.id ${op} ?`);\n params.push(cursor.id);\n }\n pages.push(statement('r.*', 'records r', conditions, params, ` ORDER BY r.id ${dir} LIMIT ?`));\n }\n\n return pages;\n};\n","/**\n * Row <-> domain object mappers shared by every SQLite-backed record\n * adapter. Column names and JSON-encoding choices are the storage\n * contract; keeping one copy means the adapters can't drift on them.\n */\n\nimport type {\n StackRecord,\n StackType,\n RecordVersion,\n Association,\n RelationshipTarget,\n} from '@haverstack/core';\n\nexport const toMs = (d: Date): number => d.getTime();\nexport const fromMs = (ms: number): Date => new Date(ms);\n\nexport const rowToRecord = (\n row: Record<string, unknown>,\n associations: Association[],\n): StackRecord => {\n const record: StackRecord = {\n id: row.id as string,\n typeId: row.type_id as string,\n createdAt: fromMs(row.created_at as number),\n updatedAt: fromMs(row.updated_at as number),\n content: JSON.parse(row.content as string),\n version: row.version as number,\n };\n // Presence, never truthiness: SQL NULL is the only spelling of an absent\n // field, so the empty string and the epoch are values like any other.\n // The query predicates beside this read `IS NULL`, and a mapper that\n // disagreed would let a record answer one way to get() and another to\n // the filter that should have found it.\n if (row.parent_id != null) record.parentId = row.parent_id as string;\n if (row.entity_id != null) record.entityId = row.entity_id as string;\n if (row.app_id != null) record.appId = row.app_id as string;\n if (row.principal_id != null) record.principalId = row.principal_id as string;\n if (row.updated_by != null) record.updatedBy = row.updated_by as string;\n if (row.updated_via != null) record.updatedVia = row.updated_via as string;\n if (row.deleted_at != null) record.deletedAt = fromMs(row.deleted_at as number);\n if (row.unlisted_at != null) record.unlistedAt = fromMs(row.unlisted_at as number);\n if (row.permissions != null) record.permissions = JSON.parse(row.permissions as string);\n if (associations.length) record.associations = associations;\n return record;\n};\n\nexport const rowToAssociation = (row: Record<string, unknown>): Association => {\n if (row.kind === 'tag') {\n return { kind: 'tag', label: row.label as string };\n }\n if (row.kind === 'attachment') {\n return {\n kind: 'attachment',\n label: row.label as string,\n fileId: row.file_id as string,\n };\n }\n // relationship\n return {\n kind: 'relationship',\n label: row.label as string,\n target: rowToTarget(row),\n };\n};\n\n/**\n * The five columns that identify an association, in the order every\n * INSERT and DELETE below binds them. One helper because the two must\n * agree exactly — a dissociate that bound them differently would delete\n * nothing and report success.\n */\nexport const associationKeyColumns = (a: Association): [string, string, string, string, string] => {\n if (a.kind === 'attachment') return [a.fileId, '', '', '', ''];\n if (a.kind !== 'relationship') return ['', '', '', '', ''];\n const t = a.target;\n if (t.scope === 'entity') return ['', 'entity', t.entityId, '', ''];\n if (t.scope === 'external') return ['', 'external', t.id, t.ns, ''];\n return ['', 'record', t.recordId, '', t.stackUrl ?? ''];\n};\n\nconst rowToTarget = (row: Record<string, unknown>): RelationshipTarget => {\n const id = row.related_id as string;\n if (row.related_scope === 'entity') return { scope: 'entity', entityId: id };\n if (row.related_scope === 'external') {\n return { scope: 'external', ns: row.related_ns as string, id };\n }\n const stackUrl = row.related_stack as string;\n return { scope: 'record', recordId: id, ...(stackUrl && { stackUrl }) };\n};\n\nexport const rowToType = (row: Record<string, unknown>): StackType => {\n const t: StackType = {\n id: row.id as string,\n baseId: row.base_id as string,\n version: row.version as number,\n name: row.name as string,\n schema: JSON.parse(row.schema as string),\n schemaHash: row.schema_hash as string,\n createdAt: fromMs(row.created_at as number),\n };\n if (row.migrates_from) t.migratesFrom = row.migrates_from as string;\n return t;\n};\n\nexport const rowToVersion = (row: Record<string, unknown>): RecordVersion => {\n const v: RecordVersion = {\n version: row.version as number,\n typeId: row.type_id as string,\n content: JSON.parse(row.content as string),\n updatedAt: fromMs(row.updated_at as number),\n };\n if (row.entity_id != null) v.entityId = row.entity_id as string;\n if (row.updated_by != null) v.updatedBy = row.updated_by as string;\n if (row.updated_via != null) v.updatedVia = row.updated_via as string;\n // Read for presence, not truthiness: the stored 'null' is the root, and\n // only a SQL NULL is a snapshot that claimed no container at all.\n if (row.parent_id != null) v.parentId = row.parent_id as string;\n if (row.associations != null) v.associations = JSON.parse(row.associations as string);\n if (row.permissions != null) v.permissions = JSON.parse(row.permissions as string);\n return v;\n};\n","/**\n * Storage-ownership lock file. A stack file is owned by exactly one\n * process at a time (see docs/spec/adapters.md § Concurrency & storage ownership).\n * The lock file sits beside the database and records the PID of its\n * opener — pure Node fs/process logic, no SQLite engine involved, so\n * every Node-backed record adapter shares one implementation and one\n * error message.\n */\n\nimport { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';\n\ntype LockInfo = { pid: number };\n\nconst lockPathFor = (dbPath: string): string => `${dbPath}.lock`;\n\nconst isProcessAlive = (pid: number): boolean => {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // ESRCH: no such process — safe to reclaim. Anything else (e.g. EPERM,\n // meaning the process exists but we can't signal it) — assume alive.\n return (err as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n};\n\n/**\n * Acquire the lock, throwing a clear error if another live process\n * already holds it. Reclaims stale locks (owning process no longer\n * running) automatically. `force` bypasses the check entirely, for the\n * rare case of PID reuse across a reboot.\n */\nexport const acquireLock = (dbPath: string, force?: boolean): void => {\n const lockPath = lockPathFor(dbPath);\n const info = existsSync(lockPath) ? readLock(lockPath) : null;\n if (info) {\n const ownedBySelf = info.pid === process.pid;\n if (!ownedBySelf && !force && isProcessAlive(info.pid)) {\n throw new Error(\n `Stack database at \"${dbPath}\" is in use by another process (pid ${info.pid}). ` +\n `Connect via its server instead, or pass { force: true } to override. ` +\n `If no such process is running, remove \"${lockPath}\".`,\n );\n }\n }\n writeFileSync(lockPath, JSON.stringify({ pid: process.pid } satisfies LockInfo));\n};\n\n/**\n * Read the lock, treating an unreadable one as absent. A lock file is\n * written non-atomically, so a crash mid-write leaves a torn one — and a\n * lock naming no live process is exactly what acquireLock() reclaims. The\n * alternative is a database that cannot be opened again by any means the\n * error message mentions, which is the worse failure: the lock exists to\n * prevent a second writer, not to outrank the data it guards.\n * releaseLock() takes the same view.\n */\nconst readLock = (lockPath: string): LockInfo | null => {\n try {\n const info = JSON.parse(readFileSync(lockPath, 'utf-8')) as LockInfo;\n return typeof info?.pid === 'number' ? info : null;\n } catch {\n return null;\n }\n};\n\n/** Release the lock, if it's still owned by this process. */\nexport const releaseLock = (dbPath: string): void => {\n const lockPath = lockPathFor(dbPath);\n if (!existsSync(lockPath)) return;\n try {\n const info = JSON.parse(readFileSync(lockPath, 'utf-8')) as LockInfo;\n if (info.pid === process.pid) unlinkSync(lockPath);\n } catch {\n // Corrupt lock file — remove it rather than leaving storage permanently unopenable.\n unlinkSync(lockPath);\n }\n};\n","/**\n * Normalizes a SQLite binding's call convention (array-bind-then-step,\n * spread-args run/get/all, cursor-returning exec) behind one interface,\n * so the actual CRUD/query/version/type/association/token logic can live\n * once in this package instead of being re-typed per engine. Each adapter\n * package provides a thin SqlExecutor implementation over its own\n * database handle.\n *\n * Deliberately synchronous: every binding this targets executes queries\n * in-process without yielding, and an async interface would force the\n * shared logic's transaction sequences to interleave.\n */\nexport interface SqlExecutor {\n /** Run DDL, pragmas, or any statement(s) with no bound params and no results needed. */\n exec(sql: string): void;\n /** Run a single parameterized statement. Returns the number of rows affected (INSERT/UPDATE/DELETE). */\n run(sql: string, params?: readonly unknown[]): number;\n /** Run a parameterized statement and return the first result row, or undefined. */\n get<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T | undefined;\n /** Run a parameterized statement and return all result rows. */\n all<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T[];\n /**\n * Run `fn` atomically: every statement it issues commits together, or\n * none do if it throws. `fn` must be synchronous and call back into\n * this executor only — not every engine's transaction primitive can\n * straddle a suspended call. A binding with a real multi-statement\n * transaction (BEGIN/COMMIT/ROLLBACK) implements this with one; a\n * binding whose transaction primitive is itself a callback wrapper\n * (e.g. a Durable Object's storage.transactionSync) can pass `fn`\n * straight through to it.\n */\n transaction<T>(fn: () => T): T;\n}\n\n/** SQLite reports FK violations with this exact message; verify it holds when adding an engine. */\nexport const isForeignKeyViolation = (err: unknown): boolean =>\n err instanceof Error && err.message.includes('FOREIGN KEY constraint failed');\n\n/** SQLite reports a PRIMARY KEY/UNIQUE collision with this exact message. */\nexport const isUniqueConstraintViolation = (err: unknown): boolean =>\n err instanceof Error && err.message.includes('UNIQUE constraint failed');\n","import type { SqlExecutor } from './executor.js';\n\n/** timezone is optional passthrough app metadata — no default. */\nexport type StackConfig = { entityId: string; timezone: string | undefined };\n\n/** Inserts the singleton _config@1 record. Only valid on a freshly-created database. */\nexport const insertConfigRecord = (\n exec: SqlExecutor,\n entityId: string,\n timezone: string | undefined,\n): void => {\n const now = Date.now();\n exec.run(\n `INSERT INTO records (id, type_id, created_at, updated_at, content, version)\n VALUES ('_config', '_config@1', ?, ?, ?, 1)`,\n [now, now, JSON.stringify({ entityId, timezone })],\n );\n};\n\n/** Reads the singleton _config@1 record. Throws if the database is missing it. */\nexport const readStackConfig = (exec: SqlExecutor): StackConfig => {\n const row = exec.get<{ content: string }>(`SELECT content FROM records WHERE id = '_config'`);\n if (!row) throw new Error('Stack database is missing its config record.');\n const content = JSON.parse(row.content) as { entityId: string; timezone?: string };\n return { entityId: content.entityId, timezone: content.timezone };\n};\n","/**\n * The actual StackRecordAdapter logic (minus lifecycle: initialize/open/\n * close/flush stay per-engine, since schema setup, pragmas, locking, and\n * durability differ there). Every CRUD/query/version/type/association\n * operation lives here exactly once, parametrized over a SqlExecutor\n * (which normalizes each engine's call conventions). Concrete adapters\n * construct one of these and delegate their StackRecordAdapter methods\n * to it — see record-adapter-sqlite.\n */\n\nimport {\n StackConflictError,\n StackVersionConflictError,\n StackNotFoundError,\n StackQueryError,\n applyMergePatch,\n} from '@haverstack/core';\nimport type {\n StackRecord,\n StackType,\n TypeId,\n FileId,\n RecordVersion,\n RecordChanges,\n StackQuery,\n QueryResult,\n Association,\n ActorOptions,\n ExpectedVersionOptions,\n} from '@haverstack/core';\nimport type { SqlExecutor } from './executor.js';\nimport { isForeignKeyViolation, isUniqueConstraintViolation } from './executor.js';\nimport { buildQueryPlan, atBudget } from './query.js';\nimport { fts5Strategy } from './fts5.js';\nimport {\n rowToRecord,\n rowToAssociation,\n rowToType,\n rowToVersion,\n toMs,\n associationKeyColumns,\n} from './mappers.js';\nimport { makeCursor } from './cursor.js';\nimport { contentSortEntry } from '@haverstack/core/adapter';\nimport type { ScalarFieldKind } from '@haverstack/core';\n\n/**\n * Run a query statement, restating an engine parse error as the wire\n * taxonomy's `bad_request` rather than letting it escape as a raw engine\n * error (which a server has no code to map, so it becomes a 500).\n *\n * Scoped to the one filter that carries a query language: `filter.search`\n * is FTS5 source text, and sanitizeFts5Query() repairs the common shapes\n * but claims no completeness against the grammar. Every other clause here\n * is built from parameters, so a parse error in one is this module's bug —\n * rethrown untouched, where it belongs.\n */\nconst asStackQueryError = <T>(query: StackQuery, run: () => T): T => {\n try {\n return run();\n } catch (err) {\n if (!query.filter?.search) throw err;\n throw new StackQueryError(\n `Search text could not be parsed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n};\n\nexport type SharedSqlRecordLogicDeps = {\n exec: SqlExecutor;\n};\n\n/**\n * Top-level scalar fields, by name and declared kind — what content_index\n * covers. Arrays and objects are left out: a value inside one has no\n * single position to order its record by, and no file reference the\n * attachment filter can see.\n */\nconst indexedFieldKinds = (\n schema: Record<string, { kind?: string }>,\n): Map<string, ScalarFieldKind> => {\n const kinds = new Map<string, ScalarFieldKind>();\n for (const [field, def] of Object.entries(schema)) {\n if (!def.kind || def.kind === 'array' || def.kind === 'object') continue;\n kinds.set(field, def.kind as ScalarFieldKind);\n }\n return kinds;\n};\n\nexport class SharedSqlRecordLogic {\n /**\n * Per-typeId cache of the fields content_index covers and their declared\n * kinds, so syncContentIndex() doesn't hit the `types` table and\n * re-parse schema JSON on every write. Populated eagerly in saveType()\n * and lazily via getIndexedFields().\n */\n private readonly indexedFieldsByType = new Map<string, Map<string, ScalarFieldKind>>();\n\n constructor(private readonly deps: SharedSqlRecordLogicDeps) {}\n\n private get exec(): SqlExecutor {\n return this.deps.exec;\n }\n\n /**\n * SQL fragment (plus its bind params) that gates a records-table\n * UPDATE/DELETE on the opt-in `expectedVersion` precondition. Empty when\n * expectedVersion is omitted, keeping today's unconditional behavior.\n */\n private versionGuard(expectedVersion: number | undefined): { clause: string; params: unknown[] } {\n return expectedVersion === undefined\n ? { clause: '', params: [] }\n : { clause: ' AND version = ?', params: [expectedVersion] };\n }\n\n /**\n * Precondition check for mutations that can't fold expectedVersion into\n * their primary UPDATE's WHERE clause: mutateRecord/restoreVersion need\n * fts.remove() to run *before* the records-table content changes, so\n * the check happens first, standalone.\n */\n private checkExpectedVersion(record: StackRecord, expectedVersion: number | undefined): void {\n if (expectedVersion === undefined || record.version === expectedVersion) return;\n throw new StackVersionConflictError(\n `Record \"${record.id}\" is at version ${record.version}, expected ${expectedVersion}`,\n record.id,\n expectedVersion,\n record.version,\n );\n }\n\n /**\n * Called after a versionGuard()-gated statement affected zero rows.\n * Distinguishes \"record doesn't exist\" (StackNotFoundError) from \"it\n * exists but isn't at expectedVersion\" (StackVersionConflictError, with\n * the actual current version for the caller to act on).\n */\n private throwVersionConflict(id: string, expectedVersion: number | undefined): never {\n const row = this.exec.get<{ version: number }>('SELECT version FROM records WHERE id = ?', [\n id,\n ]);\n if (!row) throw new StackNotFoundError(`Record not found: \"${id}\"`);\n throw new StackVersionConflictError(\n `Record \"${id}\" is at version ${row.version}, expected ${expectedVersion}`,\n id,\n expectedVersion as number,\n row.version,\n );\n }\n\n // -------------------------------------------------------\n // Records\n // -------------------------------------------------------\n\n /**\n * A duplicate id hits the `records` PK — mapped to StackConflictError\n * instead of surfacing the raw engine exception, mirroring saveVersion's\n * collision mapping below.\n */\n async createRecord(record: StackRecord): Promise<StackRecord> {\n try {\n this.exec.run(\n `INSERT INTO records\n (id, type_id, created_at, updated_at, content, version,\n parent_id, entity_id, app_id, principal_id, updated_by, updated_via,\n deleted_at, unlisted_at, permissions)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n record.id,\n record.typeId,\n toMs(record.createdAt),\n toMs(record.updatedAt),\n JSON.stringify(record.content),\n record.version,\n record.parentId ?? null,\n record.entityId ?? null,\n record.appId ?? null,\n record.principalId ?? null,\n record.updatedBy ?? null,\n record.updatedVia ?? null,\n record.deletedAt ? toMs(record.deletedAt) : null,\n record.unlistedAt ? toMs(record.unlistedAt) : null,\n record.permissions ? JSON.stringify(record.permissions) : null,\n ],\n );\n } catch (err) {\n if (isUniqueConstraintViolation(err)) {\n throw new StackConflictError(`Record already exists: \"${record.id}\"`);\n }\n throw err;\n }\n\n if (record.associations?.length) {\n this.insertAssociations(record.id, record.associations);\n }\n\n fts5Strategy.insert(this.exec, record.id, JSON.stringify(record.content));\n this.syncContentIndex(record.id, record.typeId, record.content);\n return record;\n }\n\n async getRecord(id: string): Promise<StackRecord | null> {\n return this.readRecord(id);\n }\n\n /**\n * The synchronous read behind getRecord(). Callers inside exec.transaction()\n * use this one: an `await` inside that callback yields the microtask queue\n * mid-transaction, and the next operation to run would try to open one of\n * its own — transaction() requires a synchronous callback for exactly this\n * reason (see SqlExecutor.transaction).\n *\n * The same synchrony is what lets the post-commit reads below report the\n * version their own mutation produced: `getRecord()` runs its body before\n * the `await` yields, so nothing interleaves between the commit and the\n * read. A backend that made these reads genuinely asynchronous would open\n * that window, and a concurrent mutation could be reported under this\n * one's verb.\n */\n private readRecord(id: string): StackRecord | null {\n const row = this.exec.get<Record<string, unknown>>('SELECT * FROM records WHERE id = ?', [id]);\n if (!row) return null;\n const associations = this.getAssociationsForRecord(id);\n return rowToRecord(row, associations);\n }\n\n /**\n * Apply a change set in one transaction: one UPDATE over the record row\n * with only the columns the change set names, the association set\n * replaced when it carries one, and the snapshot written alongside — so\n * one call is one version whatever it moved.\n *\n * The expectedVersion check is standalone rather than folded into the\n * UPDATE alone, because a content change needs fts.remove() to run\n * *before* the records-table content changes. The guard is still in the\n * UPDATE's WHERE, so a writer that slipped in between the read and the\n * write is caught there rather than overwriting.\n */\n async mutateRecord(\n id: string,\n changes: RecordChanges,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n const existing = await this.getRecord(id);\n if (!existing) throw new StackNotFoundError(`Record not found: \"${id}\"`);\n this.checkExpectedVersion(existing, opts.expectedVersion);\n\n const merged = changes.contentPatch\n ? applyMergePatch(existing.content, changes.contentPatch)\n : undefined;\n\n // Assembled rather than written out per aspect: every one of these is\n // the same column-and-value pair on one row, and a fixed statement per\n // combination would be 2^5 of them.\n const sets: string[] = [];\n const values: unknown[] = [];\n if (merged !== undefined) {\n sets.push('content = ?');\n values.push(JSON.stringify(merged));\n }\n if (changes.parentId !== undefined) {\n sets.push('parent_id = ?');\n values.push(changes.parentId);\n }\n if (changes.permissions !== undefined) {\n sets.push('permissions = ?');\n values.push(changes.permissions.length ? JSON.stringify(changes.permissions) : null);\n }\n\n const now = toMs(new Date());\n if (changes.unlisted !== undefined) {\n sets.push('unlisted_at = ?');\n // Stamped fresh because this key is only ever present on a\n // transition — Stack narrows a change set to the aspects that\n // actually moved before it reaches an adapter.\n values.push(changes.unlisted ? now : null);\n }\n\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n if (merged !== undefined) fts5Strategy.remove(this.exec, id);\n\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET ${sets.concat(['version = version + 1', 'updated_at = ?', 'updated_by = ?', 'updated_via = ?']).join(', ')} WHERE id = ?${clause}`,\n [...values, now, opts.updatedBy ?? null, opts.updatedVia ?? null, id, ...verParams],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n\n if (changes.associations !== undefined) {\n this.exec.run('DELETE FROM associations WHERE record_id = ?', [id]);\n if (changes.associations.length) this.insertAssociations(id, changes.associations);\n }\n\n if (merged !== undefined) {\n fts5Strategy.insert(this.exec, id, JSON.stringify(merged));\n this.syncContentIndex(id, existing.typeId, merged);\n }\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after mutateRecord: \"${id}\"`);\n return updated;\n }\n\n async deleteRecord(\n id: string,\n opts: {\n hard?: boolean;\n expectedVersion?: number;\n snapshot?: RecordVersion;\n } & ActorOptions = {},\n ): Promise<StackRecord | null> {\n if (opts.hard) {\n return this.exec.transaction(() => this.hardDeleteRecord(id, opts.expectedVersion));\n } else {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET deleted_at = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [\n toMs(new Date()),\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ...verParams,\n ],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n });\n }\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after deleteRecord: \"${id}\"`);\n return updated;\n }\n\n /**\n * Deletes a record's FTS entry, associations, versions, file-refs, sort\n * index rows, and row, returning the record as it stood — the last copy that will ever\n * exist, read here so that nothing can overtake it between the read and\n * the deletes. Null when there was no such record. No write() call —\n * callers batch it. expectedVersion is checked first so a lost CAS race\n * leaves nothing touched (children reference the row, so the check can't\n * fold into the final DELETE).\n */\n private hardDeleteRecord(id: string, expectedVersion?: number): StackRecord | null {\n const purged = this.readRecord(id);\n if (expectedVersion !== undefined) {\n if (!purged) throw new StackNotFoundError(`Record not found: \"${id}\"`);\n if (purged.version !== expectedVersion) {\n throw new StackVersionConflictError(\n `Record \"${id}\" is at version ${purged.version}, expected ${expectedVersion}`,\n id,\n expectedVersion,\n purged.version,\n );\n }\n }\n if (!purged) return null;\n fts5Strategy.remove(this.exec, id);\n this.exec.run('DELETE FROM associations WHERE record_id = ?', [id]);\n this.exec.run('DELETE FROM versions WHERE record_id = ?', [id]);\n this.exec.run('DELETE FROM content_index WHERE record_id = ?', [id]);\n this.exec.run('DELETE FROM records WHERE id = ?', [id]);\n return purged;\n }\n\n async undeleteRecord(\n id: string,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET deleted_at = NULL, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [toMs(new Date()), opts.updatedBy ?? null, opts.updatedVia ?? null, id, ...verParams],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after undelete: \"${id}\"`);\n return updated;\n }\n\n async restoreVersion(\n id: string,\n version: number,\n opts: {\n expectedVersion?: number;\n snapshot?: RecordVersion;\n restoreAssociations?: boolean;\n } & ActorOptions = {},\n ): Promise<StackRecord> {\n const existing = await this.getRecord(id);\n if (!existing) throw new Error(`Record not found: \"${id}\"`);\n this.checkExpectedVersion(existing, opts.expectedVersion);\n\n const target = await this.getVersion(id, version);\n if (!target) throw new Error(`Version not found: ${id}@${version}`);\n\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n fts5Strategy.remove(this.exec, id);\n this.exec.run(\n `UPDATE records SET type_id = ?, content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ?, parent_id = ? WHERE id = ?`,\n [\n target.typeId,\n JSON.stringify(target.content),\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n target.parentId ?? null,\n id,\n ],\n );\n // Whether a restore rolls associations back at all is decided where\n // the record's meaning is known; the snapshot's list is the only one\n // that ever lands here. See StackRecordAdapter.restoreVersion().\n const applied = opts.restoreAssociations === false ? undefined : target.associations;\n if (applied !== undefined) {\n this.exec.run('DELETE FROM associations WHERE record_id = ?', [id]);\n if (applied.length) this.insertAssociations(id, applied);\n }\n fts5Strategy.insert(this.exec, id, JSON.stringify(target.content));\n this.syncContentIndex(id, target.typeId, target.content);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after restoreVersion: \"${id}\"`);\n return updated;\n }\n\n async commitMigration(\n id: string,\n toTypeId: TypeId,\n content: Record<string, unknown>,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n // Checked here rather than folded into the UPDATE's WHERE clause:\n // fts5Strategy.remove() has to run before the content changes, so the\n // precondition has to settle first — same shape as patchContent() and\n // restoreVersion().\n const existing = await this.getRecord(id);\n if (!existing) throw new Error(`Record not found: \"${id}\"`);\n this.checkExpectedVersion(existing, opts.expectedVersion);\n\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(id, opts.snapshot);\n fts5Strategy.remove(this.exec, id);\n this.exec.run(\n `UPDATE records SET type_id = ?, content = ?, version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?`,\n [\n toTypeId,\n JSON.stringify(content),\n toMs(new Date()),\n opts.updatedBy ?? null,\n opts.updatedVia ?? null,\n id,\n ],\n );\n fts5Strategy.insert(this.exec, id, JSON.stringify(content));\n this.syncContentIndex(id, toTypeId, content);\n });\n\n const updated = await this.getRecord(id);\n if (!updated) throw new Error(`Record not found after commitMigration: \"${id}\"`);\n return updated;\n }\n\n async queryRecords(query: StackQuery): Promise<QueryResult> {\n const limit = query.limit ?? 50;\n // One extra row, to learn whether a further page follows.\n const fetch = limit + 1;\n\n // A content-field sort answers from two statements, the records that\n // hold a value at the field and then the records that hold none. They\n // read in order, each asking only for the rows the ones before it left\n // of the page, and the second is skipped once the first has filled it —\n // the common case, since a page rarely straddles the boundary.\n const rows: Record<string, unknown>[] = [];\n for (const page of buildQueryPlan(query)) {\n const budget = fetch - rows.length;\n if (budget <= 0) break;\n const statement = atBudget(page, budget);\n rows.push(\n ...asStackQueryError(query, () =>\n this.exec.all<Record<string, unknown>>(statement.sql, statement.params),\n ),\n );\n }\n\n const hasMore = rows.length > limit;\n const page = hasMore ? rows.slice(0, limit) : rows;\n\n const records = page.map((row) => {\n const associations = this.getAssociationsForRecord(row.id as string);\n return rowToRecord(row, associations);\n });\n\n const lastRecord = records[records.length - 1];\n const cursor =\n hasMore && lastRecord\n ? makeCursor(lastRecord, query.sort, (record, field) => this.sortFieldKind(record, field))\n : null;\n\n return { records, cursor };\n }\n\n /**\n * Atomically verify fileId is unreferenced, then hard-delete its\n * metadata records across every typeId in `metadataTypeIds` (see\n * deleteUnreferencedAttachmentRecords on the adapter contract). A real\n * SQL transaction of synchronous calls — no `await` between the check\n * and the deletes, so nothing interleaves.\n */\n async deleteUnreferencedAttachmentRecords(\n fileId: FileId,\n metadataTypeIds: TypeId[],\n ): Promise<StackRecord[]> {\n return this.exec.transaction(() => {\n const referenced = this.exec.all<{ found: number }>(\n `SELECT 1 as found FROM associations WHERE kind = 'attachment' AND file_id = ?\n UNION ALL\n SELECT 1 FROM content_index WHERE file_id = ?\n LIMIT 1`,\n [fileId, fileId],\n );\n if (referenced.length) {\n throw new StackConflictError('Attachment is still referenced by one or more records');\n }\n\n // An empty family still owes the caller the reference check above —\n // the conflict is a fact about the file, not about which metadata\n // types happen to be defined.\n if (metadataTypeIds.length === 0) return [];\n\n const placeholders = metadataTypeIds.map(() => '?').join(', ');\n const metaRows = this.exec.all<{ id: string }>(\n `SELECT id FROM records\n WHERE type_id IN (${placeholders}) AND json_extract(content, '$.fileId') = ?`,\n [...metadataTypeIds, fileId],\n );\n const deleted: StackRecord[] = [];\n for (const row of metaRows) {\n const purged = this.hardDeleteRecord(row.id);\n if (purged) deleted.push(purged);\n }\n\n return deleted;\n });\n }\n\n // -------------------------------------------------------\n // Versions\n // -------------------------------------------------------\n\n async getVersions(id: string): Promise<RecordVersion[]> {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM versions WHERE record_id = ? ORDER BY version DESC',\n [id],\n );\n return rows.map(rowToVersion);\n }\n\n async getVersion(id: string, version: number): Promise<RecordVersion | null> {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM versions WHERE record_id = ? AND version = ?',\n [id, version],\n );\n return rows.length ? rowToVersion(rows[0]) : null;\n }\n\n /**\n * A (record_id, version) collision is rejected loudly via the UNIQUE\n * constraint, mapped to StackConflictError — never a silently discarded\n * snapshot leaving a hole in rollback history. See\n * docs/spec/versioning.md § Optimistic concurrency (`ifVersion`).\n */\n private insertVersionRow(id: string, version: RecordVersion): void {\n try {\n this.exec.run(\n `INSERT INTO versions\n (record_id, version, type_id, content, updated_at, entity_id,\n updated_by, updated_via, parent_id, associations, permissions)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n id,\n version.version,\n version.typeId,\n JSON.stringify(version.content),\n toMs(version.updatedAt),\n version.entityId ?? null,\n version.updatedBy ?? null,\n version.updatedVia ?? null,\n version.parentId ?? null,\n version.associations ? JSON.stringify(version.associations) : null,\n version.permissions ? JSON.stringify(version.permissions) : null,\n ],\n );\n } catch (err) {\n if (isUniqueConstraintViolation(err)) {\n throw new StackConflictError(\n `Version ${version.version} already exists for record \"${id}\" — a concurrent ` +\n `writer raced past this version. Use ifVersion to detect this before it happens.`,\n );\n }\n throw err;\n }\n }\n\n /**\n * Replaces every column insertVersionRow writes, so an overwritten row is\n * indistinguishable from a freshly inserted one — a column left out here\n * would keep the replaced row's value and surface later as a restore\n * putting back something the snapshot never said.\n */\n private overwriteVersionRow(id: string, version: RecordVersion): void {\n this.exec.run(\n `UPDATE versions\n SET type_id = ?, content = ?, updated_at = ?, entity_id = ?,\n updated_by = ?, updated_via = ?, parent_id = ?, associations = ?, permissions = ?\n WHERE record_id = ? AND version = ?`,\n [\n version.typeId,\n JSON.stringify(version.content),\n toMs(version.updatedAt),\n version.entityId ?? null,\n version.updatedBy ?? null,\n version.updatedVia ?? null,\n version.parentId ?? null,\n version.associations ? JSON.stringify(version.associations) : null,\n version.permissions ? JSON.stringify(version.permissions) : null,\n id,\n version.version,\n ],\n );\n }\n\n /**\n * Standalone snapshot write for tooling and tests — loud on any\n * collision, since nothing here is about to bump the version. Mutating\n * methods take a `snapshot` option instead; see snapshotBeforeMutation.\n */\n async saveVersion(id: string, version: RecordVersion): Promise<void> {\n this.insertVersionRow(id, version);\n }\n\n /**\n * The snapshot half of a mutating method's atomic snapshot-then-mutate\n * transaction. A collision is a racing-writer conflict, except an orphan\n * row at the record's *current* version — recognized by version number\n * alone, never content — which is overwritten so the interrupted write\n * can complete. See docs/spec/versioning.md § Snapshot atomicity.\n */\n private snapshotBeforeMutation(id: string, version: RecordVersion): void {\n try {\n this.insertVersionRow(id, version);\n } catch (err) {\n if (!(err instanceof StackConflictError)) throw err;\n\n const row = this.exec.get<{ version: number }>('SELECT version FROM records WHERE id = ?', [\n id,\n ]);\n if (!row || row.version !== version.version) throw err;\n\n this.overwriteVersionRow(id, version);\n }\n }\n\n // -------------------------------------------------------\n // Types\n // -------------------------------------------------------\n\n async saveType(type: StackType): Promise<void> {\n this.exec.run(\n `INSERT OR REPLACE INTO types\n (id, base_id, version, name, schema, schema_hash, migrates_from, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n type.id,\n type.baseId,\n type.version,\n type.name,\n JSON.stringify(type.schema),\n type.schemaHash,\n type.migratesFrom ?? null,\n toMs(type.createdAt),\n ],\n );\n const schema = type.schema as Record<string, { kind?: string }>;\n this.indexedFieldsByType.set(type.id, indexedFieldKinds(schema));\n }\n\n async getType(id: TypeId): Promise<StackType | null> {\n const rows = this.exec.all<Record<string, unknown>>('SELECT * FROM types WHERE id = ?', [id]);\n return rows.length ? rowToType(rows[0]) : null;\n }\n\n async listTypes(): Promise<StackType[]> {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM types ORDER BY base_id, version',\n );\n return rows.map(rowToType);\n }\n\n // -------------------------------------------------------\n // Associations\n // -------------------------------------------------------\n\n async associate(\n recordId: string,\n association: Association,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(recordId, opts.snapshot);\n // Bump (and CAS-check) first, before the associations-table write, so\n // a lost race never partially applies.\n this.bumpVersion(recordId, opts);\n this.insertAssociations(recordId, [association]);\n });\n\n const updated = await this.getRecord(recordId);\n if (!updated) throw new Error(`Record not found after associate: \"${recordId}\"`);\n return updated;\n }\n\n async dissociate(\n recordId: string,\n association: Association,\n opts: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions = {},\n ): Promise<StackRecord> {\n this.exec.transaction(() => {\n if (opts.snapshot) this.snapshotBeforeMutation(recordId, opts.snapshot);\n this.bumpVersion(recordId, opts);\n this.exec.run(\n `DELETE FROM associations\n WHERE record_id = ?\n AND kind = ?\n AND label = ?\n AND file_id = ?\n AND related_scope = ?\n AND related_id = ?\n AND related_ns = ?\n AND related_stack = ?`,\n [recordId, association.kind, association.label, ...associationKeyColumns(association)],\n );\n });\n\n const updated = await this.getRecord(recordId);\n if (!updated) throw new Error(`Record not found after dissociate: \"${recordId}\"`);\n return updated;\n }\n\n private bumpVersion(id: string, opts: ExpectedVersionOptions & ActorOptions = {}): void {\n const { clause, params: verParams } = this.versionGuard(opts.expectedVersion);\n const changed = this.exec.run(\n `UPDATE records SET version = version + 1, updated_at = ?, updated_by = ?, updated_via = ? WHERE id = ?${clause}`,\n [toMs(new Date()), opts.updatedBy ?? null, opts.updatedVia ?? null, id, ...verParams],\n );\n if (changed === 0) this.throwVersionConflict(id, opts.expectedVersion);\n }\n\n /**\n * FK enforcement (PRAGMA_FOREIGN_KEYS_ON) means inserting an\n * association against a record that doesn't exist throws — mapped\n * here to StackNotFoundError so associate() on a nonexistent record\n * fails loudly instead of silently creating an orphan row.\n */\n private insertAssociations(recordId: string, associations: Association[]): void {\n for (const assoc of associations) {\n try {\n this.exec.run(\n `INSERT OR IGNORE INTO associations\n (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n [recordId, assoc.kind, assoc.label, ...associationKeyColumns(assoc)],\n );\n } catch (err) {\n if (isForeignKeyViolation(err)) {\n throw new StackNotFoundError(`Record not found: \"${recordId}\"`);\n }\n throw err;\n }\n }\n }\n\n private getAssociationsForRecord(recordId: string): Association[] {\n const rows = this.exec.all<Record<string, unknown>>(\n 'SELECT * FROM associations WHERE record_id = ?',\n [recordId],\n );\n return rows.map(rowToAssociation);\n }\n\n /**\n * The fields content_index covers for typeId, from the in-memory cache\n * — falling back to a `types` lookup (and populating the cache) only for\n * a typeId this process hasn't seen via saveType() yet, e.g. right after\n * open().\n */\n private getIndexedFields(typeId: string): Map<string, ScalarFieldKind> {\n const cached = this.indexedFieldsByType.get(typeId);\n if (cached) return cached;\n\n const typeRow = this.exec.get<{ schema: string }>('SELECT schema FROM types WHERE id = ?', [\n typeId,\n ]);\n const fields = typeRow\n ? indexedFieldKinds(JSON.parse(typeRow.schema) as Record<string, { kind?: string }>)\n : new Map<string, ScalarFieldKind>();\n this.indexedFieldsByType.set(typeId, fields);\n return fields;\n }\n\n /**\n * Replace a record's content_index rows with what its content currently\n * holds, on every write that can change content or typeId, so neither\n * the ordering nor the file-reference lookup can drift from the record.\n * A field holding no orderable value gets no row at all, which is what\n * puts the record at the end of a sort on that field.\n */\n private syncContentIndex(\n recordId: string,\n typeId: string,\n content: Record<string, unknown>,\n ): void {\n this.exec.run('DELETE FROM content_index WHERE record_id = ?', [recordId]);\n\n for (const [field, kind] of this.getIndexedFields(typeId)) {\n const entry = contentSortEntry(kind, content[field]);\n if (!entry) continue;\n const sql = `INSERT OR IGNORE INTO content_index\n (record_id, field, value_rank, num_value, text_key, text_value, file_id)\n VALUES (?, ?, ?, ?, ?, ?, ?)`;\n if (entry.kind === 'num') {\n this.exec.run(sql, [recordId, field, 0, entry.num, null, null, null]);\n } else {\n // A file-ref field's value is its file id, so the row it already\n // needs for ordering also answers the attachment filter — one\n // field is one row, whatever a query reads it for.\n const fileId = kind === 'file-ref' ? entry.text : null;\n this.exec.run(sql, [recordId, field, 1, null, entry.key, entry.text, fileId]);\n }\n }\n }\n\n /** The declared kind of a record's top-level `field`, if it has one. */\n private sortFieldKind(record: StackRecord, field: string): ScalarFieldKind | undefined {\n return this.getIndexedFields(record.typeId).get(field);\n }\n}\n","/**\n * The actual StackTokenStore logic (createToken/lookupToken/listTokens/\n * revokeToken), parametrized over a SqlExecutor. Currently consumed by\n * record-adapter-sqlite's NativeTokenStore, which points it at a file\n * separate from records — see TOKENS_SCHEMA_SQL in schema.ts.\n */\n\nimport { createHash, randomBytes } from 'node:crypto';\nimport type { TokenSession } from '@haverstack/core';\nimport type { TokenInfo } from '@haverstack/core/wire';\nimport type { SqlExecutor } from './executor.js';\nimport { toMs, fromMs } from './mappers.js';\n\nexport type SharedTokenLogicDeps = {\n exec: SqlExecutor;\n};\n\nexport class SharedTokenLogic {\n constructor(private readonly deps: SharedTokenLogicDeps) {}\n\n private get exec(): SqlExecutor {\n return this.deps.exec;\n }\n\n async createToken(\n principalId: string,\n opts: { onBehalfOf?: string; label?: string; expiresAt?: Date } = {},\n ): Promise<{ id: string; token: string }> {\n const id = randomBytes(8).toString('hex');\n const token = randomBytes(32).toString('hex');\n const tokenHash = createHash('sha256').update(token).digest('hex');\n this.exec.run(\n 'INSERT INTO tokens (id, token_hash, principal_id, subject_id, label, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)',\n [\n id,\n tokenHash,\n principalId,\n // Stored rather than left null for an undelegated token, so a\n // reader never has to know which column stands in for the other.\n opts.onBehalfOf ?? principalId,\n opts.label ?? null,\n toMs(new Date()),\n opts.expiresAt ? toMs(opts.expiresAt) : null,\n ],\n );\n return { id, token };\n }\n\n async lookupToken(token: string): Promise<TokenSession | null> {\n const hash = createHash('sha256').update(token).digest('hex');\n const row = this.exec.get<{\n principal_id: string;\n subject_id: string;\n expires_at: number | null;\n }>('SELECT principal_id, subject_id, expires_at FROM tokens WHERE token_hash = ?', [hash]);\n if (!row) return null;\n if (row.expires_at !== null && Date.now() > row.expires_at) return null;\n return { principalId: row.principal_id, subjectId: row.subject_id };\n }\n\n async listTokens(): Promise<TokenInfo[]> {\n const rows = this.exec.all<{\n id: string;\n principal_id: string;\n subject_id: string;\n label: string | null;\n created_at: number;\n expires_at: number | null;\n }>(\n 'SELECT id, principal_id, subject_id, label, created_at, expires_at FROM tokens ORDER BY created_at DESC',\n );\n return rows.map((row) => ({\n id: row.id,\n principalId: row.principal_id,\n subjectId: row.subject_id,\n ...(row.label && { label: row.label }),\n createdAt: fromMs(row.created_at),\n ...(row.expires_at !== null && { expiresAt: fromMs(row.expires_at) }),\n }));\n }\n\n async revokeToken(id: string): Promise<void> {\n this.exec.run('DELETE FROM tokens WHERE id = ?', [id]);\n }\n}\n","import type { SqlExecutor } from '@haverstack/sqlite-shared';\nimport { DatabaseSync } from './node-sqlite.js';\n\n/** Normalizes node:sqlite's spread-args run/get/all calls to SqlExecutor. */\nexport class NativeSqliteExecutor implements SqlExecutor {\n constructor(private readonly db: DatabaseSync) {}\n\n exec(sql: string): void {\n this.db.exec(sql);\n }\n\n run(sql: string, params: readonly unknown[] = []): number {\n const result = this.db.prepare(sql).run(...(params as (string | number | null)[]));\n return Number(result.changes);\n }\n\n get<T = Record<string, unknown>>(sql: string, params: readonly unknown[] = []): T | undefined {\n return this.db.prepare(sql).get(...(params as (string | number | null)[])) as T | undefined;\n }\n\n all<T = Record<string, unknown>>(sql: string, params: readonly unknown[] = []): T[] {\n return this.db.prepare(sql).all(...(params as (string | number | null)[])) as T[];\n }\n\n transaction<T>(fn: () => T): T {\n this.db.exec('BEGIN');\n try {\n const result = fn();\n this.db.exec('COMMIT');\n return result;\n } catch (err) {\n this.db.exec('ROLLBACK');\n throw err;\n }\n }\n}\n","/**\n * Bearer-token storage backing @haverstack/core's StackTokenStore,\n * implemented against its own file — deliberately separate from\n * NativeSQLiteRecordAdapter's records database. The portable stack file\n * is \"your data, take it with you\"; auth material shouldn't travel with\n * it, and restoring a backup shouldn't resurrect revoked tokens. Server\n * implementations wire these up as separate parts\n * (`{ adapter, tokens }`), the same philosophy as combineAdapters().\n *\n * The actual createToken/lookupToken/listTokens/revokeToken logic lives\n * in @haverstack/sqlite-shared's SharedTokenLogic, reached through the\n * SqlExecutor interface.\n */\n\nimport type { TokenSession } from '@haverstack/core';\nimport type { StackTokenStore, TokenInfo } from '@haverstack/core/wire';\nimport {\n TOKENS_SCHEMA_SQL,\n PRAGMA_JOURNAL_MODE_WAL,\n acquireLock,\n releaseLock,\n SharedTokenLogic,\n} from '@haverstack/sqlite-shared';\nimport { DatabaseSync } from './node-sqlite.js';\nimport { NativeSqliteExecutor } from './executor.js';\n\n/** Conventional sibling path for a token store beside a stack's main .db file. */\nexport const defaultTokenStorePath = (dbPath: string): string => `${dbPath}.tokens`;\n\nexport type NativeTokenStoreOptions = {\n /** Absolute path to the token store file. Created if it doesn't exist. */\n path: string;\n /** Bypass the storage-ownership lock check. See docs/spec/adapters.md § Concurrency & storage ownership. */\n force?: boolean;\n};\n\nexport class NativeTokenStore implements StackTokenStore {\n private db!: DatabaseSync;\n private tokens!: SharedTokenLogic;\n\n private constructor(private readonly path: string) {}\n\n /** Opens the token store, creating the file and schema if needed. */\n static async open(opts: NativeTokenStoreOptions): Promise<NativeTokenStore> {\n acquireLock(opts.path, opts.force);\n const store = new NativeTokenStore(opts.path);\n store.db = new DatabaseSync(opts.path);\n store.db.exec(PRAGMA_JOURNAL_MODE_WAL);\n store.db.exec(TOKENS_SCHEMA_SQL);\n store.tokens = new SharedTokenLogic({ exec: new NativeSqliteExecutor(store.db) });\n return store;\n }\n\n createToken(\n principalId: string,\n opts?: { onBehalfOf?: string; label?: string; expiresAt?: Date },\n ): Promise<{ id: string; token: string }> {\n return this.tokens.createToken(principalId, opts);\n }\n\n lookupToken(token: string): Promise<TokenSession | null> {\n return this.tokens.lookupToken(token);\n }\n\n listTokens(): Promise<TokenInfo[]> {\n return this.tokens.listTokens();\n }\n\n revokeToken(id: string): Promise<void> {\n return this.tokens.revokeToken(id);\n }\n\n async close(): Promise<void> {\n this.db.close();\n releaseLock(this.path);\n }\n}\n","/**\n * Haverstack — Native SQLite Record Adapter\n * -------------------------------------------------------\n * Implements StackRecordAdapter using node:sqlite (Node's built-in\n * SQLite binding, Node >= 22.5). No native compilation, no node-gyp,\n * no prebuilt binaries, and writes go straight to the file via normal\n * SQLite journaling (WAL mode) — no whole-database rewrite per write,\n * no in-memory copy of the whole store, real OS-level file locking.\n * Full-text search uses FTS5.\n *\n * A stack file is owned by exactly one process at a time (see\n * docs/spec/adapters.md § Concurrency & storage ownership). open()/initialize()\n * acquire a PID-stamped lock file beside the database and reject if\n * another live process already holds it; close() releases it.\n *\n * Token storage is a separate concern — see NativeTokenStore in this\n * package, which implements @haverstack/core's StackTokenStore against\n * its own file rather than this adapter's records database.\n *\n * This class itself is a thin node:sqlite binding: schema setup,\n * pragmas, and the storage-ownership lock are genuinely engine-specific\n * and live here. The actual StackRecordAdapter logic lives in\n * @haverstack/sqlite-shared's SharedSqlRecordLogic, reached through the\n * SqlExecutor interface (NativeSqliteExecutor here normalizes\n * node:sqlite's spread-args run/get/all calls to it).\n */\n\nimport { DatabaseSync } from './node-sqlite.js';\nimport { existsSync } from 'fs';\nimport type { StackType, TypeId, FileId, RecordVersion, ActorOptions } from '@haverstack/core';\nimport type {\n StackRecord,\n StackQuery,\n QueryResult,\n Association,\n RecordChanges,\n} from '@haverstack/core';\nimport type { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';\nimport {\n RECORD_SCHEMA_SQL,\n FTS5_SCHEMA_SQL,\n PRAGMA_FOREIGN_KEYS_ON,\n PRAGMA_JOURNAL_MODE_WAL,\n acquireLock,\n releaseLock,\n insertConfigRecord,\n readStackConfig,\n SharedSqlRecordLogic,\n} from '@haverstack/sqlite-shared';\nimport { NativeSqliteExecutor } from './executor.js';\n\n// -------------------------------------------------------\n// Types\n// -------------------------------------------------------\n\nexport type NativeRecordInitializeOptions = {\n /** Absolute path to the .db file. Must not already exist. */\n path: string;\n /** IANA timezone string e.g. \"America/New_York\". Optional passthrough app metadata — no default. */\n timezone?: string;\n /** Entity ID of the stack owner. */\n entityId: string;\n /** Bypass the storage-ownership lock check. See NativeRecordOpenOptions.force. */\n force?: boolean;\n};\n\nexport type NativeRecordOpenOptions = {\n /** Absolute path to an existing .db file. */\n path: string;\n /**\n * Open even if a lock file from another live process is present.\n * Only needed if that process is gone but its PID was reused by\n * something else (the automatic stale-lock check already reclaims\n * locks whose owning process is no longer running).\n */\n force?: boolean;\n};\n\n// -------------------------------------------------------\n// NativeSQLiteRecordAdapter\n// -------------------------------------------------------\n\nexport class NativeSQLiteRecordAdapter implements StackRecordAdapter {\n readonly capabilities: AdapterCapabilities = {\n filter: {\n content: 'path',\n contentPresent: true,\n search: true,\n },\n sort: {\n fields: ['createdAt', 'updatedAt', 'version'],\n contentField: true,\n },\n limits: {\n attachmentBytes: null,\n contentBytes: null,\n },\n };\n\n ownerEntityId!: string;\n timezone: string | undefined;\n\n private db!: DatabaseSync;\n private record!: SharedSqlRecordLogic;\n\n private constructor(private readonly path: string) {}\n\n private wire(): void {\n const exec = new NativeSqliteExecutor(this.db);\n this.record = new SharedSqlRecordLogic({ exec });\n }\n\n /**\n * Initialize a new stack database. Fails if the file already exists —\n * use open() for existing databases.\n */\n static async initialize(opts: NativeRecordInitializeOptions): Promise<NativeSQLiteRecordAdapter> {\n if (existsSync(opts.path)) {\n throw new Error(\n `Cannot initialize: database already exists at \"${opts.path}\". ` +\n `Use NativeSQLiteRecordAdapter.open() instead.`,\n );\n }\n acquireLock(opts.path, opts.force);\n const adapter = new NativeSQLiteRecordAdapter(opts.path);\n adapter.db = new DatabaseSync(opts.path);\n adapter.db.exec(PRAGMA_FOREIGN_KEYS_ON);\n adapter.db.exec(PRAGMA_JOURNAL_MODE_WAL);\n adapter.db.exec(RECORD_SCHEMA_SQL);\n adapter.db.exec(FTS5_SCHEMA_SQL);\n adapter.wire();\n insertConfigRecord(new NativeSqliteExecutor(adapter.db), opts.entityId, opts.timezone);\n adapter.ownerEntityId = opts.entityId;\n adapter.timezone = opts.timezone;\n return adapter;\n }\n\n /**\n * Open an existing stack database. Fails if the file does not exist —\n * use initialize() for new databases.\n */\n static async open(opts: NativeRecordOpenOptions): Promise<NativeSQLiteRecordAdapter> {\n if (!existsSync(opts.path)) {\n throw new Error(\n `Cannot open: no database found at \"${opts.path}\". ` +\n `Use NativeSQLiteRecordAdapter.initialize() to create one.`,\n );\n }\n acquireLock(opts.path, opts.force);\n const adapter = new NativeSQLiteRecordAdapter(opts.path);\n adapter.db = new DatabaseSync(opts.path);\n adapter.db.exec(PRAGMA_FOREIGN_KEYS_ON);\n adapter.db.exec(PRAGMA_JOURNAL_MODE_WAL);\n adapter.db.exec(RECORD_SCHEMA_SQL);\n adapter.db.exec(FTS5_SCHEMA_SQL);\n adapter.wire();\n const config = readStackConfig(new NativeSqliteExecutor(adapter.db));\n adapter.ownerEntityId = config.entityId;\n adapter.timezone = config.timezone;\n return adapter;\n }\n\n // -------------------------------------------------------\n // Records\n // -------------------------------------------------------\n\n createRecord(record: StackRecord): Promise<StackRecord> {\n return this.record.createRecord(record);\n }\n\n getRecord(id: string): Promise<StackRecord | null> {\n return this.record.getRecord(id);\n }\n\n mutateRecord(\n id: string,\n changes: RecordChanges,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.mutateRecord(id, changes, opts);\n }\n\n deleteRecord(\n id: string,\n opts?: { hard?: boolean; expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord | null> {\n return this.record.deleteRecord(id, opts);\n }\n\n undeleteRecord(\n id: string,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.undeleteRecord(id, opts);\n }\n\n restoreVersion(\n id: string,\n version: number,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & {\n restoreAssociations?: boolean;\n } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.restoreVersion(id, version, opts);\n }\n\n commitMigration(\n id: string,\n toTypeId: TypeId,\n content: Record<string, unknown>,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.commitMigration(id, toTypeId, content, opts);\n }\n\n queryRecords(query: StackQuery): Promise<QueryResult> {\n return this.record.queryRecords(query);\n }\n\n deleteUnreferencedAttachmentRecords(\n fileId: FileId,\n metadataTypeIds: TypeId[],\n ): Promise<StackRecord[]> {\n return this.record.deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds);\n }\n\n // -------------------------------------------------------\n // Versions\n // -------------------------------------------------------\n\n getVersions(id: string): Promise<RecordVersion[]> {\n return this.record.getVersions(id);\n }\n\n getVersion(id: string, version: number): Promise<RecordVersion | null> {\n return this.record.getVersion(id, version);\n }\n\n saveVersion(id: string, version: RecordVersion): Promise<void> {\n return this.record.saveVersion(id, version);\n }\n\n // -------------------------------------------------------\n // Types\n // -------------------------------------------------------\n\n saveType(type: StackType): Promise<void> {\n return this.record.saveType(type);\n }\n\n getType(id: TypeId): Promise<StackType | null> {\n return this.record.getType(id);\n }\n\n listTypes(): Promise<StackType[]> {\n return this.record.listTypes();\n }\n\n // -------------------------------------------------------\n // Associations\n // -------------------------------------------------------\n\n associate(\n recordId: string,\n association: Association,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.associate(recordId, association, opts);\n }\n\n dissociate(\n recordId: string,\n association: Association,\n opts?: { expectedVersion?: number; snapshot?: RecordVersion } & ActorOptions,\n ): Promise<StackRecord> {\n return this.record.dissociate(recordId, association, opts);\n }\n\n // -------------------------------------------------------\n // Lifecycle\n // -------------------------------------------------------\n\n /** Folds the WAL back into the main file — useful before copying/backing up the database. */\n async flush(): Promise<void> {\n this.db.exec('PRAGMA wal_checkpoint(TRUNCATE);');\n }\n\n async close(): Promise<void> {\n this.db.close();\n releaseLock(this.path);\n }\n}\n\nexport {\n NativeTokenStore,\n defaultTokenStorePath,\n type NativeTokenStoreOptions,\n} from './token-store.js';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haverstack/record-adapter-sqlite",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Native SQLite (node:sqlite) record adapter for Haverstack — Node's built-in engine, no native compilation",
5
5
  "type": "module",
6
6
  "engines": {
@@ -33,14 +33,14 @@
33
33
  "storage"
34
34
  ],
35
35
  "dependencies": {
36
- "@haverstack/core": "^0.27.0"
36
+ "@haverstack/core": "^0.29.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@haverstack/sqlite-shared": "0.3.0",
40
40
  "@types/node": "^22.0.0",
41
41
  "tsup": "^8.5.1",
42
42
  "typescript": "^5.5.0",
43
- "vitest": "^2.0.0"
43
+ "vitest": "^4.1.11"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup",