@haverstack/record-adapter-sqlite 0.1.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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
2
+
3
+ To the extent possible under law, the Haverstack contributors have waived all
4
+ copyright and related or neighboring rights to Haverstack. This work is
5
+ published from the United States.
6
+
7
+ https://creativecommons.org/publicdomain/zero/1.0/
@@ -0,0 +1,162 @@
1
+ import { TokenSession, StackRecord, RecordVersion, Permission, TypeId, StackQuery, QueryResult, FileId, RecordId, StackType, Association } from '@haverstack/core';
2
+ import { StackRecordAdapter, AdapterCapabilities } from '@haverstack/core/adapter';
3
+ import { StackTokenStore, TokenInfo } from '@haverstack/core/wire';
4
+
5
+ /**
6
+ * Bearer-token storage backing @haverstack/core's StackTokenStore,
7
+ * implemented against its own file — deliberately separate from
8
+ * NativeSQLiteRecordAdapter's records database. The portable stack file
9
+ * is "your data, take it with you"; auth material shouldn't travel with
10
+ * it, and restoring a backup shouldn't resurrect revoked tokens. Server
11
+ * implementations wire these up as separate parts
12
+ * (`{ adapter, tokens }`), the same philosophy as combineAdapters().
13
+ *
14
+ * The actual createToken/lookupToken/listTokens/revokeToken logic lives
15
+ * in @haverstack/sqlite-shared's SharedTokenLogic, reached through the
16
+ * SqlExecutor interface.
17
+ */
18
+
19
+ /** Conventional sibling path for a token store beside a stack's main .db file. */
20
+ declare const defaultTokenStorePath: (dbPath: string) => string;
21
+ type NativeTokenStoreOptions = {
22
+ /** Absolute path to the token store file. Created if it doesn't exist. */
23
+ path: string;
24
+ /** Bypass the storage-ownership lock check. See docs/spec/adapters.md § Concurrency & storage ownership. */
25
+ force?: boolean;
26
+ };
27
+ declare class NativeTokenStore implements StackTokenStore {
28
+ private readonly path;
29
+ private db;
30
+ private tokens;
31
+ private constructor();
32
+ /** Opens the token store, creating the file and schema if needed. */
33
+ static open(opts: NativeTokenStoreOptions): Promise<NativeTokenStore>;
34
+ createToken(principalId: string, opts?: {
35
+ onBehalfOf?: string;
36
+ label?: string;
37
+ expiresAt?: Date;
38
+ }): Promise<{
39
+ id: string;
40
+ token: string;
41
+ }>;
42
+ lookupToken(token: string): Promise<TokenSession | null>;
43
+ listTokens(): Promise<TokenInfo[]>;
44
+ revokeToken(id: string): Promise<void>;
45
+ close(): Promise<void>;
46
+ }
47
+
48
+ /**
49
+ * Haverstack — Native SQLite Record Adapter
50
+ * -------------------------------------------------------
51
+ * Implements StackRecordAdapter using node:sqlite (Node's built-in
52
+ * SQLite binding, Node >= 22.5). No native compilation, no node-gyp,
53
+ * no prebuilt binaries, and writes go straight to the file via normal
54
+ * SQLite journaling (WAL mode) — no whole-database rewrite per write,
55
+ * no in-memory copy of the whole store, real OS-level file locking.
56
+ * Full-text search uses FTS5.
57
+ *
58
+ * A stack file is owned by exactly one process at a time (see
59
+ * docs/spec/adapters.md § Concurrency & storage ownership). open()/initialize()
60
+ * acquire a PID-stamped lock file beside the database and reject if
61
+ * another live process already holds it; close() releases it.
62
+ *
63
+ * Token storage is a separate concern — see NativeTokenStore in this
64
+ * package, which implements @haverstack/core's StackTokenStore against
65
+ * its own file rather than this adapter's records database.
66
+ *
67
+ * This class itself is a thin node:sqlite binding: schema setup,
68
+ * pragmas, and the storage-ownership lock are genuinely engine-specific
69
+ * and live here. The actual StackRecordAdapter logic lives in
70
+ * @haverstack/sqlite-shared's SharedSqlRecordLogic, reached through the
71
+ * SqlExecutor interface (NativeSqliteExecutor here normalizes
72
+ * node:sqlite's spread-args run/get/all calls to it).
73
+ */
74
+
75
+ type NativeRecordInitializeOptions = {
76
+ /** Absolute path to the .db file. Must not already exist. */
77
+ path: string;
78
+ /** IANA timezone string e.g. "America/New_York". Optional passthrough app metadata — no default. */
79
+ timezone?: string;
80
+ /** Entity ID of the stack owner. */
81
+ entityId: string;
82
+ /** Bypass the storage-ownership lock check. See NativeRecordOpenOptions.force. */
83
+ force?: boolean;
84
+ };
85
+ type NativeRecordOpenOptions = {
86
+ /** Absolute path to an existing .db file. */
87
+ path: string;
88
+ /**
89
+ * Open even if a lock file from another live process is present.
90
+ * Only needed if that process is gone but its PID was reused by
91
+ * something else (the automatic stale-lock check already reclaims
92
+ * locks whose owning process is no longer running).
93
+ */
94
+ force?: boolean;
95
+ };
96
+ declare class NativeSQLiteRecordAdapter implements StackRecordAdapter {
97
+ private readonly path;
98
+ readonly capabilities: AdapterCapabilities;
99
+ ownerEntityId: string;
100
+ timezone: string | undefined;
101
+ private db;
102
+ private record;
103
+ private constructor();
104
+ private wire;
105
+ /**
106
+ * Initialize a new stack database. Fails if the file already exists —
107
+ * use open() for existing databases.
108
+ */
109
+ static initialize(opts: NativeRecordInitializeOptions): Promise<NativeSQLiteRecordAdapter>;
110
+ /**
111
+ * Open an existing stack database. Fails if the file does not exist —
112
+ * use initialize() for new databases.
113
+ */
114
+ static open(opts: NativeRecordOpenOptions): Promise<NativeSQLiteRecordAdapter>;
115
+ createRecord(record: StackRecord): Promise<StackRecord>;
116
+ getRecord(id: string): Promise<StackRecord | null>;
117
+ patchContent(id: string, patch: Record<string, unknown | null>, opts?: {
118
+ expectedVersion?: number;
119
+ snapshot?: RecordVersion;
120
+ }): Promise<StackRecord>;
121
+ deleteRecord(id: string, opts?: {
122
+ hard?: boolean;
123
+ expectedVersion?: number;
124
+ snapshot?: RecordVersion;
125
+ }): Promise<void>;
126
+ undeleteRecord(id: string, opts?: {
127
+ expectedVersion?: number;
128
+ snapshot?: RecordVersion;
129
+ }): Promise<StackRecord>;
130
+ setPermissions(id: string, permissions: Permission[], opts?: {
131
+ expectedVersion?: number;
132
+ snapshot?: RecordVersion;
133
+ }): Promise<void>;
134
+ restoreVersion(id: string, version: number, opts?: {
135
+ expectedVersion?: number;
136
+ snapshot?: RecordVersion;
137
+ }): Promise<StackRecord>;
138
+ commitMigration(id: string, toTypeId: TypeId, content: Record<string, unknown>, opts?: {
139
+ snapshot?: RecordVersion;
140
+ }): Promise<StackRecord>;
141
+ queryRecords(query: StackQuery): Promise<QueryResult>;
142
+ deleteUnreferencedAttachmentRecords(fileId: FileId, metadataTypeId: TypeId): Promise<RecordId[]>;
143
+ getVersions(id: string): Promise<RecordVersion[]>;
144
+ getVersion(id: string, version: number): Promise<RecordVersion | null>;
145
+ saveVersion(id: string, version: RecordVersion): Promise<void>;
146
+ saveType(type: StackType): Promise<void>;
147
+ getType(id: TypeId): Promise<StackType | null>;
148
+ listTypes(): Promise<StackType[]>;
149
+ associate(recordId: string, association: Association, opts?: {
150
+ expectedVersion?: number;
151
+ snapshot?: RecordVersion;
152
+ }): Promise<void>;
153
+ dissociate(recordId: string, association: Association, opts?: {
154
+ expectedVersion?: number;
155
+ snapshot?: RecordVersion;
156
+ }): Promise<void>;
157
+ /** Folds the WAL back into the main file — useful before copying/backing up the database. */
158
+ flush(): Promise<void>;
159
+ close(): Promise<void>;
160
+ }
161
+
162
+ export { type NativeRecordInitializeOptions, type NativeRecordOpenOptions, NativeSQLiteRecordAdapter, NativeTokenStore, type NativeTokenStoreOptions, defaultTokenStorePath };