slopkit 0.1.2 → 0.3.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/DEPS.md +2 -1
- package/README.md +51 -0
- package/dist/client.d.ts +31 -4
- package/dist/client.js +25 -2
- package/dist/conformance/client.test.js +7 -7
- package/dist/conformance/fixtures.d.ts +55 -3
- package/dist/conformance/fixtures.js +9 -5
- package/dist/conformance/legacy/replica-012.d.ts +52 -0
- package/dist/conformance/legacy/replica-012.js +403 -0
- package/dist/conformance/outbox-bound.test.d.ts +1 -0
- package/dist/conformance/outbox-bound.test.js +268 -0
- package/dist/conformance/paging-budget.test.d.ts +1 -0
- package/dist/conformance/paging-budget.test.js +101 -0
- package/dist/conformance/paging-fixtures.d.ts +80 -0
- package/dist/conformance/paging-fixtures.js +135 -0
- package/dist/conformance/paging-network.test.d.ts +1 -0
- package/dist/conformance/paging-network.test.js +123 -0
- package/dist/conformance/paging-state.test.d.ts +1 -0
- package/dist/conformance/paging-state.test.js +180 -0
- package/dist/conformance/paging-storage.test.d.ts +1 -0
- package/dist/conformance/paging-storage.test.js +255 -0
- package/dist/conformance/paging.test.d.ts +1 -0
- package/dist/conformance/paging.test.js +209 -0
- package/dist/conformance/pull.d.ts +74 -0
- package/dist/conformance/pull.js +400 -0
- package/dist/conformance/push-bytes.test.d.ts +1 -0
- package/dist/conformance/push-bytes.test.js +174 -0
- package/dist/conformance/push-refusal.test.d.ts +1 -0
- package/dist/conformance/push-refusal.test.js +644 -0
- package/dist/contract.d.ts +19 -6
- package/dist/contract.js +76 -9
- package/dist/server/ledger.d.ts +13 -0
- package/dist/server/ledger.js +18 -0
- package/dist/server/pull.d.ts +85 -0
- package/dist/server/pull.js +324 -0
- package/dist/server/web.d.ts +9 -5
- package/dist/server/web.js +29 -12
- package/dist/sync.d.ts +221 -16
- package/dist/sync.js +565 -96
- package/dist/web/replica.js +93 -16
- package/package.json +17 -1
package/DEPS.md
CHANGED
|
@@ -29,7 +29,8 @@ seeded network's PRNG and scheduler.
|
|
|
29
29
|
## Budgets
|
|
30
30
|
|
|
31
31
|
- **Kit client core**, gated by `pack:check` on the packed package: `schema`, `contract`, `sync`, `client`, `machine`,
|
|
32
|
-
`money`, `digest`, `states` and `web/replica`, minified and gzipped together, ≤
|
|
32
|
+
`money`, `digest`, `states` and `web/replica`, minified and gzipped together, ≤ 13,951 bytes (13,951 measured; the
|
|
33
|
+
paged first copy added 1,470, refused pushes and the bounded outbox 1,755).
|
|
33
34
|
`cron` is browser-safe but outside the core; an app that ships it counts it in its own budget. The budget grows only
|
|
34
35
|
with a new entry or guarantee, by its measured size.
|
|
35
36
|
- **Hello app** (preact, signals, and `sync`, `client`, `web/replica`, `schema`, `contract`, `states`): ≤ 17 KB gzip
|
package/README.md
CHANGED
|
@@ -94,6 +94,57 @@ export default defineConfig({
|
|
|
94
94
|
});
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
push handlers return per-command outcomes as ledger receipts. once any command in a request has acted, never throw
|
|
98
|
+
an `invalid`, `forbidden` or `conflict` refusal for the whole request: a refusal proves only that attempt did not act,
|
|
99
|
+
not that an earlier delivery did not. request schemas are checked before the handler runs.
|
|
100
|
+
|
|
101
|
+
a push is at most `PUSH_BYTES` (48 KiB) of UTF-8 json, within `serveWeb`'s default `bodyLimit` (64 KiB): never set
|
|
102
|
+
`bodyLimit` below the page's `pushBytes`. the outbox admits at most `OUTBOX_MAX` (500) commands across tabs: place
|
|
103
|
+
`deriveOutboxState(model)` in its own table (`waiting` needs a connection, `finished` needs dismissing), and a
|
|
104
|
+
command that is `unknown` and `parked` as needing attention. a `Failure` of kind `unreadable` is a reply the page
|
|
105
|
+
couldn't read: it may have acted.
|
|
106
|
+
|
|
107
|
+
### sync in pages
|
|
108
|
+
|
|
109
|
+
a device with no copy draws its newest rows after one round trip, whatever the history's size, then pulls older pages
|
|
110
|
+
back to back until it holds everything. the server half is `slopkit/server/pull`: one history table paged newest
|
|
111
|
+
first by a keyset order, the other tables whole, receipts with the rows they name, all in one read transaction and
|
|
112
|
+
within your caps.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { createPull } from "slopkit/server/pull";
|
|
116
|
+
|
|
117
|
+
const pulls = createPull({
|
|
118
|
+
db, dataset: () => store.dataset(), revision: () => store.revision(),
|
|
119
|
+
// newest first by (date, id), descending; id is unique. bump version when the order changes
|
|
120
|
+
paged: { table: "transactions", order: ["date", "id"], version: 1, live: "deleted = 0", read: toTransaction,
|
|
121
|
+
through: ([date]) => String(date) },
|
|
122
|
+
unpaged: (since) => ({ accounts: store.accounts(since) }), // whole on every reset
|
|
123
|
+
// the commands the page names (any of its pages'), or an old page's own receipts since `since`
|
|
124
|
+
receipts: { read: ({ since, commands }, viewer: Viewer) => commands === undefined
|
|
125
|
+
? ledger.receipts(viewer.principal, viewer.client, since).map((each) => ({ ...each, client: viewer.client }))
|
|
126
|
+
: ledger.receiptsOf(viewer.principal, commands),
|
|
127
|
+
names: (each) => [each.receipt.transaction] },
|
|
128
|
+
limits: { firstRows: 200, pageRows: 2000, maxRows: 5000, maxBytes: 2_000_000 },
|
|
129
|
+
// your endpoint's reply: the caps measure its json
|
|
130
|
+
respond: ({ rows, unpaged, receipts, ...rest }) => ({ ...rest, transactions: rows, ...unpaged, commands: receipts }),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
route(pull, (request, caller) => pulls.pull(request, viewerOf(request, caller)).reply);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
the page's pull request carries `page` (the engine fills it in; a request without it gets the old whole reply), and
|
|
137
|
+
the copy's history is its own subject: place `deriveHistoryState(model)` (loading, none, partial, complete) and
|
|
138
|
+
`deriveDurabilityState(model)` (the storage states) in two tables and compose both. partial is a claim beside the
|
|
139
|
+
numbers, "history from <through>, loading older · held of total", and totals cover only `through` and above. run the
|
|
140
|
+
pull contract against your own pull in your tests:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { pullConformance } from "slopkit/conformance/pull";
|
|
144
|
+
|
|
145
|
+
pullConformance({ name: "my app", create: async (limits) => fixtureOverMyRealStore(limits) });
|
|
146
|
+
```
|
|
147
|
+
|
|
97
148
|
the shared lint rules go on top of ultracite's presets (`oxlint.config.ts`):
|
|
98
149
|
|
|
99
150
|
```ts
|
package/dist/client.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* the app's own hooks (`answered`, for example opening or closing a provider's window, and `telemetry`).
|
|
8
8
|
*/
|
|
9
9
|
import type { Result } from "./contract.ts";
|
|
10
|
-
import type { Commit, createSync, Model, Msg, Patch, Pending, Receipt, Replica } from "./sync.ts";
|
|
10
|
+
import type { Commit, createSync, Model, Msg, OutboxFull, Patch, Pending, Receipt, Replica } from "./sync.ts";
|
|
11
11
|
/** The disk was forgotten or moved to another dataset under this page (another tab): its writes stop, and it reloads. */
|
|
12
12
|
export declare class Replaced extends Error {
|
|
13
13
|
constructor();
|
|
@@ -19,17 +19,34 @@ export type StoragePort<T, S, C, O> = {
|
|
|
19
19
|
commands: Pending<C, O>[];
|
|
20
20
|
unreadable: number;
|
|
21
21
|
}>;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Commit a patch, decided against the disk inside its transaction; returns what the disk now holds, and `whole:
|
|
24
|
+
* false` when it took the patch as a delta under its own history (so it lacks rows of this page's copy).
|
|
25
|
+
*/
|
|
23
26
|
commit: (commit: Commit<T, S>) => Promise<{
|
|
24
27
|
dataset: string;
|
|
25
28
|
revision: number;
|
|
29
|
+
whole?: boolean;
|
|
26
30
|
}>;
|
|
27
|
-
/**
|
|
28
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Write one command (an intent, or its outcome) under its key. A key the outbox doesn't hold yet is admitted only
|
|
33
|
+
* while it holds fewer than `max` commands (default `OUTBOX_MAX`), decided inside the write's own transaction so
|
|
34
|
+
* every tab shares the bound; otherwise nothing is written and the store's counts come back. A kept key always
|
|
35
|
+
* updates, and nothing kept is ever dropped for room.
|
|
36
|
+
*/
|
|
37
|
+
keep: (pending: Pending<C, O>, max?: number) => Promise<void | OutboxFull>;
|
|
29
38
|
drop: (keys: {
|
|
30
39
|
client: string;
|
|
31
40
|
id: number;
|
|
32
41
|
}[]) => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Journal a push before it is sent: these kept commands move to `sending`, all in one write (a key not kept is left
|
|
44
|
+
* alone). A command loaded so is uncertain, since that attempt may have acted.
|
|
45
|
+
*/
|
|
46
|
+
mark: (keys: {
|
|
47
|
+
client: string;
|
|
48
|
+
id: number;
|
|
49
|
+
}[]) => Promise<void>;
|
|
33
50
|
};
|
|
34
51
|
export type NetworkPort<P, S, C, O> = {
|
|
35
52
|
pull: (request: {
|
|
@@ -37,6 +54,16 @@ export type NetworkPort<P, S, C, O> = {
|
|
|
37
54
|
dataset: string | null;
|
|
38
55
|
since: number;
|
|
39
56
|
verify: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* The older page wanted: null for none (a paged reset is understood), the copy's `next` while partial. Absent asks
|
|
59
|
+
* for an old server's whole reply.
|
|
60
|
+
*/
|
|
61
|
+
page?: string | null;
|
|
62
|
+
/** Commands waiting on an outcome or on their effects, any page's: their receipts come with the rows they name. */
|
|
63
|
+
commands?: {
|
|
64
|
+
client: string;
|
|
65
|
+
id: number;
|
|
66
|
+
}[];
|
|
40
67
|
}) => Promise<Result<{
|
|
41
68
|
patch: Patch<P, S>;
|
|
42
69
|
receipts: Receipt<O>[];
|
package/dist/client.js
CHANGED
|
@@ -37,6 +37,10 @@ export function createClient(options) {
|
|
|
37
37
|
dataset: effect.dataset,
|
|
38
38
|
since,
|
|
39
39
|
verify: effect.verify,
|
|
40
|
+
// An old server's whole copy is asked as an old client asks it: with neither.
|
|
41
|
+
...(effect.page === undefined
|
|
42
|
+
? {}
|
|
43
|
+
: { page: effect.page, commands: effect.commands }),
|
|
40
44
|
})
|
|
41
45
|
.then((result) => result.ok
|
|
42
46
|
? { type: "pulled", since, ...result.value, now: now() }
|
|
@@ -49,7 +53,7 @@ export function createClient(options) {
|
|
|
49
53
|
case "push": {
|
|
50
54
|
const ids = effect.commands.map((each) => each.id);
|
|
51
55
|
const client = effect.client;
|
|
52
|
-
|
|
56
|
+
const send = () => ports.network
|
|
53
57
|
.push({
|
|
54
58
|
client,
|
|
55
59
|
dataset: effect.dataset,
|
|
@@ -64,6 +68,15 @@ export function createClient(options) {
|
|
|
64
68
|
ids,
|
|
65
69
|
failure: { kind: "offline", message: reason(error) },
|
|
66
70
|
now: now(),
|
|
71
|
+
}));
|
|
72
|
+
// Journalled before it is sent: a page that dies mid-push leaves these commands uncertain on disk.
|
|
73
|
+
return track(ports.storage.mark(ids.map((id) => ({ client, id }))).then(send, (error) => ({
|
|
74
|
+
type: "unsent",
|
|
75
|
+
client,
|
|
76
|
+
ids,
|
|
77
|
+
reason: reason(error),
|
|
78
|
+
replaced: error instanceof Replaced,
|
|
79
|
+
now: now(),
|
|
67
80
|
})), dispatch);
|
|
68
81
|
}
|
|
69
82
|
case "commit": {
|
|
@@ -76,7 +89,17 @@ export function createClient(options) {
|
|
|
76
89
|
}
|
|
77
90
|
case "keep": {
|
|
78
91
|
const { client, id } = effect.pending;
|
|
79
|
-
return track(ports.storage.keep(effect.pending).then(() =>
|
|
92
|
+
return track(ports.storage.keep(effect.pending, effect.max).then((kept) => kept === undefined
|
|
93
|
+
? { type: "kept", client, id, now: now() }
|
|
94
|
+
: {
|
|
95
|
+
type: "full",
|
|
96
|
+
client,
|
|
97
|
+
id,
|
|
98
|
+
waiting: kept.waiting,
|
|
99
|
+
finished: kept.finished,
|
|
100
|
+
max: kept.max,
|
|
101
|
+
now: now(),
|
|
102
|
+
}, (error) => ({
|
|
80
103
|
type: "keepFailed",
|
|
81
104
|
client,
|
|
82
105
|
id,
|
|
@@ -13,7 +13,7 @@ function peer() {
|
|
|
13
13
|
const sent = [];
|
|
14
14
|
const pulls = [];
|
|
15
15
|
let loseNextPush = false;
|
|
16
|
-
let
|
|
16
|
+
let unreadableNextPush = false;
|
|
17
17
|
let rejectPull = false;
|
|
18
18
|
let unavailable = false;
|
|
19
19
|
const network = {
|
|
@@ -57,11 +57,11 @@ function peer() {
|
|
|
57
57
|
loseNextPush = false;
|
|
58
58
|
return { ok: false, failure: { kind: "offline", message: "reply lost after execution" } };
|
|
59
59
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
60
|
+
if (unreadableNextPush) {
|
|
61
|
+
unreadableNextPush = false;
|
|
62
62
|
return {
|
|
63
63
|
ok: false,
|
|
64
|
-
failure: { kind: "
|
|
64
|
+
failure: { kind: "unreadable", message: "unreadable reply after execution" },
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
return { ok: true, value: { receipts: answers, revision } };
|
|
@@ -75,8 +75,8 @@ function peer() {
|
|
|
75
75
|
loseReply: () => {
|
|
76
76
|
loseNextPush = true;
|
|
77
77
|
},
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
unreadableReply: () => {
|
|
79
|
+
unreadableNextPush = true;
|
|
80
80
|
},
|
|
81
81
|
failPull: () => {
|
|
82
82
|
rejectPull = true;
|
|
@@ -315,7 +315,7 @@ describe("independent client executor conformance", () => {
|
|
|
315
315
|
const remote = peer();
|
|
316
316
|
const app = client(stored, remote.network, "page-a", { now: 0 });
|
|
317
317
|
await start(app, stored);
|
|
318
|
-
remote.
|
|
318
|
+
remote.unreadableReply();
|
|
319
319
|
app.runtime.command({ name: "put", value: 125 });
|
|
320
320
|
await app.runtime.settle();
|
|
321
321
|
expect(remote.effects()).toBe(1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NetworkPort } from "../client.ts";
|
|
2
2
|
import type { Infer, Schema } from "../schema.ts";
|
|
3
3
|
import type { Key, Pending, Replica } from "../sync.ts";
|
|
4
|
-
declare const rowSchema: Schema<{
|
|
4
|
+
export declare const rowSchema: Schema<{
|
|
5
5
|
deleted: boolean;
|
|
6
6
|
id: string;
|
|
7
7
|
revision: number;
|
|
@@ -14,7 +14,7 @@ export type Tables = {
|
|
|
14
14
|
export type PatchRows = {
|
|
15
15
|
rows: Row[];
|
|
16
16
|
};
|
|
17
|
-
declare const serverSchema: Schema<{
|
|
17
|
+
export declare const serverSchema: Schema<{
|
|
18
18
|
build: string;
|
|
19
19
|
}>;
|
|
20
20
|
export type Server = Infer<typeof serverSchema>;
|
|
@@ -40,6 +40,58 @@ export declare const outcomeSchema: Schema<({
|
|
|
40
40
|
kind: "unknown";
|
|
41
41
|
})>;
|
|
42
42
|
export type Outcome = Infer<typeof outcomeSchema>;
|
|
43
|
+
export declare const pendingSchema: Schema<{
|
|
44
|
+
at: number;
|
|
45
|
+
client: string;
|
|
46
|
+
command: {
|
|
47
|
+
name: "put";
|
|
48
|
+
value: number;
|
|
49
|
+
};
|
|
50
|
+
dataset: string | null;
|
|
51
|
+
id: number;
|
|
52
|
+
since?: number;
|
|
53
|
+
state: ({
|
|
54
|
+
kind: "saving";
|
|
55
|
+
} & {
|
|
56
|
+
kind: "saving";
|
|
57
|
+
}) | ({
|
|
58
|
+
kind: "queued";
|
|
59
|
+
} & {
|
|
60
|
+
kind: "queued";
|
|
61
|
+
}) | ({
|
|
62
|
+
kind: "sending";
|
|
63
|
+
} & {
|
|
64
|
+
kind: "sending";
|
|
65
|
+
}) | ({
|
|
66
|
+
failure: string;
|
|
67
|
+
kind: "unknown";
|
|
68
|
+
parked?: true;
|
|
69
|
+
} & {
|
|
70
|
+
kind: "unknown";
|
|
71
|
+
}) | ({
|
|
72
|
+
kind: "done";
|
|
73
|
+
outcome: ({
|
|
74
|
+
kind: "ok";
|
|
75
|
+
resource: string;
|
|
76
|
+
} & {
|
|
77
|
+
kind: "ok";
|
|
78
|
+
}) | ({
|
|
79
|
+
kind: "failed";
|
|
80
|
+
reason: string;
|
|
81
|
+
} & {
|
|
82
|
+
kind: "failed";
|
|
83
|
+
}) | ({
|
|
84
|
+
kind: "unknown";
|
|
85
|
+
reason: string;
|
|
86
|
+
} & {
|
|
87
|
+
kind: "unknown";
|
|
88
|
+
});
|
|
89
|
+
revision: number;
|
|
90
|
+
} & {
|
|
91
|
+
kind: "done";
|
|
92
|
+
});
|
|
93
|
+
}>;
|
|
94
|
+
export declare const rowsLens: import("../sync.ts").Lens<Tables, PatchRows>;
|
|
43
95
|
export declare const sync: {
|
|
44
96
|
initial: (client: string, now: number) => import("../sync.ts").Model<Tables, {
|
|
45
97
|
build: string;
|
|
@@ -147,7 +199,7 @@ export declare const sync: {
|
|
|
147
199
|
build: string;
|
|
148
200
|
}>, patch: import("../sync.ts").Patch<PatchRows, {
|
|
149
201
|
build: string;
|
|
150
|
-
}
|
|
202
|
+
}>, since: number) => "gap" | {
|
|
151
203
|
replica: Replica<Tables, {
|
|
152
204
|
build: string;
|
|
153
205
|
}>;
|
|
@@ -3,20 +3,20 @@ import { IDBFactory } from "fake-indexeddb";
|
|
|
3
3
|
import { boolean, literal, nullable, number, object, optional, string, variant, } from "../schema.js";
|
|
4
4
|
import { createSync, table } from "../sync.js";
|
|
5
5
|
import { indexedReplica } from "../web/replica.js";
|
|
6
|
-
const rowSchema = object({
|
|
6
|
+
export const rowSchema = object({
|
|
7
7
|
id: string(),
|
|
8
8
|
value: number({ integer: true }),
|
|
9
9
|
deleted: boolean(),
|
|
10
10
|
revision: number({ integer: true, min: 0 }),
|
|
11
11
|
}, { strict: true });
|
|
12
|
-
const serverSchema = object({ build: string() }, { strict: true });
|
|
12
|
+
export const serverSchema = object({ build: string() }, { strict: true });
|
|
13
13
|
const commandSchema = object({ name: literal("put"), value: number({ integer: true }) }, { strict: true });
|
|
14
14
|
export const outcomeSchema = variant("kind", {
|
|
15
15
|
ok: object({ kind: literal("ok"), resource: string() }, { strict: true }),
|
|
16
16
|
failed: object({ kind: literal("failed"), reason: string() }, { strict: true }),
|
|
17
17
|
unknown: object({ kind: literal("unknown"), reason: string() }, { strict: true }),
|
|
18
18
|
});
|
|
19
|
-
const pendingSchema = object({
|
|
19
|
+
export const pendingSchema = object({
|
|
20
20
|
client: string(),
|
|
21
21
|
id: number({ integer: true, min: 1 }),
|
|
22
22
|
dataset: nullable(string()),
|
|
@@ -27,7 +27,11 @@ const pendingSchema = object({
|
|
|
27
27
|
saving: object({ kind: literal("saving") }),
|
|
28
28
|
queued: object({ kind: literal("queued") }),
|
|
29
29
|
sending: object({ kind: literal("sending") }),
|
|
30
|
-
unknown: object({
|
|
30
|
+
unknown: object({
|
|
31
|
+
kind: literal("unknown"),
|
|
32
|
+
failure: string(),
|
|
33
|
+
parked: optional(literal(true)),
|
|
34
|
+
}),
|
|
31
35
|
done: object({
|
|
32
36
|
kind: literal("done"),
|
|
33
37
|
outcome: outcomeSchema,
|
|
@@ -36,7 +40,7 @@ const pendingSchema = object({
|
|
|
36
40
|
}),
|
|
37
41
|
});
|
|
38
42
|
const empty = { rows: new Map() };
|
|
39
|
-
const rowsLens = table({
|
|
43
|
+
export const rowsLens = table({
|
|
40
44
|
name: "rows",
|
|
41
45
|
schema: rowSchema,
|
|
42
46
|
rows: (patch) => patch.rows,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Schema } from "../../schema.ts";
|
|
2
|
+
import type { Lens, Op, Pending } from "../../sync.ts";
|
|
3
|
+
type Replica<T, S> = {
|
|
4
|
+
dataset: string | null;
|
|
5
|
+
revision: number;
|
|
6
|
+
tables: T;
|
|
7
|
+
server: S | null;
|
|
8
|
+
};
|
|
9
|
+
type StoragePort<T, S, C, O> = {
|
|
10
|
+
load: () => Promise<{
|
|
11
|
+
replica: Replica<T, S>;
|
|
12
|
+
commands: Pending<C, O>[];
|
|
13
|
+
unreadable: number;
|
|
14
|
+
}>;
|
|
15
|
+
commit: (commit: {
|
|
16
|
+
replica: Replica<T, S>;
|
|
17
|
+
ops: Op[];
|
|
18
|
+
reset: boolean;
|
|
19
|
+
base: number;
|
|
20
|
+
replaces: string | null;
|
|
21
|
+
}) => Promise<{
|
|
22
|
+
dataset: string;
|
|
23
|
+
revision: number;
|
|
24
|
+
}>;
|
|
25
|
+
keep: (pending: Pending<C, O>) => Promise<void>;
|
|
26
|
+
drop: (keys: {
|
|
27
|
+
client: string;
|
|
28
|
+
id: number;
|
|
29
|
+
}[]) => Promise<void>;
|
|
30
|
+
};
|
|
31
|
+
export declare function indexedReplica<T, P, S, C, O>(options: {
|
|
32
|
+
factory: IDBFactory;
|
|
33
|
+
name: string;
|
|
34
|
+
/** The object stores' layout: bump when a table is added or renamed. Row shapes are checked on their own. */
|
|
35
|
+
version: number;
|
|
36
|
+
tables: readonly Lens<T, P>[];
|
|
37
|
+
empty: T;
|
|
38
|
+
server: Schema<S>;
|
|
39
|
+
pending: Schema<Pending<C, O>>;
|
|
40
|
+
interfere?: (what: "commit" | "keep" | "drop", transaction: IDBTransaction) => void;
|
|
41
|
+
/**
|
|
42
|
+
* An outbox an earlier release kept in another store: on upgrade each record is moved into the outbox, converted to
|
|
43
|
+
* a command (or kept as it was, under its own key, to be counted unreadable). Never dropped.
|
|
44
|
+
*/
|
|
45
|
+
legacy?: {
|
|
46
|
+
store: string;
|
|
47
|
+
convert: (value: unknown) => Pending<C, O> | undefined;
|
|
48
|
+
};
|
|
49
|
+
}): StoragePort<T, S, C, O> & {
|
|
50
|
+
forget: () => Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
export {};
|