@feltdb/core 0.11.8 → 0.11.9

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.
Files changed (57) hide show
  1. package/dist/cli/index.js +0 -0
  2. package/dist/composition.d.ts +697 -0
  3. package/dist/composition.d.ts.map +1 -0
  4. package/dist/composition.js +1218 -0
  5. package/dist/create/package-versions.js +1 -1
  6. package/dist/create/server-source/crates/feltdb/src/lib.rs +363 -2
  7. package/dist/create/server-source/crates/feltdb/src/read_basis.rs +257 -0
  8. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +442 -73
  9. package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +18 -4
  10. package/dist/create/server-source/crates/feltdb-server/src/main.rs +198 -10
  11. package/dist/db.d.ts +12 -1
  12. package/dist/db.d.ts.map +1 -1
  13. package/dist/db.js +19 -1
  14. package/dist/embedded-transaction.d.ts +21 -1
  15. package/dist/embedded-transaction.d.ts.map +1 -1
  16. package/dist/embedded-transaction.js +96 -0
  17. package/dist/feltdb.d.ts +8 -0
  18. package/dist/feltdb.d.ts.map +1 -1
  19. package/dist/file-db.d.ts +8 -1
  20. package/dist/file-db.d.ts.map +1 -1
  21. package/dist/file-db.js +20 -1
  22. package/dist/flowspec.d.ts +7 -0
  23. package/dist/flowspec.d.ts.map +1 -1
  24. package/dist/flowspec.js +13 -1
  25. package/dist/http-db.d.ts +7 -0
  26. package/dist/http-db.d.ts.map +1 -1
  27. package/dist/http-db.js +38 -6
  28. package/dist/index-core.d.ts +1 -0
  29. package/dist/index-core.d.ts.map +1 -1
  30. package/dist/index-core.js +1 -0
  31. package/dist/indexeddb-db.d.ts +3 -1
  32. package/dist/indexeddb-db.d.ts.map +1 -1
  33. package/dist/indexeddb-db.js +50 -2
  34. package/dist/intent.d.ts +4 -2
  35. package/dist/intent.d.ts.map +1 -1
  36. package/dist/memory-db.d.ts +2 -1
  37. package/dist/memory-db.d.ts.map +1 -1
  38. package/dist/memory-db.js +13 -1
  39. package/dist/proposal.d.ts +5 -0
  40. package/dist/proposal.d.ts.map +1 -1
  41. package/dist/proposal.js +6 -2
  42. package/dist/state-impact.d.ts +245 -0
  43. package/dist/state-impact.d.ts.map +1 -0
  44. package/dist/state-impact.js +404 -0
  45. package/dist/studio-app/assets/{feltdb_wasm-D58hv7aa.js → feltdb_wasm-DGGqm9GK.js} +1 -1
  46. package/dist/studio-app/assets/feltdb_wasm_bg-BYJo87WY.wasm +0 -0
  47. package/dist/studio-app/assets/index-qLeZcPW_.js +30 -0
  48. package/dist/studio-app/index.html +1 -1
  49. package/dist/transaction.d.ts +61 -1
  50. package/dist/transaction.d.ts.map +1 -1
  51. package/dist/transaction.js +59 -0
  52. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  53. package/package.json +1 -1
  54. package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
  55. package/dist/create/server-source/crates/feltdb-wasm/test_wasm.db.lock +0 -0
  56. package/dist/studio-app/assets/feltdb_wasm_bg-BgFXYOtc.wasm +0 -0
  57. package/dist/studio-app/assets/index-Ccecp6rc.js +0 -30
@@ -1,3 +1,3 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#17211b"/><title>FeltDB Studio</title> <script type="module" crossorigin src="/assets/index-Ccecp6rc.js"></script>
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#17211b"/><title>FeltDB Studio</title> <script type="module" crossorigin src="/assets/index-qLeZcPW_.js"></script>
2
2
  <link rel="stylesheet" crossorigin href="/assets/index-BrsF-kWL.css">
3
3
  </head><body><div id="root"></div></body></html>
