marcidb-client 0.13.0 → 0.14.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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "marcidb-client",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "den59k"
@@ -207,6 +207,15 @@ type MarciOp = {
207
207
  query?: any;
208
208
  data?: any;
209
209
  id?: any;
210
+ journal?: JournalArgs;
211
+ };
212
+ /** The arguments of a journal action (`$journalOpen` / `$journalRead` / `$journalDrop`). `wait` is in seconds. */
213
+ type JournalArgs = {
214
+ name: string;
215
+ on?: readonly JournalOp[];
216
+ after?: number;
217
+ limit?: number;
218
+ wait?: number;
210
219
  };
211
220
  type MarciTransport = {
212
221
  exec(op: MarciOp): Promise<any>;
@@ -311,13 +320,47 @@ interface Query<
311
320
  /** @deprecated use `.select(query).first()` (removed in the next minor). */
312
321
  findFirst<Q extends T["query"] = {}>(query?: Q): Op<Rows<T, Q> | null>;
313
322
  }
314
- /** `db.<model>`: the root query, plus `reindex()` for models with a `@custom` (vector / full-text) index. */
323
+ /** What a journal can record. Only deletes so far. */
324
+ type JournalOp = "delete";
325
+ /** One recorded change. `row` is the row as it last was: its id and every scalar field. */
326
+ type JournalEntry<
327
+ T extends ModelTypes,
328
+ O extends JournalOp = JournalOp
329
+ > = {
330
+ seq: number;
331
+ op: O;
332
+ row: Rows<T, T["scalars"]>;
333
+ };
334
+ type JournalOptions<O extends JournalOp> = {
335
+ /** The operations to record. A journal that exists with other ones is an error, not a redefinition. */
336
+ on: O | readonly O[];
337
+ /** `false`: the loop ends when the journal is read through. By default it waits for the next entry. */
338
+ wait?: boolean;
339
+ };
340
+ /**
341
+ * A named, durable log of a model's changes. It starts recording when it is first asked for and keeps
342
+ * every entry until the loop that reads it has moved past it — an entry whose loop body threw, or that
343
+ * was never reached, is delivered again to the next reader of the same name.
344
+ */
345
+ interface Journal<
346
+ T extends ModelTypes,
347
+ O extends JournalOp = JournalOp
348
+ > extends AsyncIterable<JournalEntry<T, O>> {
349
+ /** Drops the journal with whatever it still holds; the model's writes stop paying for it. */
350
+ drop(): Promise<void>;
351
+ }
352
+ /**
353
+ * `db.<model>`: the root query, plus `reindex()` for models with a `@custom` (vector / full-text) index and
354
+ * `$journal(name, { on })` — the journal of this model's changes under that name, created on first use.
355
+ */
315
356
  type Collection<T extends ModelTypes> = Query<T> & (T["reindex"] extends true ? {
316
357
  reindex(): Op<{
317
358
  ok: boolean;
318
359
  indexed: number;
319
360
  }>;
320
- } : {});
361
+ } : {}) & {
362
+ $journal<O extends JournalOp>(name: string, options: JournalOptions<O>): Journal<T, O>;
363
+ };
321
364
  /** One field of a model, in slot order: `n` name, `k` key | body | one | many, `m` the relation's target model. */
