@byearlybird/starling 0.4.0 → 0.5.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
@@ -75,9 +75,14 @@ declare const create$1: (iterable?: Iterable<readonly [string, EncodedDocument]>
75
75
  };
76
76
  };
77
77
  declare namespace store_d_exports {
78
- export { Plugin, PluginHandle, PluginMethods, Store as StarlingStore, StoreHooks, StoreOnBeforeDelete, StoreOnBeforePatch, StoreOnBeforePut, StoreOnDelete, StoreOnPatch, StoreOnPut, StoreTransaction, create };
78
+ export { DeepPartial, NotPromise, Plugin, PluginMethods, Store as StarlingStore, StoreHooks, StoreOnDelete, StoreOnPatch, StoreOnPut, StorePutOptions, StoreSetTransaction, create };
79
79
  }
80
80
  type DeepPartial<T$1> = T$1 extends object ? { [P in keyof T$1]?: DeepPartial<T$1[P]> } : T$1;
81
+ /**
82
+ * Type constraint to prevent Promise returns from set callbacks.
83
+ * Transactions must be synchronous operations.
84
+ */
85
+ type NotPromise<T$1> = T$1 extends Promise<any> ? never : T$1;
81
86
  /**
82
87
  * Called once per commit with all put operations accumulated as decoded entries.
83
88
  * Only fires if at least one put occurred.
@@ -93,68 +98,47 @@ type StoreOnPatch<T$1> = (entries: ReadonlyArray<readonly [string, T$1]>) => voi
93
98
  * Only fires if at least one delete occurred.
94
99
  */
95
100
  type StoreOnDelete = (keys: ReadonlyArray<string>) => void;
96
- /**
97
- * Called before a put operation is applied.
98
- * Throws to reject the operation.
99
- */
100
- type StoreOnBeforePut<T$1> = (key: string, value: T$1) => void;
101
- /**
102
- * Called before a patch operation is applied.
103
- * Throws to reject the operation.
104
- */
105
- type StoreOnBeforePatch<T$1> = (key: string, value: DeepPartial<T$1>) => void;
106
- /**
107
- * Called before a delete operation is applied.
108
- * Throws to reject the operation.
109
- */
110
- type StoreOnBeforeDelete = (key: string) => void;
111
101
  /**
112
102
  * Hook callbacks that receive batches of decoded entries.
113
103
  * Hooks fire on commit only, never during staged operations.
114
104
  * Arrays are readonly to prevent external mutation.
115
105
  */
116
106
  type StoreHooks<T$1> = {
117
- onBeforePut?: StoreOnBeforePut<T$1>;
118
- onBeforePatch?: StoreOnBeforePatch<T$1>;
119
- onBeforeDelete?: StoreOnBeforeDelete;
120
107
  onPut?: StoreOnPut<T$1>;
121
108
  onPatch?: StoreOnPatch<T$1>;
122
109
  onDelete?: StoreOnDelete;
123
110
  };
