@haverstack/core 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.
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Stack — Core Stack Class
3
+ * -------------------------------------------------------
4
+ * The Stack class is the primary interface for apps. It sits
5
+ * on top of a StackAdapter and adds:
6
+ *
7
+ * - ID generation
8
+ * - Type definition and schema hashing
9
+ * - Content validation on write
10
+ * - Migration registry and auto-migration on read
11
+ * - Version snapshotting on update
12
+ * - Soft and hard delete
13
+ *
14
+ * Apps should never talk to a StackAdapter directly.
15
+ */
16
+ import type { ValidationError } from './validate.js';
17
+ import type { StackRecord, StackType, TypeSchema, TypeId, StackAdapter, StackQuery, QueryResult, Association, Permission, Migration, RecordVersion, AdapterCapabilities } from './types.js';
18
+ export type CreateRecordOptions = {
19
+ parentId?: string;
20
+ entityId?: string;
21
+ appId?: string;
22
+ permissions?: Permission[];
23
+ associations?: Association[];
24
+ };
25
+ export type GetRecordOptions = {
26
+ /** If false, return the raw stored record without auto-migrating. Default: true */
27
+ migrate?: boolean;
28
+ };
29
+ export type DeleteRecordOptions = {
30
+ /** If true, permanently remove the record and all its history. Default: false */
31
+ hard?: boolean;
32
+ };
33
+ export type DefineTypeOptions = {
34
+ migratesFrom?: TypeId;
35
+ };
36
+ export declare class StackValidationError extends Error {
37
+ readonly errors: ValidationError[];
38
+ constructor(errors: ValidationError[]);
39
+ }
40
+ export declare class StackMigrationError extends Error {
41
+ constructor(message: string);
42
+ }
43
+ export declare class Stack {
44
+ private readonly adapter;
45
+ readonly ownerEntityId: string | null;
46
+ readonly timezone: string;
47
+ private readonly migrations;
48
+ private constructor();
49
+ /**
50
+ * Create a Stack instance. Reads ownerEntityId and timezone from the
51
+ * adapter's config — the adapter is the single source of truth for
52
+ * stack-level configuration.
53
+ */
54
+ static create(adapter: StackAdapter): Promise<Stack>;
55
+ get capabilities(): AdapterCapabilities;
56
+ /**
57
+ * Define and persist a new Type. Computes the schemaHash automatically.
58
+ * Should be called at app startup before creating any records of this type.
59
+ */
60
+ defineType(id: TypeId, name: string, schema: TypeSchema, opts?: DefineTypeOptions): Promise<StackType>;
61
+ getType(id: TypeId): Promise<StackType | null>;
62
+ listTypes(): Promise<StackType[]>;
63
+ /**
64
+ * Check whether a record's type is compatible with a required schema.
65
+ * Useful for duck-typed consumption across types.
66
+ */
67
+ typeIsCompatible(typeId: TypeId, requiredSchema: TypeSchema): Promise<boolean>;
68
+ /**
69
+ * Register a migration function between two adjacent Type versions.
70
+ * Call at app startup after defineType(). The library composes
71
+ * adjacent migrations into chains automatically.
72
+ *
73
+ * Migrations run in-memory — they do not write to the adapter unless
74
+ * you call migrateAll() explicitly.
75
+ */
76
+ registerMigration(migration: Migration): void;
77
+ /**
78
+ * Find and compose a migration path from one TypeId to another.
79
+ * Returns null if no path exists.
80
+ */
81
+ private resolveMigrationPath;
82
+ /**
83
+ * Find the latest registered version of a type family.
84
+ * Follows the migration chain from the given typeId to the end.
85
+ */
86
+ private latestTypeId;
87
+ /**
88
+ * Eagerly migrate all records of a type family to the latest version,
89
+ * committing the results to disk immediately.
90
+ *
91
+ * Normally migration is lazy — records are migrated in memory on read
92
+ * and written back only when next updated. Call migrateAll() when you
93
+ * want to commit all pending migrations in one deliberate pass, for
94
+ * example before a deployment or after a major schema change.
95
+ *
96
+ * Previous content is preserved in version history before each write.
97
+ */
98
+ migrateAll(baseTypeId: string): Promise<{
99
+ migrated: number;
100
+ }>;
101
+ /**
102
+ * Create a new record. Validates content against the type's schema.
103
+ */
104
+ create<T extends Record<string, unknown> = Record<string, unknown>>(typeId: TypeId, content: T, opts?: CreateRecordOptions): Promise<StackRecord & {
105
+ content: T;
106
+ }>;
107
+ /**
108
+ * Get a record by ID.
109
+ *
110
+ * If the record is on an older type version and a migration path is
111
+ * registered, the content is migrated in memory and returned at the
112
+ * latest type version — but nothing is written to disk. Migration is
113
+ * only persisted when the record is next updated via update().
114
+ *
115
+ * Pass { migrate: false } to get the raw stored record without migration.
116
+ */
117
+ get(id: string, opts?: GetRecordOptions): Promise<StackRecord | null>;
118
+ /**
119
+ * Update a record's content. Accepts a partial content object — only the
120
+ * fields provided are changed. Omitted fields retain their current values.
121
+ *
122
+ * To remove an optional field, set it explicitly to null:
123
+ * stack.update(id, { title: null }) // removes 'title' from content
124
+ *
125
+ * If the record is on an older type version, its content is migrated
126
+ * in memory first, then the patch is applied. The updated record is
127
+ * written at the latest type version — this is when lazy migration
128
+ * is committed to disk.
129
+ *
130
+ * Validates the merged result against the type's schema. Saves the
131
+ * previous state to version history before updating.
132
+ *
133
+ * For association or permission changes, use associate(), dissociate(),
134
+ * and setPermissions() instead.
135
+ */
136
+ update(id: string, content: Record<string, unknown | null>): Promise<StackRecord>;
137
+ /**
138
+ * Add an association to a record.
139
+ * If the association already exists (same kind, label, and payload), this is a no-op.
140
+ */
141
+ associate(id: string, association: Association): Promise<void>;
142
+ /**
143
+ * Remove an association from a record.
144
+ * Matched by kind, label, and payload. No-op if not found.
145
+ */
146
+ dissociate(id: string, association: Association): Promise<void>;
147
+ /**
148
+ * Replace all permissions on a record.
149
+ * Pass an empty array to make the record private (the default).
150
+ */
151
+ setPermissions(id: string, permissions: Permission[]): Promise<void>;
152
+ /**
153
+ * Soft-delete a record (default) or hard-delete it permanently.
154
+ * Soft-deleted records are excluded from queries unless includeDeleted is set.
155
+ * Hard-deleted records and all their version history are permanently removed.
156
+ */
157
+ delete(id: string, opts?: DeleteRecordOptions): Promise<void>;
158
+ /**
159
+ * Query records. See StackQuery for filter, sort, and pagination options.
160
+ */
161
+ query(query?: StackQuery): Promise<QueryResult>;
162
+ getVersions(id: string): Promise<RecordVersion[]>;
163
+ getVersion(id: string, version: number): Promise<RecordVersion | null>;
164
+ /**
165
+ * Restore a record to a previous version by creating a new version
166
+ * with the old content. Never rewrites history.
167
+ */
168
+ restoreVersion(id: string, version: number): Promise<StackRecord>;
169
+ putAttachment(data: Uint8Array, mimeType: string): Promise<string>;
170
+ getAttachment(fileId: string): Promise<Uint8Array>;
171
+ /**
172
+ * Flush any pending writes to the underlying storage.
173
+ * For adapters that write immediately (SQLite, JSON), this is a no-op.
174
+ * For the API adapter, this commits the offline write queue to the server.
175
+ * Safe to call at any time — always resolves, never rejects on its own.
176
+ */
177
+ flush(): Promise<void>;
178
+ /**
179
+ * Release any resources held by the adapter (connections, file handles, timers).
180
+ * Call this when the stack is no longer needed — especially important for the
181
+ * API adapter, which holds an open connection and retry timers.
182
+ * Safe to call even if the adapter has no resources to release.
183
+ */
184
+ close(): Promise<void>;
185
+ private saveVersion;
186
+ }
187
+ //# sourceMappingURL=stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,UAAU,EACV,MAAM,EACN,YAAY,EACZ,UAAU,EACV,WAAW,EACX,WAAW,EACX,UAAU,EACV,SAAS,EAET,aAAa,EACb,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAMpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;CAC9B,CAAC;AAGF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,iFAAiF;IACjF,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,qBAAa,oBAAqB,SAAQ,KAAK;aACjB,MAAM,EAAE,eAAe,EAAE;gBAAzB,MAAM,EAAE,eAAe,EAAE;CAMtD;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAMD,qBAAa,KAAK;IAId,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAL3B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgC;IAE3D,OAAO;IAMP;;;;OAIG;WACU,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC;IAM1D,IAAI,YAAY,IAAI,mBAAmB,CAEtC;IAMD;;;OAGG;IACG,UAAU,CACd,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,SAAS,CAAC;IAyBf,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAI9C,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAIvC;;;OAGG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAUpF;;;;;;;OAOG;IACH,iBAAiB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAO7C;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;;;;;;;OAUG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAkDnE;;OAEG;IACG,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtE,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,CAAC,EACV,IAAI,GAAE,mBAAwB,GAC7B,OAAO,CAAC,WAAW,GAAG;QAAE,OAAO,EAAE,CAAC,CAAA;KAAE,CAAC;IA+BxC;;;;;;;;;OASG;IACG,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IA0B/E;;;;;;;;;;;;;;;;;OAiBG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IA6CvF;;;OAGG;IACG,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;;OAGG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrE;;;OAGG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1E;;;;OAIG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,mBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE;;OAEG;IACG,KAAK,CAAC,KAAK,GAAE,UAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAQnD,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAIjD,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAI5E;;;OAGG;IACG,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAyBjE,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAQxD;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAQd,WAAW;CAS1B"}
package/dist/stack.js ADDED
@@ -0,0 +1,429 @@
1
+ /**
2
+ * Stack — Core Stack Class
3
+ * -------------------------------------------------------
4
+ * The Stack class is the primary interface for apps. It sits
5
+ * on top of a StackAdapter and adds:
6
+ *
7
+ * - ID generation
8
+ * - Type definition and schema hashing
9
+ * - Content validation on write
10
+ * - Migration registry and auto-migration on read
11
+ * - Version snapshotting on update
12
+ * - Soft and hard delete
13
+ *
14
+ * Apps should never talk to a StackAdapter directly.
15
+ */
16
+ import { generateId } from './id.js';
17
+ import { hashSchema, isCompatible, parseTypeId } from './schema.js';
18
+ import { validateContent } from './validate.js';
19
+ export class StackValidationError extends Error {
20
+ errors;
21
+ constructor(errors) {
22
+ super(`Content validation failed:\n` + errors.map((e) => ` ${e.path}: ${e.message}`).join('\n'));
23
+ this.errors = errors;
24
+ this.name = 'StackValidationError';
25
+ }
26
+ }
27
+ export class StackMigrationError extends Error {
28
+ constructor(message) {
29
+ super(message);
30
+ this.name = 'StackMigrationError';
31
+ }
32
+ }
33
+ // -------------------------------------------------------
34
+ // Stack class
35
+ // -------------------------------------------------------
36
+ export class Stack {
37
+ adapter;
38
+ ownerEntityId;
39
+ timezone;
40
+ migrations = new Map();
41
+ constructor(adapter, ownerEntityId, timezone) {
42
+ this.adapter = adapter;
43
+ this.ownerEntityId = ownerEntityId;
44
+ this.timezone = timezone;
45
+ }
46
+ /**
47
+ * Create a Stack instance. Reads ownerEntityId and timezone from the
48
+ * adapter's config — the adapter is the single source of truth for
49
+ * stack-level configuration.
50
+ */
51
+ static async create(adapter) {
52
+ const entityId = await adapter.getConfig('entity_id');
53
+ const timezone = (await adapter.getConfig('timezone')) ?? 'UTC';
54
+ return new Stack(adapter, entityId, timezone);
55
+ }
56
+ get capabilities() {
57
+ return this.adapter.capabilities;
58
+ }
59
+ // -------------------------------------------------------
60
+ // Types
61
+ // -------------------------------------------------------
62
+ /**
63
+ * Define and persist a new Type. Computes the schemaHash automatically.
64
+ * Should be called at app startup before creating any records of this type.
65
+ */
66
+ async defineType(id, name, schema, opts = {}) {
67
+ const parsed = parseTypeId(id);
68
+ if (!parsed) {
69
+ throw new Error(`Invalid TypeId format: "${id}". Expected "namespace/name@version", e.g. "com.example.myapp/note@1".`);
70
+ }
71
+ const schemaHash = await hashSchema(schema);
72
+ const type = {
73
+ id,
74
+ baseId: parsed.baseId,
75
+ version: parsed.version,
76
+ name,
77
+ schema,
78
+ schemaHash,
79
+ createdAt: new Date(),
80
+ ...(opts.migratesFrom && { migratesFrom: opts.migratesFrom }),
81
+ };
82
+ await this.adapter.saveType(type);
83
+ return type;
84
+ }
85
+ async getType(id) {
86
+ return this.adapter.getType(id);
87
+ }
88
+ async listTypes() {
89
+ return this.adapter.listTypes();
90
+ }
91
+ /**
92
+ * Check whether a record's type is compatible with a required schema.
93
+ * Useful for duck-typed consumption across types.
94
+ */
95
+ async typeIsCompatible(typeId, requiredSchema) {
96
+ const type = await this.adapter.getType(typeId);
97
+ if (!type)
98
+ return false;
99
+ return isCompatible(type.schema, requiredSchema);
100
+ }
101
+ // -------------------------------------------------------
102
+ // Migration registry
103
+ // -------------------------------------------------------
104
+ /**
105
+ * Register a migration function between two adjacent Type versions.
106
+ * Call at app startup after defineType(). The library composes
107
+ * adjacent migrations into chains automatically.
108
+ *
109
+ * Migrations run in-memory — they do not write to the adapter unless
110
+ * you call migrateAll() explicitly.
111
+ */
112
+ registerMigration(migration) {
113
+ if (this.migrations.has(migration.from)) {
114
+ throw new StackMigrationError(`A migration from "${migration.from}" is already registered.`);
115
+ }
116
+ this.migrations.set(migration.from, migration);
117
+ }
118
+ /**
119
+ * Find and compose a migration path from one TypeId to another.
120
+ * Returns null if no path exists.
121
+ */
122
+ resolveMigrationPath(fromId, toId) {
123
+ if (fromId === toId)
124
+ return (content) => content;
125
+ const fns = [];
126
+ let current = fromId;
127
+ while (current !== toId) {
128
+ const migration = this.migrations.get(current);
129
+ if (!migration)
130
+ return null;
131
+ fns.push(migration.migrate);
132
+ current = migration.to;
133
+ }
134
+ return (content) => fns.reduce((c, fn) => fn(c), content);
135
+ }
136
+ /**
137
+ * Find the latest registered version of a type family.
138
+ * Follows the migration chain from the given typeId to the end.
139
+ */
140
+ latestTypeId(fromId) {
141
+ let current = fromId;
142
+ while (this.migrations.has(current)) {
143
+ current = this.migrations.get(current).to;
144
+ }
145
+ return current;
146
+ }
147
+ /**
148
+ * Eagerly migrate all records of a type family to the latest version,
149
+ * committing the results to disk immediately.
150
+ *
151
+ * Normally migration is lazy — records are migrated in memory on read
152
+ * and written back only when next updated. Call migrateAll() when you
153
+ * want to commit all pending migrations in one deliberate pass, for
154
+ * example before a deployment or after a major schema change.
155
+ *
156
+ * Previous content is preserved in version history before each write.
157
+ */
158
+ async migrateAll(baseTypeId) {
159
+ const types = await this.adapter.listTypes();
160
+ const familyTypeIds = types.filter((t) => t.baseId === baseTypeId).map((t) => t.id);
161
+ if (familyTypeIds.length === 0) {
162
+ throw new StackMigrationError(`migrateAll: no registered types found for baseTypeId "${baseTypeId}"`);
163
+ }
164
+ let migrated = 0;
165
+ for (const typeId of familyTypeIds) {
166
+ const latestId = this.latestTypeId(typeId);
167
+ if (typeId === latestId)
168
+ continue;
169
+ const migrateFn = this.resolveMigrationPath(typeId, latestId);
170
+ if (!migrateFn)
171
+ continue;
172
+ let cursor;
173
+ do {
174
+ const result = await this.adapter.queryRecords({
175
+ filter: { typeId },
176
+ limit: 100,
177
+ cursor,
178
+ });
179
+ for (const record of result.records) {
180
+ await this.saveVersion(record);
181
+ const migratedContent = migrateFn(record.content);
182
+ await this.adapter.updateRecord(record.id, {
183
+ typeId: latestId,
184
+ content: migratedContent,
185
+ updatedAt: new Date(),
186
+ version: record.version + 1,
187
+ });
188
+ migrated++;
189
+ }
190
+ cursor = result.cursor ?? undefined;
191
+ } while (cursor);
192
+ }
193
+ return { migrated };
194
+ }
195
+ // -------------------------------------------------------
196
+ // Records
197
+ // -------------------------------------------------------
198
+ /**
199
+ * Create a new record. Validates content against the type's schema.
200
+ */
201
+ async create(typeId, content, opts = {}) {
202
+ const type = await this.adapter.getType(typeId);
203
+ if (!type) {
204
+ throw new Error(`Unknown type: "${typeId}". Call defineType() first.`);
205
+ }
206
+ const errors = validateContent(content, type.schema);
207
+ if (errors.length > 0) {
208
+ throw new StackValidationError(errors);
209
+ }
210
+ const now = new Date();
211
+ const record = {
212
+ id: generateId(),
213
+ typeId,
214
+ createdAt: now,
215
+ updatedAt: now,
216
+ content,
217
+ version: 1,
218
+ ...(opts.parentId && { parentId: opts.parentId }),
219
+ ...(opts.entityId
220
+ ? { entityId: opts.entityId }
221
+ : this.ownerEntityId && { entityId: this.ownerEntityId }),
222
+ ...(opts.appId && { appId: opts.appId }),
223
+ ...(opts.permissions?.length && { permissions: opts.permissions }),
224
+ ...(opts.associations?.length && { associations: opts.associations }),
225
+ };
226
+ return this.adapter.createRecord(record);
227
+ }
228
+ /**
229
+ * Get a record by ID.
230
+ *
231
+ * If the record is on an older type version and a migration path is
232
+ * registered, the content is migrated in memory and returned at the
233
+ * latest type version — but nothing is written to disk. Migration is
234
+ * only persisted when the record is next updated via update().
235
+ *
236
+ * Pass { migrate: false } to get the raw stored record without migration.
237
+ */
238
+ async get(id, opts = {}) {
239
+ const record = await this.adapter.getRecord(id);
240
+ if (!record)
241
+ return null;
242
+ const shouldMigrate = opts.migrate !== false;
243
+ if (!shouldMigrate)
244
+ return record;
245
+ const latestId = this.latestTypeId(record.typeId);
246
+ if (latestId === record.typeId)
247
+ return record;
248
+ const migrateFn = this.resolveMigrationPath(record.typeId, latestId);
249
+ if (!migrateFn) {
250
+ console.warn(`[Stack] No migration path from "${record.typeId}" to "${latestId}" for record "${id}". ` +
251
+ `Returning raw record. Register migrations with stack.registerMigration().`);
252
+ return record;
253
+ }
254
+ return {
255
+ ...record,
256
+ typeId: latestId,
257
+ content: migrateFn(record.content),
258
+ };
259
+ }
260
+ /**
261
+ * Update a record's content. Accepts a partial content object — only the
262
+ * fields provided are changed. Omitted fields retain their current values.
263
+ *
264
+ * To remove an optional field, set it explicitly to null:
265
+ * stack.update(id, { title: null }) // removes 'title' from content
266
+ *
267
+ * If the record is on an older type version, its content is migrated
268
+ * in memory first, then the patch is applied. The updated record is
269
+ * written at the latest type version — this is when lazy migration
270
+ * is committed to disk.
271
+ *
272
+ * Validates the merged result against the type's schema. Saves the
273
+ * previous state to version history before updating.
274
+ *
275
+ * For association or permission changes, use associate(), dissociate(),
276
+ * and setPermissions() instead.
277
+ */
278
+ async update(id, content) {
279
+ const existing = await this.adapter.getRecord(id);
280
+ if (!existing) {
281
+ throw new Error(`Record not found: "${id}"`);
282
+ }
283
+ // Resolve the latest type version and migrate existing content
284
+ // in memory before applying the patch. This is when lazy migration
285
+ // from get() gets committed to disk.
286
+ const latestTypeId = this.latestTypeId(existing.typeId);
287
+ const migrateFn = this.resolveMigrationPath(existing.typeId, latestTypeId);
288
+ const existingContent = migrateFn ? migrateFn(existing.content) : existing.content;
289
+ const type = await this.adapter.getType(latestTypeId);
290
+ if (!type) {
291
+ throw new Error(`Unknown type: "${latestTypeId}"`);
292
+ }
293
+ // Shallow merge: start with (migrated) existing content, apply changes.
294
+ // null values mean "delete this field" (RFC 7396 / JSON Merge Patch).
295
+ const merged = { ...existingContent };
296
+ for (const [key, value] of Object.entries(content)) {
297
+ if (value === null) {
298
+ delete merged[key];
299
+ }
300
+ else {
301
+ merged[key] = value;
302
+ }
303
+ }
304
+ const errors = validateContent(merged, type.schema);
305
+ if (errors.length > 0) {
306
+ throw new StackValidationError(errors);
307
+ }
308
+ // Snapshot the raw stored state before overwriting
309
+ await this.saveVersion(existing);
310
+ return this.adapter.updateRecord(id, {
311
+ typeId: latestTypeId,
312
+ content: merged,
313
+ updatedAt: new Date(),
314
+ version: existing.version + 1,
315
+ });
316
+ }
317
+ /**
318
+ * Add an association to a record.
319
+ * If the association already exists (same kind, label, and payload), this is a no-op.
320
+ */
321
+ async associate(id, association) {
322
+ return this.adapter.associate(id, association);
323
+ }
324
+ /**
325
+ * Remove an association from a record.
326
+ * Matched by kind, label, and payload. No-op if not found.
327
+ */
328
+ async dissociate(id, association) {
329
+ return this.adapter.dissociate(id, association);
330
+ }
331
+ /**
332
+ * Replace all permissions on a record.
333
+ * Pass an empty array to make the record private (the default).
334
+ */
335
+ async setPermissions(id, permissions) {
336
+ const existing = await this.adapter.getRecord(id);
337
+ if (!existing) {
338
+ throw new Error(`Record not found: "${id}"`);
339
+ }
340
+ await this.adapter.updateRecord(id, { permissions });
341
+ }
342
+ /**
343
+ * Soft-delete a record (default) or hard-delete it permanently.
344
+ * Soft-deleted records are excluded from queries unless includeDeleted is set.
345
+ * Hard-deleted records and all their version history are permanently removed.
346
+ */
347
+ async delete(id, opts = {}) {
348
+ return this.adapter.deleteRecord(id, opts);
349
+ }
350
+ /**
351
+ * Query records. See StackQuery for filter, sort, and pagination options.
352
+ */
353
+ async query(query = {}) {
354
+ return this.adapter.queryRecords(query);
355
+ }
356
+ // -------------------------------------------------------
357
+ // Versions
358
+ // -------------------------------------------------------
359
+ async getVersions(id) {
360
+ return this.adapter.getVersions(id);
361
+ }
362
+ async getVersion(id, version) {
363
+ return this.adapter.getVersion(id, version);
364
+ }
365
+ /**
366
+ * Restore a record to a previous version by creating a new version
367
+ * with the old content. Never rewrites history.
368
+ */
369
+ async restoreVersion(id, version) {
370
+ const existing = await this.adapter.getRecord(id);
371
+ if (!existing) {
372
+ throw new Error(`Record not found: "${id}"`);
373
+ }
374
+ const target = await this.adapter.getVersion(id, version);
375
+ if (!target) {
376
+ throw new Error(`Version ${version} not found for record "${id}"`);
377
+ }
378
+ // Snapshot current state before restoring
379
+ await this.saveVersion(existing);
380
+ return this.adapter.updateRecord(id, {
381
+ content: target.content,
382
+ updatedAt: new Date(),
383
+ version: existing.version + 1,
384
+ });
385
+ }
386
+ // -------------------------------------------------------
387
+ // Attachments
388
+ // -------------------------------------------------------
389
+ async putAttachment(data, mimeType) {
390
+ return this.adapter.putAttachment(data, mimeType);
391
+ }
392
+ async getAttachment(fileId) {
393
+ return this.adapter.getAttachment(fileId);
394
+ }
395
+ // -------------------------------------------------------
396
+ // Lifecycle
397
+ // -------------------------------------------------------
398
+ /**
399
+ * Flush any pending writes to the underlying storage.
400
+ * For adapters that write immediately (SQLite, JSON), this is a no-op.
401
+ * For the API adapter, this commits the offline write queue to the server.
402
+ * Safe to call at any time — always resolves, never rejects on its own.
403
+ */
404
+ async flush() {
405
+ await this.adapter.flush?.();
406
+ }
407
+ /**
408
+ * Release any resources held by the adapter (connections, file handles, timers).
409
+ * Call this when the stack is no longer needed — especially important for the
410
+ * API adapter, which holds an open connection and retry timers.
411
+ * Safe to call even if the adapter has no resources to release.
412
+ */
413
+ async close() {
414
+ await this.adapter.close?.();
415
+ }
416
+ // -------------------------------------------------------
417
+ // Private helpers
418
+ // -------------------------------------------------------
419
+ async saveVersion(record) {
420
+ const version = {
421
+ version: record.version,
422
+ content: record.content,
423
+ updatedAt: record.updatedAt,
424
+ ...(record.entityId && { entityId: record.entityId }),
425
+ };
426
+ await this.adapter.saveVersion(record.id, version);
427
+ }
428
+ }
429
+ //# sourceMappingURL=stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"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"}