snapback4 0.0.5 → 0.0.6

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/README.md CHANGED
@@ -227,10 +227,12 @@ durably and predicts its rows, and follows the server's stream for the
227
227
  rest. Its options: `url`; one of `persona` (development), `session` (from
228
228
  `snapback4/auth`, kept and restored by the client), or `guest: true`
229
229
  (mint one when none is kept); `store`; `sessionStore`; `fetch`;
230
- `waitSeconds`. It has `query`, `observe`, `mutate`, `link()`, `onLink`,
231
- `viewer()`, `session()`, `signOut()`, `sync()`, `replica`, `close()`. The
232
- online client takes `url` with `persona`, `session` or `token`, and has
233
- the same card without a store.
230
+ `waitSeconds`. It has `query(op, args) → Read`, `observe(op, args)
231
+ { getSnapshot(), subscribe(listener) → unsubscribe, close() }` (a live
232
+ read; React's `useSyncExternalStore` shape), `mutate(op, args) Write`,
233
+ `link()`, `onLink`, `viewer()`, `session()`, `signOut()`, `sync()`,
234
+ `replica`, `close()`. The online client takes `url` with `persona`,
235
+ `session` or `token`, and has the same card without a store.
234
236
 
235
237
  ### The card
236
238
 
@@ -245,10 +247,14 @@ type Write = { state: "pending"; id } | { state: "sent"; id; seq } | { state: "f
245
247
  type Refusal = { code: string; family: "input" | "schema" | "auth" | "rule" | "constraint" | "bound" | "op" | "link" | "generation"; message: string; site?: string; rewrite?: string; guide?: string; retryable?: boolean };
246
248
  ```
247
249
 
248
- `complete` says the page is whole; `fresh` says the device has everything
249
- the server had at the last sync (`since` is the last sync's time when it
250
- does not); `next` is the cursor for the next page (pass it as the query's
251
- `cursor ?` argument). A row this device wrote and the server has not yet
250
+ `complete` says this is everything the query can answer: false when the
251
+ meter cut the page, when a scan's read rule is not decided by its prefix
252
+ (§2, the meter), or when a page declared without a cursor had more rows
253
+ than it holds (declare `c: cursor ?` and page with `after c`; `check`
254
+ notes it). `fresh` says the device has everything the server had at the
255
+ last sync (`since` is the last sync's time when it does not); `next` is
256
+ the cursor for the next page (pass it as the query's `cursor ?` argument);
257
+ a page with a cursor and more rows is `complete: true` with `next`. A row this device wrote and the server has not yet
252
258
  confirmed carries `pending: true`. A write is `pending` while the link is
253
259
  down (it lands when it returns), `sent` with the server's sequence, or
254
260
  `failed` with the refusal and its predicted rows withdrawn. `isReady`,
@@ -302,8 +308,11 @@ flips the link.
302
308
  - **`snapback4 run <op> '<json args>' --as alice [--json]`** runs one
303
309
  operation against the local store and prints the result and the meter.
304
310
  - **`snapback4 test [--fixture dir] [--as persona]`** loads a fixture
305
- (one `<table>.jsonl` per table), runs every query, and prints the
306
- meters beside the ceilings.
311
+ (one `<table>.jsonl` per table), runs every query as each persona with
312
+ arguments derived from the data (a reference is the table's first row,
313
+ a principal the persona, a text the shortest that fits), prints the
314
+ meters beside the ceilings and the arguments it used, and names any
315
+ query it could not run.
307
316
  - **`snapback4 check [--cost] [--json]`** compiles, writes
308
317
  `snapback/generated/api.ts` (`Rows`, `Ops`, `api`), and prints every
309
318
  diagnostic with its site and rewrite; `--cost` adds each site's
@@ -58,6 +58,8 @@ export declare class Interpreter {
58
58
  private written;
59
59
  private capped;
60
60
  private beyondHorizon;
61
+ /** The program's page had more rows and no cursor to continue on. */
62
+ private pageOverflow;
61
63
  private page;
62
64
  private pageDepth;
63
65
  private created;
@@ -135,6 +135,8 @@ export class Interpreter {
135
135
  written = 0;
136
136
  capped = false;
137
137
  beyondHorizon = false;
138
+ /** The program's page had more rows and no cursor to continue on. */
139
+ pageOverflow = false;
138
140
  page = null;
139
141
  pageDepth = 0;
140
142
  created = new Set();
@@ -176,7 +178,7 @@ export class Interpreter {
176
178
  }
177
179
  const flow = await this.statements(program.body, program.name);
178
180
  const data = flow instanceof Flow && flow.kind === "return" ? flow.value : null;
179
- return { data, complete: !this.capped && !this.beyondHorizon, next: this.capped ? null : this.nextCursor(), tables: this.tables };
181
+ return { data, complete: !this.capped && !this.beyondHorizon && !this.pageOverflow, next: this.capped ? null : this.nextCursor(), tables: this.tables };
180
182
  }
181
183
  nextCursor() {
182
184
  if (!this.page || !this.page.next)
@@ -595,6 +597,8 @@ export class Interpreter {
595
597
  if (held >= horizon.last)
596
598
  this.beyondHorizon = true;
597
599
  }
600
+ if (this.pageDepth === 0 && !scan.opaque_cursor && hasMore)
601
+ this.pageOverflow = true;
598
602
  if (this.pageDepth === 0 && scan.opaque_cursor) {
599
603
  const columns = definition.indexes[index].components.length;
600
604
  this.page = { table, index, order, prefixLen: prefix.length, next: hasMore && lastKey && visible.length ? suffixOf(this.schema, table, index, visible[visible.length - 1], prefix.length).slice(0, columns - prefix.length) : null };
@@ -634,6 +638,8 @@ export class Interpreter {
634
638
  candidates.sort((a, b) => { const c = totalCompare(a.key, b.key); return order === "Asc" ? c : -c; });
635
639
  const hasMore = candidates.length > take;
636
640
  const page = candidates.slice(0, take);
641
+ if (this.pageDepth === 0 && !first.opaque_cursor && hasMore)
642
+ this.pageOverflow = true;
637
643
  if (this.pageDepth === 0 && first.opaque_cursor)
638
644
  this.page = { table, index, order, prefixLen, next: hasMore && page.length ? page[page.length - 1].key : null };
639
645
  return page.map((c) => c.row);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snapback4",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "description": "Snapback 4: the card (contract), the online and local-first clients (IndexedDB, SQLite), React hooks, sign-in, a labelled mock, and the `snapback4` CLI. LLP 3000.",
6
6
  "bin": {
@@ -42,7 +42,7 @@
42
42
  "README.md"
43
43
  ],
44
44
  "optionalDependencies": {
45
- "snapback4-darwin-arm64": "0.0.5"
45
+ "snapback4-darwin-arm64": "0.0.6"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsc -p tsconfig.build.json",