@haverstack/record-adapter-do-sqlite 0.14.0 → 0.16.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,6 +1,113 @@
1
1
  import { StackRecord, RecordChanges, RecordVersion, ActorOptions, TypeId, StackQuery, QueryResult, FileId, StackType, Association } from '@haverstack/core';
2
2
  import { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';
3
- import { SqlExecutor } from '@haverstack/sqlite-shared/record';
3
+
4
+ /**
5
+ * Normalizes a SQLite binding's call convention (array-bind-then-step,
6
+ * spread-args run/get/all, cursor-returning exec) behind one interface,
7
+ * so the actual CRUD/query/version/type/association/token logic can live
8
+ * once in this package instead of being re-typed per engine. Each adapter
9
+ * package provides a thin SqlExecutor implementation over its own
10
+ * database handle.
11
+ *
12
+ * Deliberately synchronous: every binding this targets executes queries
13
+ * in-process without yielding, and an async interface would force the
14
+ * shared logic's transaction sequences to interleave.
15
+ */
16
+ interface SqlExecutor {
17
+ /** Run DDL, pragmas, or any statement(s) with no bound params and no results needed. */
18
+ exec(sql: string): void;
19
+ /** Run a single parameterized statement. Returns the number of rows affected (INSERT/UPDATE/DELETE). */
20
+ run(sql: string, params?: readonly unknown[]): number;
21
+ /** Run a parameterized statement and return the first result row, or undefined. */
22
+ get<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T | undefined;
23
+ /** Run a parameterized statement and return all result rows. */
24
+ all<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T[];
25
+ /**
26
+ * Run `fn` atomically: every statement it issues commits together, or
27
+ * none do if it throws. `fn` must be synchronous and call back into
28
+ * this executor only — not every engine's transaction primitive can
29
+ * straddle a suspended call. A binding with a real multi-statement
30
+ * transaction (BEGIN/COMMIT/ROLLBACK) implements this with one; a
31
+ * binding whose transaction primitive is itself a callback wrapper
32
+ * (e.g. a Durable Object's storage.transactionSync) can pass `fn`
33
+ * straight through to it.
34
+ */
35
+ transaction<T>(fn: () => T): T;
36
+ }
37
+
38
+ /** timezone is optional passthrough app metadata — no default. */
39
+ type StackConfig = {
40
+ entityId: string;
41
+ timezone: string | undefined;
42
+ };
43
+
44
+ /**
45
+ * The engine-independent half of a SQLite-backed StackRecordAdapter: the
46
+ * capability declaration and the delegation of all seventeen
47
+ * StackRecordAdapter methods onto SharedSqlRecordLogic. Every method here
48
+ * is a forward with no engine-specific content, so an adapter that owns a
49
+ * SqlExecutor already knows how to answer all of them.
50
+ *
51
+ * What a concrete adapter is left to supply is exactly what differs
52
+ * between bindings: constructing the database handle, applying the
53
+ * schema, and its own lifecycle (flush/close) — hence those two being the
54
+ * only abstract members.
55
+ *
56
+ * Lives in record.js's barrel rather than index.js's, so an adapter that
57
+ * imports it never pulls in token-logic.ts's `node:crypto` — see the note
58
+ * at the top of record.ts.
59
+ */
60
+
61
+ declare abstract class SharedSqlRecordAdapter implements StackRecordAdapter {
62
+ readonly capabilities: AdapterCapabilities;
63
+ readonly ownerEntityId: string;
64
+ readonly timezone: string | undefined;
65
+ private readonly record;
66
+ protected constructor(exec: SqlExecutor, config: StackConfig);
67
+ createRecord(record: StackRecord): Promise<StackRecord>;
68
+ getRecord(id: string): Promise<StackRecord | null>;
69
+ mutateRecord(id: string, changes: RecordChanges, opts?: {
70
+ expectedVersion?: number;
71
+ snapshot?: RecordVersion;
72
+ } & ActorOptions): Promise<StackRecord>;
73
+ deleteRecord(id: string, opts?: {
74
+ hard?: boolean;
75
+ expectedVersion?: number;
76
+ snapshot?: RecordVersion;
77
+ } & ActorOptions): Promise<StackRecord | null>;
78
+ undeleteRecord(id: string, opts?: {
79
+ expectedVersion?: number;
80
+ snapshot?: RecordVersion;
81
+ } & ActorOptions): Promise<StackRecord>;
82
+ restoreVersion(id: string, version: number, opts?: {
83
+ expectedVersion?: number;
84
+ snapshot?: RecordVersion;
85
+ } & {
86
+ restoreAssociations?: boolean;
87
+ } & ActorOptions): Promise<StackRecord>;
88
+ commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
89
+ expectedVersion?: number;
90
+ snapshot?: RecordVersion;
91
+ } & ActorOptions): Promise<StackRecord>;
92
+ queryRecords(query: StackQuery): Promise<QueryResult>;
93
+ deleteUnreferencedAttachmentRecords(fileId: FileId, metadataTypeIds: TypeId[]): Promise<StackRecord[]>;
94
+ getVersions(id: string): Promise<RecordVersion[]>;
95
+ getVersion(id: string, version: number): Promise<RecordVersion | null>;
96
+ saveVersion(id: string, version: RecordVersion): Promise<void>;
97
+ saveType(type: StackType): Promise<void>;
98
+ getType(id: TypeId): Promise<StackType | null>;
99
+ listTypes(): Promise<StackType[]>;
100
+ associate(recordId: string, association: Association, opts?: {
101
+ expectedVersion?: number;
102
+ snapshot?: RecordVersion;
103
+ } & ActorOptions): Promise<StackRecord>;
104
+ dissociate(recordId: string, association: Association, opts?: {
105
+ expectedVersion?: number;
106
+ snapshot?: RecordVersion;
107
+ } & ActorOptions): Promise<StackRecord>;
108
+ abstract flush(): Promise<void>;
109
+ abstract close(): Promise<void>;
110
+ }
4
111
 