322
365
  type FieldDesc = {
323
366
  n: string;
@@ -365,4 +408,4 @@ declare function createDecoderRegistry(MODELS: Record<string, FieldDesc[]>): {
365
408
  decodeBuffer(decoder: (reader: any) => any, bytes: Uint8Array, many: boolean): any;
366
409
  };
367
410
  declare function encodeId(id: any): string;
368
- export { requestBinary, request, encodeId, createQueryLayer, createDecoderRegistry, WhereValue, VectorSearch, ValueOps, UpdateNumValue, Sub, StrOps, ServiceKeys, Rows, Resolve, RefUpdateStruct, RefUpdate, RefListUpdateStruct, RefListUpdateOrdered, RefListUpdate, QueryLayerOptions, Query, PrimitiveListUpdate, Op, Only, NumOps, Never, ModelsMeta, ModelTypes, MarciTransport, MarciOp, JsonValue, JsonType, JsonPathWhere, JsonCondition, IdArg, GetResult, FullTextSearch, FieldOps, FieldDesc, Effective, CustomSearchValue, CustomSearch, CompareValue, CompareStrValue, CompareRefValue, CompareRefListValue, CompareNumValue, Collection, BareId, AggregateResult, AggregateKeys };
411
+ export { AggregateKeys, AggregateResult, BareId, Collection, CompareNumValue, CompareRefListValue, CompareRefValue, CompareStrValue, CompareValue, CustomSearch, CustomSearchValue, Effective, FieldDesc, FieldOps, FullTextSearch, GetResult, IdArg, Journal, JournalArgs, JournalEntry, JournalOp, JournalOptions, JsonCondition, JsonPathWhere, JsonType, JsonValue, MarciOp, MarciTransport, ModelTypes, ModelsMeta, Never, NumOps, Only, Op, PrimitiveListUpdate, Query, QueryLayerOptions, RefListUpdate, RefListUpdateOrdered, RefListUpdateStruct, RefUpdate, RefUpdateStruct, Resolve, Rows, ServiceKeys, StrOps, Sub, UpdateNumValue, ValueOps, VectorSearch, WhereValue, createDecoderRegistry, createQueryLayer, encodeId, request, requestBinary };
package/runtime/index.js CHANGED
@@ -44,6 +44,39 @@ function createQueryLayer(options) {
44
44
  Object.assign(out, scalarSelect(model));
45
45
  return out;
46
46
  };
47
+ const journal = (model, name, options) => {
48
+ const on = Array.isArray(options.on) ? options.on : [options.on];
49
+ const wait = options.wait === false ? 0 : 30;
50
+ const opened = run({ model, action: "$journalOpen", journal: { name, on } });
51
+ opened.catch(() => {});
52
+ const read = (after, limit, wait) => run({ model, action: "$journalRead", journal: { name, after, limit, wait } });
53
+ return {
54
+ async* [Symbol.asyncIterator]() {
55
+ await opened;
56
+ let done;
57
+ let confirmed;
58
+ try {
59
+ for (;; ) {
60
+ const entries = await read(done, 100, wait);
61
+ confirmed = done;
62
+ if (entries.length === 0) {
63
+ if (wait === 0)
64
+ return;
65
+ continue;
66
+ }
67
+ for (const entry of entries) {
68
+ yield entry;
69
+ done = entry.seq;
70
+ }
71
+ }
72
+ } finally {
73
+ if (done !== undefined && done !== confirmed)
74
+ await read(done, 1, 0).catch(() => {});
75
+ }
76
+ },
77
+ drop: () => opened.catch(() => {}).then(() => run({ model, action: "$journalDrop", journal: { name } })).then(() => {})
78
+ };
79
+ };
47
80
  const collection = (model) => {
48
81
  const make = (st) => {
49
82
  const build = (query) => {
@@ -105,6 +138,7 @@ function createQueryLayer(options) {
105
138
  return op({ model, action: "deleteMany", query: whereOnly() });
106
139
  },
107
140
  reindex: () => op({ model, action: "$reindex" }),
141
+ $journal: (name, options) => journal(model, name, options),
108
142
  findMany: (query) => op({ model, action: "findMany", query: build(query) }),
109
143
  findFirst: (query) => op({ model, action: "findFirst", query: build(query) })
110
144
  };
@@ -343,9 +377,9 @@ function encodeId(id) {
343
377
  return String(id);
344
378
  }
345
379
  export {
346
- requestBinary,
347
- request,
348
- encodeId,
380
+ createDecoderRegistry,
349
381
  createQueryLayer,
350
- createDecoderRegistry
382
+ encodeId,
383
+ request,
384
+ requestBinary
351
385
  };