@@ -102,8 +102,48 @@ export interface AtomicTransactionOperationRequest {
102
102
  export interface AtomicTransactionDocument {
103
103
  transactionId?: string;
104
104
  preconditions?: AtomicTransactionPrecondition[];
105
+ fences?: TransactionReadBasis[];
105
106
  operations: AtomicTransactionOperationRequest[];
106
107
  }
108
+ /**
109
+ * State a decision was read from: records by identity, and equality
110
+ * predicates over a collection ("every record of `collection` whose fields
111
+ * equal these values", including records that do not exist yet).
112
+ *
113
+ * Generic: it names collections, ids, fields, and values, never what they mean.
114
+ */
115
+ export interface TransactionReadSet {
116
+ records?: Array<{
117
+ collection: string;
118
+ id: string;
119
+ }>;
120
+ predicates?: Array<{
121
+ collection: string;
122
+ where: Array<{
123
+ field: string;
124
+ eq: unknown;
125
+ }>;
126
+ }>;
127
+ }
128
+ /**
129
+ * A read set, and what the authority held for it at one moment.
130
+ *
131
+ * Issued by `db.readBasis()`. The token is opaque and meaningful only to the
132
+ * authority that issued it: a transaction that carries this basis as a fence
133
+ * commits only if re-reading the same set, inside the commit decision, yields
134
+ * the same token. Records and predicates outside the set never conflict.
135
+ */
136
+ export interface TransactionReadBasis {
137
+ reads: TransactionReadSet;
138
+ token: string;
139
+ }
140
+ /** Largest number of records plus predicates one read basis may name. */
141
+ export declare const TRANSACTION_READ_SET_LIMIT = 1000;
142
+ /**
143
+ * Validate, de-duplicate, and order a read set, so that two descriptions of
144
+ * the same reads are one read set and one token.
145
+ */
146
+ export declare function normaliseReadSet(reads: TransactionReadSet): Required<TransactionReadSet>;
107
147
  export interface AtomicTransactionRequest {
108
148
  transactionId: string;
109
149
  operations: AtomicTransactionOperationRequest[];
@@ -114,6 +154,12 @@ export interface AtomicTransactionRequest {
114
154
  * would make the guard a mutation.
115
155
  */
116
156
  preconditions?: AtomicTransactionPrecondition[];
157
+ /**
158
+ * Read bases the transaction depends on. Each is re-evaluated inside the
159
+ * commit decision; if the state it names differs from what was read, the
160
+ * transaction is refused as a conflict and nothing is written.
161
+ */
162
+ fences?: TransactionReadBasis[];
117
163
  }
118
164
  export interface AtomicTransactionResult {
119
165
  /** Durable identity of the transaction. Stable across restart and replay. */
@@ -158,10 +204,21 @@ export interface AtomicTransactionScope {
158
204
  * ```
159
205
  */
160
206
  require(condition: AtomicTransactionPrecondition): AtomicTransactionScope;
207
+ /**
208
+ * Commit only if the state `basis` read is unchanged.
209
+ *
210
+ * ```ts
211
+ * const basis = await db.readBasis({ records: [{ collection: 'a', id: '1' }] });
212
+ * await db.transaction(tx => { tx.fence(basis); tx.collection('b').set('2', next); });
213
+ * ```
214
+ */
215
+ fence(basis: TransactionReadBasis): AtomicTransactionScope;
161
216
  /** Operations staged so far. */
162
217
  readonly operations: readonly AtomicTransactionOperationRequest[];
163
218
  /** Preconditions staged so far. */
164
219
  readonly preconditions: readonly AtomicTransactionPrecondition[];
220
+ /** Read bases staged so far. */
221
+ readonly fences: readonly TransactionReadBasis[];
165
222
  }
166
223
  export interface AtomicTransactionOptions {
167
224
  /**
@@ -179,7 +236,7 @@ export declare class AtomicTransactionError extends Error {
179
236
  }
180
237
  /** Which predicate failed, and what the authority actually holds. */
181
238
  export interface ConditionalConflictDetail {
182
- predicate: 'version' | 'epoch' | 'lease' | 'present' | 'missing';
239
+ predicate: 'version' | 'epoch' | 'lease' | 'present' | 'missing' | 'fence';
183
240
  collection: string;
184
241
  key: string;
185
242
  expected?: number | string;
@@ -242,12 +299,15 @@ export declare class AtomicTransactionBuilder implements AtomicTransactionScope
242
299
  readonly transactionId: string;
243
300
  private readonly staged;
244
301
  private readonly required;
302
+ private readonly fenced;
245
303
  private sealed;
246
304
  constructor(transactionId?: string);
247
305
  get operations(): readonly AtomicTransactionOperationRequest[];
248
306
  collection<T = unknown>(name: string): AtomicTransactionCollection<T>;
249
307
  get preconditions(): readonly AtomicTransactionPrecondition[];
250
308
  require(condition: AtomicTransactionPrecondition): AtomicTransactionScope;
309
+ get fences(): readonly TransactionReadBasis[];
310
+ fence(basis: TransactionReadBasis): AtomicTransactionScope;
251
311
  /** Freeze the builder and produce the request to send. */
252
312
  seal(): AtomicTransactionRequest;
253
313
  }
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,50 @@
22
22
  * a runtime that cannot provide that path refuses the transaction rather than
23
23
  * silently degrading it into a sequence of independent writes.
24
24
  */
25
+ /** Largest number of records plus predicates one read basis may name. */
26
+ export const TRANSACTION_READ_SET_LIMIT = 1_000;
27
+ const READ_SET_SEGMENT = /^[A-Za-z0-9._-]{1,128}$/;
28
+ /**
29
+ * Validate, de-duplicate, and order a read set, so that two descriptions of
30
+ * the same reads are one read set and one token.
31
+ */
32
+ export function normaliseReadSet(reads) {
33
+ if (!reads || typeof reads !== 'object')
34
+ throw new Error('a read set is required');
35
+ const records = new Map();
36
+ for (const record of reads.records ?? []) {
37
+ const collection = String(record?.collection ?? '');
38
+ const id = String(record?.id ?? '');
39
+ if (!READ_SET_SEGMENT.test(collection) || !READ_SET_SEGMENT.test(id))
40
+ throw new Error(`invalid read ${collection}:${id}`);
41
+ records.set(`${collection}:${id}`, { collection, id });
42
+ }
43
+ const predicates = new Map();
44
+ for (const predicate of reads.predicates ?? []) {
45
+ const collection = String(predicate?.collection ?? '');
46
+ if (!READ_SET_SEGMENT.test(collection))
47
+ throw new Error(`invalid predicate collection ${collection}`);
48
+ if (!Array.isArray(predicate.where) || predicate.where.length === 0) {
49
+ throw new Error(`a predicate over ${collection} must name at least one field; a whole-collection read is not a narrow fence`);
50
+ }
51
+ const where = predicate.where.map(condition => {
52
+ if (typeof condition?.field !== 'string' || !condition.field || !('eq' in condition)) {
53
+ throw new Error(`a predicate over ${collection} takes { field, eq } conditions`);
54
+ }
55
+ return { field: condition.field, eq: condition.eq };
56
+ }).sort((left, right) => (left.field < right.field ? -1 : left.field > right.field ? 1 : 0));
57
+ predicates.set(`${collection}\u0000${JSON.stringify(where)}`, { collection, where });
58
+ }
59
+ if (records.size + predicates.size === 0)
60
+ throw new Error('a read set that names nothing fences nothing');
61
+ if (records.size + predicates.size > TRANSACTION_READ_SET_LIMIT) {
62
+ throw new Error(`a read set may name at most ${TRANSACTION_READ_SET_LIMIT} records and predicates`);
63
+ }
64
+ return {
65
+ records: [...records.entries()].sort(([left], [right]) => (left < right ? -1 : 1)).map(([, value]) => value),
66
+ predicates: [...predicates.entries()].sort(([left], [right]) => (left < right ? -1 : 1)).map(([, value]) => value),
67
+ };
68
+ }
25
69
  /** Thrown when a transaction cannot be committed. Nothing was applied. */
26
70
  export class AtomicTransactionError extends Error {
27
71
  transactionId;
@@ -119,6 +163,7 @@ export class AtomicTransactionBuilder {
119
163
  transactionId;
120
164
  staged = [];
121
165
  required = [];
166
+ fenced = [];
122
167
  sealed = false;
123
168
  constructor(transactionId) {
124
169
  this.transactionId = transactionId || generateTransactionId();
@@ -205,6 +250,19 @@ export class AtomicTransactionBuilder {
205
250
  this.required.push({ ...normalised, id: String(condition.id) });
206
251
  return this;
207
252
  }
253
+ get fences() {
254
+ return this.fenced;
255
+ }
256
+ fence(basis) {
257
+ if (this.sealed) {
258
+ throw new Error('this transaction has already been committed; start a new one');
259
+ }
260
+ if (!basis || typeof basis.token !== 'string' || !basis.token) {
261
+ throw new Error('a fence requires a read basis issued by db.readBasis()');
262
+ }
263
+ this.fenced.push({ reads: normaliseReadSet(basis.reads), token: basis.token });
264
+ return this;
265
+ }
208
266
  /** Freeze the builder and produce the request to send. */
209
267
  seal() {
210
268
  this.sealed = true;
@@ -212,6 +270,7 @@ export class AtomicTransactionBuilder {
212
270
  transactionId: this.transactionId,
213
271
  operations: [...this.staged],
214
272
  preconditions: [...this.required],
273
+ ...(this.fenced.length ? { fences: [...this.fenced] } : {}),
215
274
  };
216
275
  }
217
276
  }
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feltdb/core",
3
- "version": "0.11.8",
3
+ "version": "0.11.9",
4
4
  "description": "FeltDB Core - Application-facing database with Browser, Local, and Server runtimes. Durable state, reactive APIs.",
5
5
  "license": "MIT",
6
6
  "type": "module",