@haverstack/record-adapter-sqlite 0.22.0 → 0.24.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 +124 -55
- package/dist/index.js +156 -117
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,115 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StackRecord, RecordChanges, RecordVersion, ActorOptions, TypeId, StackQuery, QueryResult, FileId, StackType, Association, TokenSession } from '@haverstack/core';
|
|
2
2
|
import { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';
|
|
3
3
|
import { StackTokenStore, TokenInfo } from '@haverstack/core/wire';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes a SQLite binding's call convention (array-bind-then-step,
|
|
7
|
+
* spread-args run/get/all, cursor-returning exec) behind one interface,
|
|
8
|
+
* so the actual CRUD/query/version/type/association/token logic can live
|
|
9
|
+
* once in this package instead of being re-typed per engine. Each adapter
|
|
10
|
+
* package provides a thin SqlExecutor implementation over its own
|
|
11
|
+
* database handle.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately synchronous: every binding this targets executes queries
|
|
14
|
+
* in-process without yielding, and an async interface would force the
|
|
15
|
+
* shared logic's transaction sequences to interleave.
|
|
16
|
+
*/
|
|
17
|
+
interface SqlExecutor {
|
|
18
|
+
/** Run DDL, pragmas, or any statement(s) with no bound params and no results needed. */
|
|
19
|
+
exec(sql: string): void;
|
|
20
|
+
/** Run a single parameterized statement. Returns the number of rows affected (INSERT/UPDATE/DELETE). */
|
|
21
|
+
run(sql: string, params?: readonly unknown[]): number;
|
|
22
|
+
/** Run a parameterized statement and return the first result row, or undefined. */
|
|
23
|
+
get<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T | undefined;
|
|
24
|
+
/** Run a parameterized statement and return all result rows. */
|
|
25
|
+
all<T = Record<string, unknown>>(sql: string, params?: readonly unknown[]): T[];
|
|
26
|
+
/**
|
|
27
|
+
* Run `fn` atomically: every statement it issues commits together, or
|
|
28
|
+
* none do if it throws. `fn` must be synchronous and call back into
|
|
29
|
+
* this executor only — not every engine's transaction primitive can
|
|
30
|
+
* straddle a suspended call. A binding with a real multi-statement
|
|
31
|
+
* transaction (BEGIN/COMMIT/ROLLBACK) implements this with one; a
|
|
32
|
+
* binding whose transaction primitive is itself a callback wrapper
|
|
33
|
+
* (e.g. a Durable Object's storage.transactionSync) can pass `fn`
|
|
34
|
+
* straight through to it.
|
|
35
|
+
*/
|
|
36
|
+
transaction<T>(fn: () => T): T;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** timezone is optional passthrough app metadata — no default. */
|
|
40
|
+
type StackConfig = {
|
|
41
|
+
entityId: string;
|
|
42
|
+
timezone: string | undefined;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The engine-independent half of a SQLite-backed StackRecordAdapter: the
|
|
47
|
+
* capability declaration and the delegation of all seventeen
|
|
48
|
+
* StackRecordAdapter methods onto SharedSqlRecordLogic. Every method here
|
|
49
|
+
* is a forward with no engine-specific content, so an adapter that owns a
|
|
50
|
+
* SqlExecutor already knows how to answer all of them.
|
|
51
|
+
*
|
|
52
|
+
* What a concrete adapter is left to supply is exactly what differs
|
|
53
|
+
* between bindings: constructing the database handle, applying the
|
|
54
|
+
* schema, and its own lifecycle (flush/close) — hence those two being the
|
|
55
|
+
* only abstract members.
|
|
56
|
+
*
|
|
57
|
+
* Lives in record.js's barrel rather than index.js's, so an adapter that
|
|
58
|
+
* imports it never pulls in token-logic.ts's `node:crypto` — see the note
|
|
59
|
+
* at the top of record.ts.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
declare abstract class SharedSqlRecordAdapter implements StackRecordAdapter {
|
|
63
|
+
readonly capabilities: AdapterCapabilities;
|
|
64
|
+
readonly ownerEntityId: string;
|
|
65
|
+
readonly timezone: string | undefined;
|
|
66
|
+
private readonly record;
|
|
67
|
+
protected constructor(exec: SqlExecutor, config: StackConfig);
|
|
68
|
+
createRecord(record: StackRecord): Promise<StackRecord>;
|
|
69
|
+
getRecord(id: string): Promise<StackRecord | null>;
|
|
70
|
+
mutateRecord(id: string, changes: RecordChanges, opts?: {
|
|
71
|
+
expectedVersion?: number;
|
|
72
|
+
snapshot?: RecordVersion;
|
|
73
|
+
} & ActorOptions): Promise<StackRecord>;
|
|
74
|
+
deleteRecord(id: string, opts?: {
|
|
75
|
+
hard?: boolean;
|
|
76
|
+
expectedVersion?: number;
|
|
77
|
+
snapshot?: RecordVersion;
|
|
78
|
+
} & ActorOptions): Promise<StackRecord | null>;
|
|
79
|
+
undeleteRecord(id: string, opts?: {
|
|
80
|
+
expectedVersion?: number;
|
|
81
|
+
snapshot?: RecordVersion;
|
|
82
|
+
} & ActorOptions): Promise<StackRecord>;
|
|
83
|
+
restoreVersion(id: string, version: number, opts?: {
|
|
84
|
+
expectedVersion?: number;
|
|
85
|
+
snapshot?: RecordVersion;
|
|
86
|
+
} & {
|
|
87
|
+
restoreAssociations?: boolean;
|
|
88
|
+
} & ActorOptions): Promise<StackRecord>;
|
|
89
|
+
commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
|
|
90
|
+
expectedVersion?: number;
|
|
91
|
+
snapshot?: RecordVersion;
|
|
92
|
+
} & ActorOptions): Promise<StackRecord>;
|
|
93
|
+
queryRecords(query: StackQuery): Promise<QueryResult>;
|
|
94
|
+
deleteUnreferencedAttachmentRecords(fileId: FileId, metadataTypeIds: TypeId[]): Promise<StackRecord[]>;
|
|
95
|
+
getVersions(id: string): Promise<RecordVersion[]>;
|
|
96
|
+
getVersion(id: string, version: number): Promise<RecordVersion | null>;
|
|
97
|
+
saveVersion(id: string, version: RecordVersion): Promise<void>;
|
|
98
|
+
saveType(type: StackType): Promise<void>;
|
|
99
|
+
getType(id: TypeId): Promise<StackType | null>;
|
|
100
|
+
listTypes(): Promise<StackType[]>;
|
|
101
|
+
associate(recordId: string, association: Association, opts?: {
|
|
102
|
+
expectedVersion?: number;
|
|
103
|
+
snapshot?: RecordVersion;
|
|
104
|
+
} & ActorOptions): Promise<StackRecord>;
|
|
105
|
+
dissociate(recordId: string, association: Association, opts?: {
|
|
106
|
+
expectedVersion?: number;
|
|
107
|
+
snapshot?: RecordVersion;
|
|
108
|
+
} & ActorOptions): Promise<StackRecord>;
|
|
109
|
+
abstract flush(): Promise<void>;
|
|
110
|
+
abstract close(): Promise<void>;
|
|
111
|
+
}
|
|
112
|
+
|
|
5
113
|
/**
|
|
6
114
|
* Bearer-token storage backing @haverstack/core's StackTokenStore,
|
|
7
115
|
* implemented against its own file — deliberately separate from
|
|
@@ -64,12 +172,12 @@ declare class NativeTokenStore implements StackTokenStore {
|
|
|
64
172
|
* package, which implements @haverstack/core's StackTokenStore against
|
|
65
173
|
* its own file rather than this adapter's records database.
|
|
66
174
|
*
|
|
67
|
-
* This class itself is a thin node:sqlite binding:
|
|
68
|
-
*
|
|
69
|
-
* and live here.
|
|
70
|
-
* @haverstack/sqlite-shared's
|
|
71
|
-
* SqlExecutor interface (NativeSqliteExecutor here normalizes
|
|
72
|
-
*
|
|
175
|
+
* This class itself is a thin node:sqlite binding: opening the file, the
|
|
176
|
+
* storage-ownership lock, and the WAL checkpoint are what's genuinely
|
|
177
|
+
* engine-specific and live here. Everything above storage comes from
|
|
178
|
+
* @haverstack/sqlite-shared's SharedSqlRecordAdapter, reached through the
|
|
179
|
+
* SqlExecutor interface (NativeSqliteExecutor here normalizes node:sqlite's
|
|
180
|
+
* spread-args run/get/all calls to it).
|
|
73
181
|
*/
|
|
74
182
|
|
|
75
183
|
type NativeRecordInitializeOptions = {
|
|
@@ -93,15 +201,17 @@ type NativeRecordOpenOptions = {
|
|
|
93
201
|
*/
|
|
94
202
|
force?: boolean;
|
|
95
203
|
};
|
|
96
|
-
declare class NativeSQLiteRecordAdapter
|
|
204
|
+
declare class NativeSQLiteRecordAdapter extends SharedSqlRecordAdapter {
|
|
97
205
|
private readonly path;
|
|
98
|
-
readonly
|
|
99
|
-
ownerEntityId: string;
|
|
100
|
-
timezone: string | undefined;
|
|
101
|
-
private db;
|
|
102
|
-
private record;
|
|
206
|
+
private readonly db;
|
|
103
207
|
private constructor();
|
|
104
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Takes the storage-ownership lock, opens the file, and brings the
|
|
210
|
+
* schema up. Shared by initialize() and open(), which differ only in
|
|
211
|
+
* whether the file may already exist and in where the config record
|
|
212
|
+
* comes from.
|
|
213
|
+
*/
|
|
214
|
+
private static attach;
|
|
105
215
|
/**
|
|
106
216
|
* Initialize a new stack database. Fails if the file already exists —
|
|
107
217
|
* use open() for existing databases.
|
|
@@ -112,47 +222,6 @@ declare class NativeSQLiteRecordAdapter implements StackRecordAdapter {
|
|
|
112
222
|
* use initialize() for new databases.
|
|
113
223
|
*/
|
|
114
224
|
static open(opts: NativeRecordOpenOptions): Promise<NativeSQLiteRecordAdapter>;
|
|
115
|
-
createRecord(record: StackRecord): Promise<StackRecord>;
|
|
116
|
-
getRecord(id: string): Promise<StackRecord | null>;
|
|
117
|
-
mutateRecord(id: string, changes: RecordChanges, opts?: {
|
|
118
|
-
expectedVersion?: number;
|
|
119
|
-
snapshot?: RecordVersion;
|
|
120
|
-
} & ActorOptions): Promise<StackRecord>;
|
|
121
|
-
deleteRecord(id: string, opts?: {
|
|
122
|
-
hard?: boolean;
|
|
123
|
-
expectedVersion?: number;
|
|
124
|
-
snapshot?: RecordVersion;
|
|
125
|
-
} & ActorOptions): Promise<StackRecord | null>;
|
|
126
|
-
undeleteRecord(id: string, opts?: {
|
|
127
|
-
expectedVersion?: number;
|
|
128
|
-
snapshot?: RecordVersion;
|
|
129
|
-
} & ActorOptions): Promise<StackRecord>;
|
|
130
|
-
restoreVersion(id: string, version: number, opts?: {
|
|
131
|
-
expectedVersion?: number;
|
|
132
|
-
snapshot?: RecordVersion;
|
|
133
|
-
} & {
|
|
134
|
-
restoreAssociations?: boolean;
|
|
135
|
-
} & ActorOptions): Promise<StackRecord>;
|
|
136
|
-
commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
|
|
137
|
-
expectedVersion?: number;
|
|
138
|
-
snapshot?: RecordVersion;
|
|
139
|
-
} & ActorOptions): Promise<StackRecord>;
|
|
140
|
-
queryRecords(query: StackQuery): Promise<QueryResult>;
|
|
141
|
-
deleteUnreferencedAttachmentRecords(fileId: FileId, metadataTypeIds: TypeId[]): Promise<StackRecord[]>;
|
|
142
|
-
getVersions(id: string): Promise<RecordVersion[]>;
|
|
143
|
-
getVersion(id: string, version: number): Promise<RecordVersion | null>;
|
|
144
|
-
saveVersion(id: string, version: RecordVersion): Promise<void>;
|
|
145
|
-
saveType(type: StackType): Promise<void>;
|
|
146
|
-
getType(id: TypeId): Promise<StackType | null>;
|
|
147
|
-
listTypes(): Promise<StackType[]>;
|
|
148
|
-
associate(recordId: string, association: Association, opts?: {
|
|
149
|
-
expectedVersion?: number;
|
|
150
|
-
snapshot?: RecordVersion;
|
|
151
|
-
} & ActorOptions): Promise<StackRecord>;
|
|
152
|
-
dissociate(recordId: string, association: Association, opts?: {
|
|
153
|
-
expectedVersion?: number;
|
|
154
|
-
snapshot?: RecordVersion;
|
|
155
|
-
} & ActorOptions): Promise<StackRecord>;
|
|
156
225
|
/** Folds the WAL back into the main file — useful before copying/backing up the database. */
|
|
157
226
|
flush(): Promise<void>;
|
|
158
227
|
close(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,11 @@ var RECORD_SCHEMA_SQL = `
|
|
|
33
33
|
-- a foreign namespace. All four are in the primary key, so two targets
|
|
34
34
|
-- differing only by namespace are two associations.
|
|
35
35
|
--
|
|
36
|
+
-- attachment_record_id stays out of the primary key: it annotates a
|
|
37
|
+
-- reference rather than naming one, so re-pointing it updates the row an
|
|
38
|
+
-- associate() lands on instead of writing a second reference to the same
|
|
39
|
+
-- file. See docs/spec/attachments.md \xA7 Naming the upload a reference came from.
|
|
40
|
+
--
|
|
36
41
|
-- The primary key leads with record_id, so its implicit index already
|
|
37
42
|
-- serves every by-record read and delete; the indexes below exist only
|
|
38
43
|
-- for the filter directions it cannot answer.
|
|
@@ -45,6 +50,7 @@ var RECORD_SCHEMA_SQL = `
|
|
|
45
50
|
related_id TEXT NOT NULL DEFAULT '',
|
|
46
51
|
related_ns TEXT NOT NULL DEFAULT '',
|
|
47
52
|
related_stack TEXT NOT NULL DEFAULT '',
|
|
53
|
+
attachment_record_id TEXT NOT NULL DEFAULT '',
|
|
48
54
|
PRIMARY KEY (record_id, kind, label, file_id, related_scope, related_id, related_ns, related_stack)
|
|
49
55
|
) STRICT;
|
|
50
56
|
|
|
@@ -151,6 +157,12 @@ var FTS5_SCHEMA_SQL = `
|
|
|
151
157
|
`;
|
|
152
158
|
var PRAGMA_FOREIGN_KEYS_ON = `PRAGMA foreign_keys = ON;`;
|
|
153
159
|
var PRAGMA_JOURNAL_MODE_WAL = `PRAGMA journal_mode = WAL;`;
|
|
160
|
+
var applyRecordSchema = (exec, opts) => {
|
|
161
|
+
exec.exec(PRAGMA_FOREIGN_KEYS_ON);
|
|
162
|
+
exec.exec(PRAGMA_JOURNAL_MODE_WAL);
|
|
163
|
+
exec.exec(RECORD_SCHEMA_SQL);
|
|
164
|
+
exec.exec(FTS5_SCHEMA_SQL);
|
|
165
|
+
};
|
|
154
166
|
var SORT_FIELDS = NATIVE_SORT_FIELDS;
|
|
155
167
|
var toBase64 = (text) => {
|
|
156
168
|
const bytes = new TextEncoder().encode(text);
|
|
@@ -575,10 +587,15 @@ var rowToAssociation = (row) => {
|
|
|
575
587
|
return { kind: "tag", label: row.label };
|
|
576
588
|
}
|
|
577
589
|
if (row.kind === "attachment") {
|
|
590
|
+
const attachmentRecordId = row.attachment_record_id;
|
|
578
591
|
return {
|
|
579
592
|
kind: "attachment",
|
|
580
593
|
label: row.label,
|
|
581
|
-
fileId: row.file_id
|
|
594
|
+
fileId: row.file_id,
|
|
595
|
+
// Stored as '' when absent, like every other optional column here —
|
|
596
|
+
// the field is omitted rather than served empty, so a round trip
|
|
597
|
+
// produces the association the caller wrote.
|
|
598
|
+
...attachmentRecordId && { attachmentRecordId }
|
|
582
599
|
};
|
|
583
600
|
}
|
|
584
601
|
return {
|
|
@@ -650,18 +667,26 @@ var isForeignKeyViolation = (err) => err instanceof Error && err.message.include
|
|
|
650
667
|
var isUniqueConstraintViolation = (err) => err instanceof Error && err.message.includes("UNIQUE constraint failed");
|
|
651
668
|
|
|
652
669
|
// ../sqlite-shared/dist/config.js
|
|
670
|
+
var CONFIG_ROW_SQL = `SELECT content FROM records WHERE id = '_config'`;
|
|
653
671
|
var insertConfigRecord = (exec, entityId, timezone) => {
|
|
654
672
|
const now = Date.now();
|
|
655
673
|
exec.run(`INSERT INTO records (id, type_id, created_at, updated_at, content, version)
|
|
656
674
|
VALUES ('_config', '_config@1', ?, ?, ?, 1)`, [now, now, JSON.stringify({ entityId, timezone })]);
|
|
675
|
+
return { entityId, timezone };
|
|
657
676
|
};
|
|
658
|
-
var
|
|
659
|
-
const row = exec.get(
|
|
677
|
+
var tryReadStackConfig = (exec) => {
|
|
678
|
+
const row = exec.get(CONFIG_ROW_SQL);
|
|
660
679
|
if (!row)
|
|
661
|
-
|
|
680
|
+
return null;
|
|
662
681
|
const content = JSON.parse(row.content);
|
|
663
682
|
return { entityId: content.entityId, timezone: content.timezone };
|
|
664
683
|
};
|
|
684
|
+
var readStackConfig = (exec) => {
|
|
685
|
+
const config = tryReadStackConfig(exec);
|
|
686
|
+
if (!config)
|
|
687
|
+
throw new Error("Stack database is missing its config record.");
|
|
688
|
+
return config;
|
|
689
|
+
};
|
|
665
690
|
var asStackQueryError = (query, run) => {
|
|
666
691
|
try {
|
|
667
692
|
return run();
|
|
@@ -1189,6 +1214,10 @@ var SharedSqlRecordLogic = class {
|
|
|
1189
1214
|
this.insertAssociations(recordId, associations);
|
|
1190
1215
|
}
|
|
1191
1216
|
/**
|
|
1217
|
+
* Upserts on association identity: the primary key decides which row a
|
|
1218
|
+
* write lands on, and `attachment_record_id` — outside it — is whatever
|
|
1219
|
+
* the incoming association says, so re-pointing one costs no second row.
|
|
1220
|
+
*
|
|
1192
1221
|
* FK enforcement (PRAGMA_FOREIGN_KEYS_ON) means inserting an
|
|
1193
1222
|
* association against a record that doesn't exist throws — mapped
|
|
1194
1223
|
* here to StackNotFoundError so associate() on a nonexistent record
|
|
@@ -1197,9 +1226,17 @@ var SharedSqlRecordLogic = class {
|
|
|
1197
1226
|
insertAssociations(recordId, associations) {
|
|
1198
1227
|
for (const assoc of associations) {
|
|
1199
1228
|
try {
|
|
1200
|
-
this.exec.run(`INSERT
|
|
1201
|
-
(record_id, kind, label, file_id, related_scope, related_id, related_ns,
|
|
1202
|
-
|
|
1229
|
+
this.exec.run(`INSERT INTO associations
|
|
1230
|
+
(record_id, kind, label, file_id, related_scope, related_id, related_ns,
|
|
1231
|
+
related_stack, attachment_record_id)
|
|
1232
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1233
|
+
ON CONFLICT DO UPDATE SET attachment_record_id = excluded.attachment_record_id`, [
|
|
1234
|
+
recordId,
|
|
1235
|
+
assoc.kind,
|
|
1236
|
+
assoc.label,
|
|
1237
|
+
...associationKeyColumns(assoc),
|
|
1238
|
+
assoc.kind === "attachment" ? assoc.attachmentRecordId ?? "" : ""
|
|
1239
|
+
]);
|
|
1203
1240
|
} catch (err) {
|
|
1204
1241
|
if (isForeignKeyViolation(err)) {
|
|
1205
1242
|
throw new StackNotFoundError(`Record not found: "${recordId}"`);
|
|
@@ -1258,6 +1295,97 @@ var SharedSqlRecordLogic = class {
|
|
|
1258
1295
|
return this.getIndexedFields(record.typeId).get(field);
|
|
1259
1296
|
}
|
|
1260
1297
|
};
|
|
1298
|
+
|
|
1299
|
+
// ../sqlite-shared/dist/record-adapter.js
|
|
1300
|
+
var SQLITE_RECORD_CAPABILITIES = {
|
|
1301
|
+
filter: {
|
|
1302
|
+
content: "path",
|
|
1303
|
+
contentPresent: true,
|
|
1304
|
+
search: true
|
|
1305
|
+
},
|
|
1306
|
+
sort: {
|
|
1307
|
+
fields: ["createdAt", "updatedAt", "version"],
|
|
1308
|
+
contentField: true
|
|
1309
|
+
},
|
|
1310
|
+
limits: {
|
|
1311
|
+
attachmentBytes: null,
|
|
1312
|
+
contentBytes: null
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
var SharedSqlRecordAdapter = class {
|
|
1316
|
+
capabilities = SQLITE_RECORD_CAPABILITIES;
|
|
1317
|
+
ownerEntityId;
|
|
1318
|
+
timezone;
|
|
1319
|
+
record;
|
|
1320
|
+
constructor(exec, config) {
|
|
1321
|
+
this.record = new SharedSqlRecordLogic({ exec });
|
|
1322
|
+
this.ownerEntityId = config.entityId;
|
|
1323
|
+
this.timezone = config.timezone;
|
|
1324
|
+
}
|
|
1325
|
+
// -------------------------------------------------------
|
|
1326
|
+
// Records
|
|
1327
|
+
// -------------------------------------------------------
|
|
1328
|
+
createRecord(record) {
|
|
1329
|
+
return this.record.createRecord(record);
|
|
1330
|
+
}
|
|
1331
|
+
getRecord(id) {
|
|
1332
|
+
return this.record.getRecord(id);
|
|
1333
|
+
}
|
|
1334
|
+
mutateRecord(id, changes, opts) {
|
|
1335
|
+
return this.record.mutateRecord(id, changes, opts);
|
|
1336
|
+
}
|
|
1337
|
+
deleteRecord(id, opts) {
|
|
1338
|
+
return this.record.deleteRecord(id, opts);
|
|
1339
|
+
}
|
|
1340
|
+
undeleteRecord(id, opts) {
|
|
1341
|
+
return this.record.undeleteRecord(id, opts);
|
|
1342
|
+
}
|
|
1343
|
+
restoreVersion(id, version, opts) {
|
|
1344
|
+
return this.record.restoreVersion(id, version, opts);
|
|
1345
|
+
}
|
|
1346
|
+
commitMigration(id, toTypeId, content, opts) {
|
|
1347
|
+
return this.record.commitMigration(id, toTypeId, content, opts);
|
|
1348
|
+
}
|
|
1349
|
+
queryRecords(query) {
|
|
1350
|
+
return this.record.queryRecords(query);
|
|
1351
|
+
}
|
|
1352
|
+
deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds) {
|
|
1353
|
+
return this.record.deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds);
|
|
1354
|
+
}
|
|
1355
|
+
// -------------------------------------------------------
|
|
1356
|
+
// Versions
|
|
1357
|
+
// -------------------------------------------------------
|
|
1358
|
+
getVersions(id) {
|
|
1359
|
+
return this.record.getVersions(id);
|
|
1360
|
+
}
|
|
1361
|
+
getVersion(id, version) {
|
|
1362
|
+
return this.record.getVersion(id, version);
|
|
1363
|
+
}
|
|
1364
|
+
saveVersion(id, version) {
|
|
1365
|
+
return this.record.saveVersion(id, version);
|
|
1366
|
+
}
|
|
1367
|
+
// -------------------------------------------------------
|
|
1368
|
+
// Types
|
|
1369
|
+
// -------------------------------------------------------
|
|
1370
|
+
saveType(type) {
|
|
1371
|
+
return this.record.saveType(type);
|
|
1372
|
+
}
|
|
1373
|
+
getType(id) {
|
|
1374
|
+
return this.record.getType(id);
|
|
1375
|
+
}
|
|
1376
|
+
listTypes() {
|
|
1377
|
+
return this.record.listTypes();
|
|
1378
|
+
}
|
|
1379
|
+
// -------------------------------------------------------
|
|
1380
|
+
// Associations
|
|
1381
|
+
// -------------------------------------------------------
|
|
1382
|
+
associate(recordId, association, opts) {
|
|
1383
|
+
return this.record.associate(recordId, association, opts);
|
|
1384
|
+
}
|
|
1385
|
+
dissociate(recordId, association, opts) {
|
|
1386
|
+
return this.record.dissociate(recordId, association, opts);
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1261
1389
|
var lockPathFor = (dbPath) => `${dbPath}.lock`;
|
|
1262
1390
|
var isProcessAlive = (pid) => {
|
|
1263
1391
|
try {
|
|
@@ -1415,33 +1543,26 @@ var NativeTokenStore = class _NativeTokenStore {
|
|
|
1415
1543
|
};
|
|
1416
1544
|
|
|
1417
1545
|
// src/index.ts
|
|
1418
|
-
var NativeSQLiteRecordAdapter = class _NativeSQLiteRecordAdapter {
|
|
1419
|
-
constructor(path) {
|
|
1546
|
+
var NativeSQLiteRecordAdapter = class _NativeSQLiteRecordAdapter extends SharedSqlRecordAdapter {
|
|
1547
|
+
constructor(path, db, exec, config) {
|
|
1548
|
+
super(exec, config);
|
|
1420
1549
|
this.path = path;
|
|
1550
|
+
this.db = db;
|
|
1421
1551
|
}
|
|
1422
1552
|
path;
|
|
1423
|
-
capabilities = {
|
|
1424
|
-
filter: {
|
|
1425
|
-
content: "path",
|
|
1426
|
-
contentPresent: true,
|
|
1427
|
-
search: true
|
|
1428
|
-
},
|
|
1429
|
-
sort: {
|
|
1430
|
-
fields: ["createdAt", "updatedAt", "version"],
|
|
1431
|
-
contentField: true
|
|
1432
|
-
},
|
|
1433
|
-
limits: {
|
|
1434
|
-
attachmentBytes: null,
|
|
1435
|
-
contentBytes: null
|
|
1436
|
-
}
|
|
1437
|
-
};
|
|
1438
|
-
ownerEntityId;
|
|
1439
|
-
timezone;
|
|
1440
1553
|
db;
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1554
|
+
/**
|
|
1555
|
+
* Takes the storage-ownership lock, opens the file, and brings the
|
|
1556
|
+
* schema up. Shared by initialize() and open(), which differ only in
|
|
1557
|
+
* whether the file may already exist and in where the config record
|
|
1558
|
+
* comes from.
|
|
1559
|
+
*/
|
|
1560
|
+
static attach(path, force) {
|
|
1561
|
+
acquireLock(path, force);
|
|
1562
|
+
const db = new DatabaseSync(path);
|
|
1563
|
+
const exec = new NativeSqliteExecutor(db);
|
|
1564
|
+
applyRecordSchema(exec);
|
|
1565
|
+
return [db, exec];
|
|
1445
1566
|
}
|
|
1446
1567
|
/**
|
|
1447
1568
|
* Initialize a new stack database. Fails if the file already exists —
|
|
@@ -1453,18 +1574,9 @@ var NativeSQLiteRecordAdapter = class _NativeSQLiteRecordAdapter {
|
|
|
1453
1574
|
`Cannot initialize: database already exists at "${opts.path}". Use NativeSQLiteRecordAdapter.open() instead.`
|
|
1454
1575
|
);
|
|
1455
1576
|
}
|
|
1456
|
-
|
|
1457
|
-
const
|
|
1458
|
-
|
|
1459
|
-
adapter.db.exec(PRAGMA_FOREIGN_KEYS_ON);
|
|
1460
|
-
adapter.db.exec(PRAGMA_JOURNAL_MODE_WAL);
|
|
1461
|
-
adapter.db.exec(RECORD_SCHEMA_SQL);
|
|
1462
|
-
adapter.db.exec(FTS5_SCHEMA_SQL);
|
|
1463
|
-
adapter.wire();
|
|
1464
|
-
insertConfigRecord(new NativeSqliteExecutor(adapter.db), opts.entityId, opts.timezone);
|
|
1465
|
-
adapter.ownerEntityId = opts.entityId;
|
|
1466
|
-
adapter.timezone = opts.timezone;
|
|
1467
|
-
return adapter;
|
|
1577
|
+
const [db, exec] = _NativeSQLiteRecordAdapter.attach(opts.path, opts.force);
|
|
1578
|
+
const config = insertConfigRecord(exec, opts.entityId, opts.timezone);
|
|
1579
|
+
return new _NativeSQLiteRecordAdapter(opts.path, db, exec, config);
|
|
1468
1580
|
}
|
|
1469
1581
|
/**
|
|
1470
1582
|
* Open an existing stack database. Fails if the file does not exist —
|
|
@@ -1476,81 +1588,8 @@ var NativeSQLiteRecordAdapter = class _NativeSQLiteRecordAdapter {
|
|
|
1476
1588
|
`Cannot open: no database found at "${opts.path}". Use NativeSQLiteRecordAdapter.initialize() to create one.`
|
|
1477
1589
|
);
|
|
1478
1590
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
adapter.db = new DatabaseSync(opts.path);
|
|
1482
|
-
adapter.db.exec(PRAGMA_FOREIGN_KEYS_ON);
|
|
1483
|
-
adapter.db.exec(PRAGMA_JOURNAL_MODE_WAL);
|
|
1484
|
-
adapter.db.exec(RECORD_SCHEMA_SQL);
|
|
1485
|
-
adapter.db.exec(FTS5_SCHEMA_SQL);
|
|
1486
|
-
adapter.wire();
|
|
1487
|
-
const config = readStackConfig(new NativeSqliteExecutor(adapter.db));
|
|
1488
|
-
adapter.ownerEntityId = config.entityId;
|
|
1489
|
-
adapter.timezone = config.timezone;
|
|
1490
|
-
return adapter;
|
|
1491
|
-
}
|
|
1492
|
-
// -------------------------------------------------------
|
|
1493
|
-
// Records
|
|
1494
|
-
// -------------------------------------------------------
|
|
1495
|
-
createRecord(record) {
|
|
1496
|
-
return this.record.createRecord(record);
|
|
1497
|
-
}
|
|
1498
|
-
getRecord(id) {
|
|
1499
|
-
return this.record.getRecord(id);
|
|
1500
|
-
}
|
|
1501
|
-
mutateRecord(id, changes, opts) {
|
|
1502
|
-
return this.record.mutateRecord(id, changes, opts);
|
|
1503
|
-
}
|
|
1504
|
-
deleteRecord(id, opts) {
|
|
1505
|
-
return this.record.deleteRecord(id, opts);
|
|
1506
|
-
}
|
|
1507
|
-
undeleteRecord(id, opts) {
|
|
1508
|
-
return this.record.undeleteRecord(id, opts);
|
|
1509
|
-
}
|
|
1510
|
-
restoreVersion(id, version, opts) {
|
|
1511
|
-
return this.record.restoreVersion(id, version, opts);
|
|
1512
|
-
}
|
|
1513
|
-
commitMigration(id, toTypeId, content, opts) {
|
|
1514
|
-
return this.record.commitMigration(id, toTypeId, content, opts);
|
|
1515
|
-
}
|
|
1516
|
-
queryRecords(query) {
|
|
1517
|
-
return this.record.queryRecords(query);
|
|
1518
|
-
}
|
|
1519
|
-
deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds) {
|
|
1520
|
-
return this.record.deleteUnreferencedAttachmentRecords(fileId, metadataTypeIds);
|
|
1521
|
-
}
|
|
1522
|
-
// -------------------------------------------------------
|
|
1523
|
-
// Versions
|
|
1524
|
-
// -------------------------------------------------------
|
|
1525
|
-
getVersions(id) {
|
|
1526
|
-
return this.record.getVersions(id);
|
|
1527
|
-
}
|
|
1528
|
-
getVersion(id, version) {
|
|
1529
|
-
return this.record.getVersion(id, version);
|
|
1530
|
-
}
|
|
1531
|
-
saveVersion(id, version) {
|
|
1532
|
-
return this.record.saveVersion(id, version);
|
|
1533
|
-
}
|
|
1534
|
-
// -------------------------------------------------------
|
|
1535
|
-
// Types
|
|
1536
|
-
// -------------------------------------------------------
|
|
1537
|
-
saveType(type) {
|
|
1538
|
-
return this.record.saveType(type);
|
|
1539
|
-
}
|
|
1540
|
-
getType(id) {
|
|
1541
|
-
return this.record.getType(id);
|
|
1542
|
-
}
|
|
1543
|
-
listTypes() {
|
|
1544
|
-
return this.record.listTypes();
|
|
1545
|
-
}
|
|
1546
|
-
// -------------------------------------------------------
|
|
1547
|
-
// Associations
|
|
1548
|
-
// -------------------------------------------------------
|
|
1549
|
-
associate(recordId, association, opts) {
|
|
1550
|
-
return this.record.associate(recordId, association, opts);
|
|
1551
|
-
}
|
|
1552
|
-
dissociate(recordId, association, opts) {
|
|
1553
|
-
return this.record.dissociate(recordId, association, opts);
|
|
1591
|
+
const [db, exec] = _NativeSQLiteRecordAdapter.attach(opts.path, opts.force);
|
|
1592
|
+
return new _NativeSQLiteRecordAdapter(opts.path, db, exec, readStackConfig(exec));
|
|
1554
1593
|
}
|
|
1555
1594
|
// -------------------------------------------------------
|
|
1556
1595
|
// Lifecycle
|