124
- type StoreTransaction<T$1> = {
125
- put: (key: string, value: T$1) => void;
111
+ type StorePutOptions = {
112
+ withId?: string;
113
+ };
114
+ type StoreSetTransaction<T$1> = {
115
+ put: (value: T$1, options?: StorePutOptions) => string;
126
116
  patch: (key: string, value: DeepPartial<T$1>) => void;
127
117
  merge: (doc: EncodedDocument) => void;
128
118
  del: (key: string) => void;
129
- has: (key: string) => boolean;
130
- commit: (opts?: {
131
- silent: boolean;
132
- }) => void;
119
+ get: (key: string) => T$1 | null;
133
120
  rollback: () => void;
134
121
  };
135
122
  type PluginMethods = Record<string, (...args: any[]) => any>;
136
- type PluginHandle<T$1, M$1 extends PluginMethods = {}> = {
137
- init: () => Promise<void> | void;
123
+ type Plugin<T$1, M$1 extends PluginMethods = {}> = {
124
+ init: (store: Store<T$1>) => Promise<void> | void;
138
125
  dispose: () => Promise<void> | void;
139
126
  hooks?: StoreHooks<T$1>;
140
127
  methods?: M$1;
141
128
  };
142
- type Plugin<T$1, M$1 extends PluginMethods = {}> = (store: Store<T$1, any>) => PluginHandle<T$1, M$1>;
143
129
  type Store<T$1, Extended = {}> = {
144
130
  get: (key: string) => T$1 | null;
145
- has: (key: string) => boolean;
146
- readonly size: number;
147
- values: () => IterableIterator<T$1>;
131
+ set: <R = void>(callback: (tx: StoreSetTransaction<T$1>) => NotPromise<R>, opts?: {
132
+ silent?: boolean;
133
+ }) => NotPromise<R>;
148
134
  entries: () => IterableIterator<readonly [string, T$1]>;
149
135
  snapshot: () => EncodedDocument[];
150
- put: (key: string, value: T$1) => void;
151
- patch: (key: string, value: DeepPartial<T$1>) => void;
152
- del: (key: string) => void;
153
- begin: () => StoreTransaction<T$1>;
154
136
  use: <M extends PluginMethods>(plugin: Plugin<T$1, M>) => Store<T$1, Extended & M>;
155
137
  init: () => Promise<Store<T$1, Extended>>;
156
138
  dispose: () => Promise<void>;
157
139
  } & Extended;
158
- declare const create: <T>() => Store<T, {}>;
140
+ declare const create: <T>(config?: {
141
+ getId?: () => string;
142
+ }) => Store<T, {}>;
159
143
  //#endregion
160
144
  export { type clock_d_exports as Clock, document_d_exports as Document, eventstamp_d_exports as Eventstamp, kv_d_exports as KV, record_d_exports as Record, store_d_exports as Store, value_d_exports as Value };
package/dist/index.js CHANGED
@@ -226,20 +226,18 @@ const create$1 = (iterable) => {
226
226
  //#endregion
227
227
  //#region src/store.ts
228
228
  var store_exports = /* @__PURE__ */ __export({ create: () => create });
229
- const create = () => {
229
+ const create = (config = {}) => {
230
230
  const kv = create$1();
231
231
  const clock = create$2();
232
+ const initializers = /* @__PURE__ */ new Set();
233
+ const disposers = /* @__PURE__ */ new Set();
234
+ const getId = config.getId ?? (() => crypto.randomUUID());
232
235
  const encodeValue = (key, value) => encode(key, value, clock.now());
233
236
  const listeners = {
234
- beforePut: /* @__PURE__ */ new Set(),
235
- beforePatch: /* @__PURE__ */ new Set(),
236
- beforeDel: /* @__PURE__ */ new Set(),
237
237
  put: /* @__PURE__ */ new Set(),
238
238
  patch: /* @__PURE__ */ new Set(),
239
239
  del: /* @__PURE__ */ new Set()
240
240
  };
241
- const initializers = /* @__PURE__ */ new Set();
242
- const disposers = /* @__PURE__ */ new Set();
243
241
  const decodeActive = (doc) => {
244
242
  if (!doc || doc["~deletedAt"]) return null;
245
243
  return decode(doc)["~data"];
@@ -248,18 +246,6 @@ const create = () => {
248
246
  get(key) {
249
247
  return decodeActive(kv.get(key));
250
248
  },
251
- has(key) {
252
- return decodeActive(kv.get(key)) !== null;
253
- },
254
- values() {
255
- function* iterator() {
256
- for (const doc of kv.values()) {
257
- const data = decodeActive(doc);
258
- if (data !== null) yield data;
259
- }
260
- }
261
- return iterator();
262
- },
263
249
  entries() {
264
250
  function* iterator() {
265
251
  for (const [key, doc] of kv.entries()) {
@@ -272,45 +258,27 @@ const create = () => {
272
258
  snapshot() {
273
259
  return Array.from(kv.values());
274
260
  },
275
- get size() {
276
- let count = 0;
277
- for (const doc of kv.values()) if (doc && !doc["~deletedAt"]) count++;
278
- return count;
279
- },
280
- put(key, value) {
281
- const tx = this.begin();
282
- tx.put(key, value);
283
- tx.commit();
284
- },
285
- patch(key, value) {
286
- const tx = this.begin();
287
- tx.patch(key, value);
288
- tx.commit();
289
- },
290
- del(key) {
291
- const tx = this.begin();
292
- tx.del(key);
293
- tx.commit();
294
- },
295
- begin() {
261
+ set(callback, opts) {
296
262
  const tx = kv.begin();
263
+ const silent = opts?.silent ?? false;
297
264
  const putKeyValues = [];
298
265
  const patchKeyValues = [];
299
266
  const deleteKeys = [];
300
- return {
301
- put(key, value) {
302
- for (const fn of listeners.beforePut) fn(key, value);
267
+ let rolledBack = false;
268
+ const setTransaction = {
269
+ put(value, options) {
270
+ const key = options?.withId ?? getId();
303
271
  tx.put(key, encodeValue(key, value));
304
272
  putKeyValues.push([key, value]);
273
+ return key;
305
274
  },
306
275
  patch(key, value) {
307
- for (const fn of listeners.beforePatch) fn(key, value);
308
276
  tx.patch(key, encode(key, value, clock.now()));
309
277
  const merged = decodeActive(tx.get(key));
310
278
  if (merged) patchKeyValues.push([key, merged]);
311
279
  },
312
280
  merge(doc) {
313
- if (tx.has(doc["~id"])) tx.patch(doc["~id"], doc);
281
+ if (tx.get(doc["~id"])) tx.patch(doc["~id"], doc);
314
282
  else tx.put(doc["~id"], doc);
315
283
  const currentDoc = tx.get(doc["~id"]);
316
284
  if (currentDoc && !currentDoc["~deletedAt"]) {
@@ -319,50 +287,36 @@ const create = () => {
319
287
  }
320
288
  },
321
289
  del(key) {
322
- for (const fn of listeners.beforeDel) fn(key);
323
290
  if (!tx.get(key)) return;
324
291
  tx.del(key, clock.now());
325
292
  deleteKeys.push(key);
326
293
  },
327
- has(key) {
328
- return tx.has(key);
329
- },
330
- commit(opts = { silent: false }) {
331
- tx.commit();
332
- if (opts.silent) return;
333
- if (putKeyValues.length > 0) for (const fn of listeners.put) fn(Object.freeze([...putKeyValues]));
334
- if (patchKeyValues.length > 0) for (const fn of listeners.patch) fn(Object.freeze([...patchKeyValues]));
335
- if (deleteKeys.length > 0) for (const fn of listeners.del) fn(Object.freeze([...deleteKeys]));
294
+ get(key) {
295
+ return decodeActive(tx.get(key));
336
296
  },
337
297
  rollback() {
298
+ rolledBack = true;
338
299
  tx.rollback();
339
300
  }
340
301
  };
302
+ try {
303
+ const result = callback(setTransaction);
304
+ if (rolledBack) return result;
305
+ tx.commit();
306
+ if (!silent) {
307
+ if (putKeyValues.length > 0) for (const fn of listeners.put) fn(Object.freeze([...putKeyValues]));
308
+ if (patchKeyValues.length > 0) for (const fn of listeners.patch) fn(Object.freeze([...patchKeyValues]));
309
+ if (deleteKeys.length > 0) for (const fn of listeners.del) fn(Object.freeze([...deleteKeys]));
310
+ }
311
+ return result;
312
+ } catch (error) {
313
+ tx.rollback();
314
+ throw error;
315
+ }
341
316
  },
342
317
  use(plugin) {
343
- const { hooks: pluginHooks, init, dispose, methods } = plugin(this);
318
+ const { hooks: pluginHooks, init, dispose, methods } = plugin;
344
319
  if (pluginHooks) {
345
- if (pluginHooks.onBeforePut) {
346
- const callback = pluginHooks.onBeforePut;
347
- listeners.beforePut.add(callback);
348
- disposers.add(() => {
349
- listeners.beforePut.delete(callback);
350
- });
351
- }
352
- if (pluginHooks.onBeforePatch) {
353
- const callback = pluginHooks.onBeforePatch;
354
- listeners.beforePatch.add(callback);
355
- disposers.add(() => {
356
- listeners.beforePatch.delete(callback);
357
- });
358
- }
359
- if (pluginHooks.onBeforeDelete) {
360
- const callback = pluginHooks.onBeforeDelete;
361
- listeners.beforeDel.add(callback);
362
- disposers.add(() => {
363
- listeners.beforeDel.delete(callback);
364
- });
365
- }
366
320
  if (pluginHooks.onPut) {
367
321
  const callback = pluginHooks.onPut;
368
322
  listeners.put.add(callback);
@@ -391,7 +345,7 @@ const create = () => {
391
345
  return this;
392
346
  },
393
347
  async init() {
394
- for (const fn of initializers) await fn();
348
+ for (const fn of initializers) await fn(this);
395
349
  return this;
396
350
  },
397
351
  async dispose() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byearlybird/starling",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",