5
112
  /**
6
113
  * Normalizes a Durable Object's ctx.storage.sql to SqlExecutor.
@@ -47,12 +154,12 @@ declare class DurableObjectSqliteExecutor implements SqlExecutor {
47
154
  * call returns, so flush()/close() are no-ops kept only to satisfy the
48
155
  * optional StackRecordAdapter methods.
49
156
  *
50
- * This class itself is a thin binding: schema setup and the one piece of
51
- * genuinely engine-specific wiring — SqlExecutor.transaction() reaching
157
+ * This class itself is a thin binding: the one piece of genuinely
158
+ * engine-specific wiring — SqlExecutor.transaction() reaching
52
159
  * ctx.storage.transactionSync() instead of raw SQL BEGIN/COMMIT/ROLLBACK,
53
- * which DO SQLite rejects outright — live here (see executor.ts). The
54
- * actual StackRecordAdapter logic lives in @haverstack/sqlite-shared's
55
- * SharedSqlRecordLogic, exactly as it does for record-adapter-sqlite.
160
+ * which DO SQLite rejects outright — lives in executor.ts. Everything
161
+ * above storage comes from @haverstack/sqlite-shared's
162
+ * SharedSqlRecordAdapter, exactly as it does for record-adapter-sqlite.
56
163
  */
57
164
 
58
165
  type DoRecordCreateOptions = {
@@ -61,12 +168,7 @@ type DoRecordCreateOptions = {
61
168
  /** IANA timezone string e.g. "America/New_York". Optional passthrough app metadata — no default. */
62
169
  timezone?: string;
63
170
  };
64
- declare class DoSQLiteRecordAdapter implements StackRecordAdapter {
65
- private readonly exec;
66
- readonly capabilities: AdapterCapabilities;
67
- ownerEntityId: string;
68
- timezone: string | undefined;
69
- private readonly record;
171
+ declare class DoSQLiteRecordAdapter extends SharedSqlRecordAdapter {
70
172
  private constructor();
71
173
  /**
72
174
  * Create (or reattach to) the adapter for a DO instance. There is no
@@ -76,49 +178,12 @@ declare class DoSQLiteRecordAdapter implements StackRecordAdapter {
76
178
  * or it doesn't (first call — opts.entityId/timezone become the config).
77
179
  * Schema DDL is `CREATE TABLE IF NOT EXISTS`, so running it every call
78
180
  * is idempotent and cheap.
181
+ *
182
+ * DO SQLite manages its own durability and rejects PRAGMA journal_mode
183
+ * outright ("not authorized") — verified against the real runtime, not
184
+ * assumed — hence `wal: false`, unlike record-adapter-sqlite.
79
185
  */
80
186
  static create(storage: DurableObjectStorage, opts: DoRecordCreateOptions): Promise<DoSQLiteRecordAdapter>;
81
- createRecord(record: StackRecord): Promise<StackRecord>;
82
- getRecord(id: string): Promise<StackRecord | null>;
83
- mutateRecord(id: string, changes: RecordChanges, opts?: {
84
- expectedVersion?: number;
85
- snapshot?: RecordVersion;
86
- } & ActorOptions): Promise<StackRecord>;
87
- deleteRecord(id: string, opts?: {
88
- hard?: boolean;
89
- expectedVersion?: number;
90
- snapshot?: RecordVersion;
91
- } & ActorOptions): Promise<StackRecord | null>;
92
- undeleteRecord(id: string, opts?: {
93
- expectedVersion?: number;
94
- snapshot?: RecordVersion;
95
- } & ActorOptions): Promise<StackRecord>;
96
- restoreVersion(id: string, version: number, opts?: {
97
- expectedVersion?: number;
98
- snapshot?: RecordVersion;
99
- } & {
100
- restoreAssociations?: boolean;
101
- } & ActorOptions): Promise<StackRecord>;
102
- commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
103
- expectedVersion?: number;
104
- snapshot?: RecordVersion;
105
- } & ActorOptions): Promise<StackRecord>;
106
- queryRecords(query: StackQuery): Promise<QueryResult>;
107
- deleteUnreferencedAttachmentRecords(fileId: FileId, metadataTypeIds: TypeId[]): Promise<StackRecord[]>;
108
- getVersions(id: string): Promise<RecordVersion[]>;
109
- getVersion(id: string, version: number): Promise<RecordVersion | null>;
110
- saveVersion(id: string, version: RecordVersion): Promise<void>;
111
- saveType(type: StackType): Promise<void>;
112
- getType(id: TypeId): Promise<StackType | null>;
113
- listTypes(): Promise<StackType[]>;
114
- associate(recordId: string, association: Association, opts?: {
115
- expectedVersion?: number;
116
- snapshot?: RecordVersion;
117
- } & ActorOptions): Promise<StackRecord>;
118
- dissociate(recordId: string, association: Association, opts?: {
119
- expectedVersion?: number;
120
- snapshot?: RecordVersion;
121
- } & ActorOptions): Promise<StackRecord>;
122
187
  /** No-op: every write through ctx.storage.sql is already durable. */
123
188
  flush(): Promise<void>;
124
189
  /** No-op: no lock file, no connection to release — the DO's own lifecycle governs storage. */