diff --git a/.changeset/harden-client.md b/.changeset/harden-client.md new file mode 100644 index 0000000000..8ee25064a4 --- /dev/null +++ b/.changeset/harden-client.md @@ -0,0 +1,5 @@ +--- +'zero-swift': patch +--- + +A mutate from inside a mutator runs after it, as in JS, instead of sharing its mutation ID and dropping it from the push. A poke's `desiredQueriesPatches` apply in the server's order. Closing a connecting socket aborts its handshake, so a timed-out attempt no longer leaks a socket. App errors drop falsy `details`, as JS does. diff --git a/.changeset/harden-queries.md b/.changeset/harden-queries.md new file mode 100644 index 0000000000..b814211157 --- /dev/null +++ b/.changeset/harden-queries.md @@ -0,0 +1,5 @@ +--- +'zero-swift': patch +--- + +A flipped `whereExists` over a subquery with a `limit` no longer crashes the app: `materialize` and `liveQuery` throw "Partitioned take does not allow unpartitioned fetches", and `run` rejects with it, as in JS. View listeners are notified as in JS when one of them changes the client: a `mutate` from a listener runs once the notification is done, so listeners after it no longer end on stale rows and other views see every change; a listener removed during a notification is skipped, one added is called in the same pass, and destroying a view from its own listener still lets its other listeners hear that change. A change a view can't take where JS throws, such as two rows for a `one` relationship or a new row reaching a flipped `whereExists` over a `limit`, no longer crashes the app either: as in JS, the poke fails and the connection goes to the `error` state, or the mutation's `client` outcome is the error, and later mutations, `materialize`, and `run` fail with it. diff --git a/.changeset/harden-storage.md b/.changeset/harden-storage.md new file mode 100644 index 0000000000..cc0ce0b26c --- /dev/null +++ b/.changeset/harden-storage.md @@ -0,0 +1,5 @@ +--- +'zero-swift': patch +--- + +Storage hardening. Collecting old databases no longer deletes one a client opens while the collection runs, files of a newer layout or format, or files that aren't Zero databases. A client of a group that is behind another no longer writes an older snapshot over a newer one. `zero.delete()` and `dropAllDatabases` also drop the memory databases clients fell back to after their SQLite store failed. Stored data that doesn't decode now makes the client delete the database and start a new one, calling `onClientStateNotFound` if set, instead of running on memory at every launch. Writes kept because the database was busy are written by `flush()` and `close()`, or reported as a storage failure, instead of being lost. Dropping a database twice no longer deletes the new one a replaced client started. diff --git a/.changeset/harden-untrusted-input.md b/.changeset/harden-untrusted-input.md new file mode 100644 index 0000000000..45e848dea5 --- /dev/null +++ b/.changeset/harden-untrusted-input.md @@ -0,0 +1,5 @@ +--- +'zero-swift': patch +--- + +Bad server data, storage contents, and options no longer crash the app. A poke whose last mutation ID isn't a safe integer, or whose row update moves a row off its primary key, is rejected and the client disconnects; stored mutations with invalid IDs are dropped at launch. Huge or non-finite timer delays fire at once, as `setTimeout` does. A query built with a negative or out-of-range `limit`, a `limit` or `orderBy` in a junction relationship, or `start` or `related` inside `exists` throws JS's message when materialized, run, or preloaded. An `Int` column holding -2^63 fails to read instead of trapping, and `minPageSize` is clamped. diff --git a/packages/zero-conformance/src/client/app.ts b/packages/zero-conformance/src/client/app.ts index 807a0ede92..9b7bbb53e5 100644 --- a/packages/zero-conformance/src/client/app.ts +++ b/packages/zero-conformance/src/client/app.ts @@ -34,6 +34,28 @@ export const queries = defineQueries({ zql.issue.where('id', args).related('owner').one(), ), firstTwo: defineQuery(() => zql.issue.orderBy('title', 'asc').limit(2)), + // A flipped EXISTS over a limited subquery: fetching it throws + // `Partitioned take does not allow unpartitioned fetches`. + // Hydrates, but adding an issue then fetches the limited flipped + // subquery without a constraint, which throws from the push. + withAuthoredComments: defineQuery(() => + zql.issue + .whereExists('comments', q => + q.whereExists('author', a => a.limit(3), {flip: true}), + ) + .orderBy('id', 'asc'), + ), + // `oneComment` is a `one` relationship that several comments can match. + withOneComment: defineQuery(() => + zql.issue.related('oneComment').orderBy('id', 'asc'), + ), + withFirstComment: defineQuery(() => + zql.issue + .whereExists('comments', q => q.orderBy('id', 'asc').limit(1), { + flip: true, + }) + .orderBy('id', 'asc'), + ), }, users: { all: defineQuery(() => zql.user.orderBy('id', 'asc')), @@ -118,8 +140,46 @@ const issue = { ), }; +type CRUDOp = { + op: 'insert' | 'upsert' | 'update' | 'delete'; + table: string; + value: JSONObject; +}; + +const harden = { + /** + * Runs `ops` (CRUD operations on any table, values as given), then throws + * `fail` if set, or reads the `read` tables back through the transaction + * and returns them with the mutation ID, or returns `return`. With + * `failOnRebase` it throws before writing when replayed. + */ + ops: defineMutator(async ({tx, args}: {tx: Tx; args: JSONObject}) => { + if (args['failOnRebase'] === true && tx.reason === 'rebase') { + throw new ApplicationError('fails on rebase'); + } + for (const o of (args['ops'] ?? []) as CRUDOp[]) { + await tx.mutate[o.table][o.op](o.value); + } + if (typeof args['fail'] === 'string') { + throw new ApplicationError(args['fail'], {details: args['details']}); + } + const read = args['read'] as string[] | undefined; + if (read) { + const out: Record = {}; + for (const t of read) { + out[t] = await tx.run( + (zql as unknown as Record)[t], + ); + } + return {mutationID: tx.mutationID, read: out} as ReadonlyJSONValue; + } + return args['return']; + }), +}; + export const mutators = defineMutators({ issue, + harden, comment: { add: defineMutator(async ({tx, args}: {tx: Tx; args: JSONObject}) => { await tx.mutate.comment.insert({ diff --git a/packages/zero-conformance/src/client/harden-connection.test.ts b/packages/zero-conformance/src/client/harden-connection.test.ts new file mode 100644 index 0000000000..d1aa21afc9 --- /dev/null +++ b/packages/zero-conformance/src/client/harden-connection.test.ts @@ -0,0 +1,26 @@ +import {test} from 'vitest'; +import {emitVectors} from '../vectors.ts'; +import { + runScenario, + START_TIME, + type Scenario, + type StepResult, +} from './harness.ts'; +import {hardenConnectionScenarios} from './scenarios/harden-connection.ts'; + +type Case = Scenario & {expect: StepResult[]}; + +test('client hardening scenarios: connection', {timeout: 600_000}, async () => { + const cases: Case[] = []; + for (const scenario of hardenConnectionScenarios) { + cases.push({...scenario, expect: await runScenario(scenario)}); + } + emitVectors( + 'client-harden-connection', + 'client-scenarios', + 'Hardening scenarios for the connection and the sync protocol, run against the JS zero-client like vectors/client.json. See src/client/scenarios/harden-connection.ts.', + cases, + {startTime: START_TIME}, + {compact: true}, + ); +}); diff --git a/packages/zero-conformance/src/client/harden-mutations.test.ts b/packages/zero-conformance/src/client/harden-mutations.test.ts new file mode 100644 index 0000000000..3b2c613d2f --- /dev/null +++ b/packages/zero-conformance/src/client/harden-mutations.test.ts @@ -0,0 +1,30 @@ +import {test} from 'vitest'; +import {emitVectors} from '../vectors.ts'; +import { + runScenario, + START_TIME, + type Scenario, + type StepResult, +} from './harness.ts'; +import {hardenMutationScenarios} from './scenarios/harden-mutations.ts'; + +type Case = Scenario & {expect: StepResult[]}; + +test( + 'client hardening scenarios for mutations', + {timeout: 600_000}, + async () => { + const cases: Case[] = []; + for (const scenario of hardenMutationScenarios) { + cases.push({...scenario, expect: await runScenario(scenario)}); + } + emitVectors( + 'client-harden-mutations', + 'client-scenarios', + 'Hardening scenarios for mutations, run against the JS zero-client like client.json (see src/client/harness.ts and src/client/scenarios/harden-mutations.ts).', + cases, + {startTime: START_TIME}, + {compact: true}, + ); + }, +); diff --git a/packages/zero-conformance/src/client/harden-queries.test.ts b/packages/zero-conformance/src/client/harden-queries.test.ts new file mode 100644 index 0000000000..c31d015179 --- /dev/null +++ b/packages/zero-conformance/src/client/harden-queries.test.ts @@ -0,0 +1,29 @@ +// Hardening client scenarios for views and queries, kept apart from +// `client.json`. `swift/Tests/ConformanceTests/HardenQueryTests.swift` +// replays them. +import {test} from 'vitest'; +import {emitVectors} from '../vectors.ts'; +import { + runScenario, + START_TIME, + type Scenario, + type StepResult, +} from './harness.ts'; +import {hardenQueryScenarios} from './scenarios/harden-queries.ts'; + +type Case = Scenario & {expect: StepResult[]}; + +test('harden query scenarios', {timeout: 600_000}, async () => { + const cases: Case[] = []; + for (const scenario of hardenQueryScenarios) { + cases.push({...scenario, expect: await runScenario(scenario)}); + } + emitVectors( + 'harden-queries', + 'client-scenarios', + 'Hardening client scenarios: re-entrant listeners, odd TTLs, query change throttling, pokes that leave views unchanged. See src/client/harness.ts.', + cases, + {startTime: START_TIME}, + {compact: true}, + ); +}); diff --git a/packages/zero-conformance/src/client/harness.ts b/packages/zero-conformance/src/client/harness.ts index 3ed620a26f..fe0463bee5 100644 --- a/packages/zero-conformance/src/client/harness.ts +++ b/packages/zero-conformance/src/client/harness.ts @@ -81,6 +81,10 @@ export type Step = bytes?: number[]; socket?: number; } + /** A text frame of exactly `text`, which need not be JSON. */ + | {op: 'receiveText'; text: string; socket?: number} + /** A binary frame of exactly `bytes`, the message type byte included. */ + | {op: 'receiveBinary'; bytes: number[]; socket?: number} /** The socket closes from the server or network side. */ | { op: 'socketClose'; @@ -95,6 +99,15 @@ export type Step = * tab does (`document.visibilityState`). */ | {op: 'visibility'; state: 'visible' | 'hidden'} + /** + * Once `connection.state` next becomes `state`, runs `then` (a `connect` + * or `close` step) synchronously from inside the state listener. + */ + | { + op: 'onState'; + state: string; + then: {op: 'connect'; auth?: string} | {op: 'close'}; + } /** `zero.connection.connect()`. */ | {op: 'connect'; auth?: string} | {op: 'close'} @@ -113,7 +126,22 @@ export type Step = advance?: number; options?: ScenarioOptions; mutators?: 'all' | 'issues'; - }; + } + /** + * Adds the listener `listener` to `view`. Its calls are reported in + * `listeners`. Once it has been called more than `after` times (default + * 1: the call `addListener` makes), its next call performs `actions`, + * from inside the notification, once. + */ + | { + op: 'listen'; + view: string; + listener: string; + after?: number; + actions?: Step[]; + } + /** Removes a listener added by `listen`. */ + | {op: 'unlisten'; listener: string}; type Args = ReadonlyJSONValue | undefined; @@ -172,6 +200,14 @@ export type StepResult = { preloads?: Record; /** `onUpdateNeeded` and `onClientStateNotFound` calls. */ events?: ReadonlyJSONValue[]; + /** + * Listeners added by `listen`, in order: the data and result type of + * each call during the step. + */ + listeners?: Record< + string, + {calls: {data: ReadonlyJSONValue; resultType: string}[]; threw?: string} + >; }; class CloseEventPolyfill extends Event { @@ -355,6 +391,14 @@ export async function runScenario(scenario: Scenario): Promise { >(); const sentCounts: number[] = []; const closedBefore: boolean[] = []; + const listeners = new Map< + string, + { + remove: () => void; + calls: {data: ReadonlyJSONValue; resultType: string}[]; + threw?: string | undefined; + } + >(); const targetSocket = (index: number | undefined) => { const socket = index === undefined ? sockets.at(-1) : sockets[index]; @@ -474,6 +518,18 @@ export async function runScenario(scenario: Scenario): Promise { ); return; } + case 'receiveText': + targetSocket(step.socket).dispatchEvent( + new MessageEvent('message', {data: step.text}), + ); + return; + case 'receiveBinary': + targetSocket(step.socket).dispatchEvent( + new MessageEvent('message', { + data: Uint8Array.from(step.bytes).buffer, + }), + ); + return; case 'socketClose': targetSocket(step.socket).dispatchEvent( new CloseEvent('close', { @@ -489,6 +545,21 @@ export async function runScenario(scenario: Scenario): Promise { case 'visibility': document.setVisibility(step.state); return; + case 'onState': { + const {then} = step; + const unsubscribe = z.connection.state.subscribe(state => { + if (state.name !== step.state) return; + unsubscribe(); + if (then.op === 'close') { + void z.close(); + } else { + void z.connection.connect( + then.auth === undefined ? undefined : {auth: then.auth}, + ); + } + }); + return; + } case 'connect': void z.connection.connect( step.auth === undefined ? undefined : {auth: step.auth}, @@ -497,6 +568,36 @@ export async function runScenario(scenario: Scenario): Promise { case 'close': void z.close(); return; + case 'listen': { + const view = views.get(step.view)?.view; + if (!view) throw new Error(`No view ${step.view}`); + const after = step.after ?? 1; + let pending = step.actions; + const entry: { + remove: () => void; + calls: {data: ReadonlyJSONValue; resultType: string}[]; + threw?: string | undefined; + } = {remove: () => {}, calls: []}; + listeners.set(step.listener, entry); + let count = 0; + entry.remove = view.addListener((data, resultType) => { + entry.calls.push({data: json(data), resultType}); + if (++count > after && pending) { + const actions = pending; + pending = undefined; + for (const action of actions) { + // The synchronous part of each step runs inside the listener. + perform(action).catch(e => { + entry.threw ??= errorMessage(e); + }); + } + } + }); + return; + } + case 'unlisten': + listeners.get(step.listener)?.remove(); + return; case 'restart': { await z.close(); await settle(); @@ -517,6 +618,7 @@ export async function runScenario(scenario: Scenario): Promise { placeholders.set(clientGroupID, '$clientGroupID'); ids.set('$clientID', clientID); views.clear(); + listeners.clear(); mutations.clear(); runs.clear(); preloads.clear(); @@ -583,6 +685,15 @@ export async function runScenario(scenario: Scenario): Promise { if (events.length > 0) { result.events = events.splice(0); } + if (listeners.size > 0) { + result.listeners = Object.fromEntries( + Array.from(listeners, ([k, v]) => [ + k, + {calls: v.calls.splice(0), ...(v.threw ? {threw: v.threw} : {})}, + ]), + ); + for (const v of listeners.values()) v.threw = undefined; + } results.push(result as StepResult); } } finally { diff --git a/packages/zero-conformance/src/client/scenarios/harden-connection.ts b/packages/zero-conformance/src/client/scenarios/harden-connection.ts new file mode 100644 index 0000000000..be0f2f173a --- /dev/null +++ b/packages/zero-conformance/src/client/scenarios/harden-connection.ts @@ -0,0 +1,760 @@ +/** + * Hardening scenarios for the connection and the sync protocol: hostile or + * unexpected server messages, messages that arrive together with the + * handshake, error bodies at the edges of their schemas, and timing. + */ +import type {Scenario} from '../harness.ts'; +import { + advance, + connected, + errorMessage, + issue, + mutate, + poke, + pokeMessages, + pong, + put, + queryHash, + start, + syncPoke, +} from './helpers.ts'; + +const allHash = queryHash('issues.all'); + +export const hardenConnectionScenarios: Scenario[] = [ + { + name: 'handshake: the initial poke right behind connected in one read', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + start, + { + op: 'receiveAll', + messages: [ + ['connected', {wsid: 'w1'}], + ...pokeMessages('p1', null, '01', { + gotQueriesPatch: [{op: 'put', hash: allHash}], + rowsPatch: [put('issues', issue('i1'))], + }), + ], + }, + advance(10), + ], + }, + { + name: 'handshake: a close right after connected in the same turn', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + start, + connected, + {op: 'socketClose', code: 1006}, + connected, + ], + }, + { + name: 'ping: a pong that was never asked for, and one while connecting', + steps: [start, pong, connected, pong, advance(5000), pong], + }, + { + name: 'ping: a poke while waiting for the pong does not end the wait', + steps: [ + start, + connected, + advance(5000), + ...poke('p1', null, '01', {}), + advance(4999), + advance(1), + ], + }, + { + name: 'error: an unknown error kind is an invalid message', + steps: [ + start, + connected, + errorMessage({kind: 'Teapot', message: 'short and stout'}), + {op: 'connect'}, + connected, + ], + }, + { + name: 'error: an error without a message is an invalid message', + steps: [start, connected, errorMessage({kind: 'Internal'})], + }, + { + name: 'error: ServerOverloaded with minBackoffMs above maxBackoffMs', + steps: [ + start, + connected, + errorMessage({ + kind: 'ServerOverloaded', + message: 'busy', + minBackoffMs: 9000, + maxBackoffMs: 2000, + }), + advance(1999), + advance(1), + connected, + ], + }, + { + name: 'error: ServerOverloaded with a zero maxBackoffMs reconnects at once', + steps: [ + start, + connected, + errorMessage({ + kind: 'ServerOverloaded', + message: 'busy', + maxBackoffMs: 0, + }), + connected, + ], + }, + { + name: 'error: reconnectParams keep their order and skip the standard ones', + steps: [ + start, + connected, + errorMessage({ + kind: 'Rehome', + message: 'go', + maxBackoffMs: 10, + reconnectParams: { + 'zone': 'b', + 'clientID': 'evil', + '10': 'x', + '9': 'y', + 'a': 'z', + }, + }), + advance(10), + ], + }, + { + name: 'error: a backoff error while in backoff from another', + steps: [ + start, + connected, + errorMessage({ + kind: 'Rebalance', + message: 'a', + minBackoffMs: 8000, + reconnectParams: {first: '1'}, + }), + advance(8000), + errorMessage({ + kind: 'Rebalance', + message: 'b', + maxBackoffMs: 100, + }), + advance(100), + connected, + ], + }, + { + name: 'error: MutationRateLimited while connecting', + steps: [ + start, + mutate('m1', 'issue.create', {id: 'i1'}), + errorMessage({kind: 'MutationRateLimited', message: 'slow'}), + connected, + ], + }, + { + name: 'error: ClientNotFound while connecting', + steps: [ + start, + errorMessage({ + kind: 'ClientNotFound', + message: 'gone', + origin: 'zeroCache', + }), + advance(10_000), + {op: 'connect'}, + advance(10_000), + ], + }, + { + name: 'error: InvalidConnectionRequestBaseCookie while connecting', + steps: [ + start, + errorMessage({ + kind: 'InvalidConnectionRequestBaseCookie', + message: 'ahead', + origin: 'zeroCache', + }), + {op: 'connect'}, + advance(10), + ], + }, + { + name: 'error: SchemaVersionNotSupported while connecting, then connect()', + steps: [ + start, + mutate('m1', 'issue.create', {id: 'i1'}), + errorMessage({ + kind: 'SchemaVersionNotSupported', + message: 'old', + origin: 'zeroCache', + }), + {op: 'connect'}, + connected, + mutate('m2', 'issue.create', {id: 'i2'}), + ], + }, + { + name: 'error: VersionNotSupported then connect()', + steps: [ + start, + connected, + errorMessage({kind: 'VersionNotSupported', message: 'old'}), + {op: 'connect'}, + connected, + ], + }, + { + name: 'error: two errors in one read', + steps: [ + start, + connected, + { + op: 'receiveAll', + messages: [ + ['error', {kind: 'Internal', message: 'first', origin: 'zeroCache'}], + [ + 'error', + {kind: 'AuthInvalidated', message: 'second', origin: 'zeroCache'}, + ], + ], + }, + ], + }, + { + name: 'error: TransformFailed 401 from the API server, connect() with a token', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + errorMessage({ + kind: 'TransformFailed', + origin: 'zeroCache', + reason: 'http', + status: 401, + bodyPreview: '', + message: 'no', + queryIDs: [allHash], + }), + {op: 'connect', auth: 'token-2'}, + connected, + ], + }, + { + name: 'auth: connect() with an empty token while needs-auth', + options: {auth: 'token-1'}, + steps: [ + start, + connected, + errorMessage({ + kind: 'AuthInvalidated', + message: 'x', + origin: 'zeroCache', + }), + {op: 'connect', auth: ''}, + connected, + {op: 'connect', auth: 'token-3'}, + ], + }, + { + name: 'auth: connect() twice while needs-auth', + steps: [ + start, + connected, + errorMessage({ + kind: 'AuthInvalidated', + message: 'x', + origin: 'zeroCache', + }), + {op: 'connect', auth: 'a'}, + {op: 'connect', auth: 'b'}, + connected, + ], + }, + { + name: 'auth: connect() in needs-auth while hidden', + options: {hiddenTabDisconnectDelay: 0}, + steps: [ + start, + connected, + errorMessage({ + kind: 'AuthInvalidated', + message: 'x', + origin: 'zeroCache', + }), + {op: 'visibility', state: 'hidden'}, + advance(10), + {op: 'connect', auth: 'a'}, + advance(10_000), + {op: 'visibility', state: 'visible'}, + advance(10), + connected, + ], + }, + { + name: 'visibility: hidden and visible again within one turn', + options: {hiddenTabDisconnectDelay: 0}, + steps: [ + start, + connected, + {op: 'visibility', state: 'hidden'}, + {op: 'visibility', state: 'visible'}, + advance(10), + {op: 'visibility', state: 'hidden'}, + advance(1), + {op: 'visibility', state: 'visible'}, + advance(1), + connected, + ], + }, + { + name: 'visibility: hidden during the backoff', + options: {hiddenTabDisconnectDelay: 0}, + steps: [ + start, + connected, + {op: 'socketClose', code: 1006}, + {op: 'socketClose', code: 1006}, + {op: 'visibility', state: 'hidden'}, + advance(5000), + advance(60_000), + {op: 'visibility', state: 'visible'}, + connected, + ], + }, + { + name: 'visibility: hidden in the error state, then connect() and visible', + options: {hiddenTabDisconnectDelay: 0}, + steps: [ + start, + connected, + errorMessage({kind: 'Internal', message: 'x', origin: 'zeroCache'}), + {op: 'visibility', state: 'hidden'}, + advance(1), + {op: 'connect'}, + advance(1), + {op: 'visibility', state: 'visible'}, + connected, + ], + }, + { + name: 'timeout: connect timeout, then the late connected on the old socket', + steps: [ + advance(10_000), + {op: 'receive', message: ['connected', {wsid: 'w1'}], socket: 0}, + advance(5000), + connected, + ], + }, + { + name: 'disconnect timeout: offline, then an error on the next attempt', + options: {disconnectTimeoutMs: 5000}, + steps: [ + advance(10_000), + advance(5000), + errorMessage({kind: 'Internal', message: 'x', origin: 'zeroCache'}), + {op: 'connect'}, + connected, + ], + }, + { + name: 'disconnect timeout: offline, then a Rebalance on the next attempt', + options: {disconnectTimeoutMs: 5000}, + steps: [ + advance(10_000), + advance(5000), + errorMessage({kind: 'Rebalance', message: 'x', minBackoffMs: 7000}), + advance(6999), + advance(1), + connected, + ], + }, + { + name: 'close(): messages after close are ignored', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + {op: 'close'}, + {op: 'receive', socket: 0, message: ['pong', {}]}, + ...syncPoke( + 'p1', + null, + '01', + [allHash], + [put('issues', issue('i1'))], + ).map(s => ({...s, socket: 0})), + {op: 'socketClose', code: 1006, socket: 0}, + advance(10_000), + ], + }, + { + name: 'close(): during the backoff', + steps: [ + start, + {op: 'socketClose', code: 1006}, + {op: 'close'}, + advance(10_000), + ], + }, + { + name: 'close(): in needs-auth, then connect()', + steps: [ + start, + connected, + errorMessage({ + kind: 'AuthInvalidated', + message: 'x', + origin: 'zeroCache', + }), + {op: 'close'}, + {op: 'connect', auth: 'b'}, + advance(10_000), + ], + }, + { + name: 'poke: a canceled pokeEnd for another poke is an error', + steps: [ + start, + connected, + {op: 'receive', message: ['pokeStart', {pokeID: 'p1', baseCookie: null}]}, + { + op: 'receive', + message: ['pokeEnd', {pokeID: 'p2', cookie: '01', cancel: true}], + }, + ], + }, + { + name: 'poke: a canceled pokeEnd without a poke is an error', + steps: [ + start, + connected, + { + op: 'receive', + message: ['pokeEnd', {pokeID: 'p2', cookie: '01', cancel: true}], + }, + ], + }, + { + name: 'poke: rows missing primary key columns', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + ...syncPoke( + 'p1', + null, + '01', + [allHash], + [{op: 'put', tableName: 'issues', value: {title: 'no id'}}], + ), + advance(10), + ], + }, + { + name: 'poke: update of a row the client does not have', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + ...syncPoke( + 'p1', + null, + '01', + [allHash], + [ + { + op: 'update', + tableName: 'issues', + id: {id: 'nope'}, + merge: {title: 'T'}, + }, + ], + ), + advance(10), + ], + }, + { + name: 'poke: a row with a numeric primary key in a string column', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + ...syncPoke( + 'p1', + null, + '01', + [allHash], + [ + {op: 'put', tableName: 'issues', value: {...issue('i1'), id: 1}}, + put('issues', issue('1')), + ], + ), + advance(10), + ], + }, + { + name: 'poke: desiredQueriesPatches are applied in the order the server sent them', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + ...poke('p1', null, '01', { + desiredQueriesPatches: { + '~other': [{op: 'clear'}], + '$clientID': [{op: 'put', hash: allHash}], + }, + }), + {op: 'socketClose', code: 1006}, + advance(10), + ], + }, + { + name: 'poke: an empty pokeID and an empty cookie', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + ...syncPoke('', null, '', [allHash], [put('issues', issue('i1'))]), + ...syncPoke('', '', '01', [], [put('issues', issue('i2'))]), + advance(10), + ], + }, + { + name: 'poke: a poke while connecting, before connected', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + start, + ...syncPoke('p1', null, '01', [allHash], [put('issues', issue('i1'))]), + connected, + advance(10), + ], + }, + { + name: 'poke: a poke that spans a reconnect is dropped', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + {op: 'receive', message: ['pokeStart', {pokeID: 'p1', baseCookie: null}]}, + {op: 'socketClose', code: 1006}, + connected, + { + op: 'receive', + message: [ + 'pokePart', + {pokeID: 'p1', rowsPatch: [put('issues', issue('i1'))]}, + ], + }, + advance(10), + ], + }, + { + name: 'poke: an applied poke whose frame is still waiting when the socket drops', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + { + op: 'receiveAll', + messages: [ + ...pokeMessages('p1', null, '01', { + gotQueriesPatch: [{op: 'put', hash: allHash}], + rowsPatch: [put('issues', issue('i1'))], + }), + ], + }, + {op: 'socketClose', code: 1006}, + connected, + ], + }, + { + name: 'message: a pushResponse for another client is ignored', + steps: [ + start, + connected, + mutate('m1', 'issue.create', {id: 'i1'}), + { + op: 'receive', + message: [ + 'pushResponse', + {mutations: [{id: {clientID: 'other', id: 1}, result: {}}]}, + ], + }, + advance(10), + ], + }, + { + name: 'message: a pushResponse error for a mutation not yet sent', + steps: [ + start, + connected, + { + op: 'receive', + message: [ + 'pushResponse', + { + error: 'unsupportedPushVersion', + mutationIDs: [{clientID: '$clientID', id: 7}], + }, + ], + }, + mutate('m1', 'issue.create', {id: 'i1'}), + ], + }, + { + name: 'message: a pull response with no pending pull', + steps: [ + start, + connected, + advance(4000), + { + op: 'receive', + message: [ + 'pull', + {cookie: '05', requestID: 'r', lastMutationIDChanges: {}}, + ], + }, + advance(4000), + ], + }, + { + name: 'message: a transformError for a query nobody asked for', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + connected, + { + op: 'receive', + message: [ + 'transformError', + [{error: 'app', id: 'zzz', name: 'nope', message: 'x'}], + ], + }, + ...syncPoke('p1', null, '01', [allHash], [put('issues', issue('i1'))]), + advance(10), + ], + }, + { + name: 'message: deleteClients from the server', + steps: [ + start, + connected, + {op: 'receive', message: ['deleteClients', {clientIDs: ['x']}]}, + {op: 'socketClose', code: 1006}, + connected, + ], + }, + { + name: 'message: a binary frame of an unknown type', + steps: [start, connected, {op: 'receiveBinary', bytes: [7, 1, 2]}], + }, + { + name: 'message: an empty binary frame', + steps: [start, connected, {op: 'receiveBinary', bytes: []}], + }, + { + name: 'message: a tuple with an extra element', + steps: [start, connected, {op: 'receive', message: ['pong', {}, 1]}], + }, + { + name: 'socket: a close code 1009 while connecting', + steps: [ + start, + {op: 'socketClose', code: 1009}, + advance(10_000), + {op: 'connect'}, + advance(10_000), + ], + }, + { + name: 'socket: a close reported twice', + steps: [ + start, + connected, + {op: 'socketClose', code: 1006}, + {op: 'socketClose', code: 1006, socket: 0}, + connected, + ], + }, + { + name: 'socket: a close code 1009 after an error', + steps: [ + start, + connected, + errorMessage({kind: 'Internal', message: 'x', origin: 'zeroCache'}), + {op: 'socketClose', code: 1009, socket: 0}, + ], + }, + { + name: 'header: an auth token over maxHeaderLength is still sent', + options: {maxHeaderLength: 10, auth: 'a-long-token-for-a-tiny-header'}, + steps: [start, connected], + }, + { + name: 'reentrancy: connect({auth}) from a needs-auth listener', + steps: [ + start, + connected, + { + op: 'onState', + state: 'needs-auth', + then: {op: 'connect', auth: 'token-2'}, + }, + errorMessage({ + kind: 'AuthInvalidated', + message: 'x', + origin: 'zeroCache', + }), + connected, + ], + }, + { + name: 'reentrancy: connect() from an error listener', + steps: [ + start, + connected, + {op: 'onState', state: 'error', then: {op: 'connect'}}, + errorMessage({kind: 'Internal', message: 'x', origin: 'zeroCache'}), + connected, + ], + }, + { + name: 'reentrancy: close() from a connected listener', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + mutate('m1', 'issue.create', {id: 'i1'}), + start, + {op: 'onState', state: 'connected', then: {op: 'close'}}, + connected, + advance(10_000), + ], + }, + { + name: 'reentrancy: close() from a connecting listener after a drop', + steps: [ + start, + connected, + {op: 'onState', state: 'connecting', then: {op: 'close'}}, + {op: 'socketClose', code: 1006}, + advance(10_000), + ], + }, + { + name: 'reentrancy: close() from a disconnected listener', + options: {disconnectTimeoutMs: 5000}, + steps: [ + start, + {op: 'onState', state: 'disconnected', then: {op: 'close'}}, + advance(10_000), + advance(10_000), + ], + }, + { + name: 'reentrancy: close() from a needs-auth listener', + steps: [ + start, + connected, + mutate('m1', 'issue.create', {id: 'i1'}), + {op: 'onState', state: 'needs-auth', then: {op: 'close'}}, + errorMessage({kind: 'Unauthorized', message: 'x', origin: 'zeroCache'}), + advance(10_000), + ], + }, +]; diff --git a/packages/zero-conformance/src/client/scenarios/harden-mutations.ts b/packages/zero-conformance/src/client/scenarios/harden-mutations.ts new file mode 100644 index 0000000000..bff10fff6d --- /dev/null +++ b/packages/zero-conformance/src/client/scenarios/harden-mutations.ts @@ -0,0 +1,577 @@ +/** + * Hardening scenarios for mutations: CRUD edge cases inside a mutator, + * mutators that throw midway or only on a rebase, what a mutator returns, + * and push responses and stored results the server sends in odd orders and + * shapes. + */ +import type {ReadonlyJSONValue} from '../../../../shared/src/json.ts'; +import type {Scenario, Step} from '../harness.ts'; +import { + advance, + connected, + gotPut, + issue, + mutate, + poke, + pushResponse, + put, + queryHash, + start, + syncPoke, +} from './helpers.ts'; + +type JSONObject = {readonly [key: string]: ReadonlyJSONValue}; + +const allHash = queryHash('issues.all'); +const labelsHash = queryHash('issueLabels.all'); + +const viewAll = {op: 'materialize', view: 'v', query: 'issues.all'} as const; + +type Op = ['insert' | 'upsert' | 'update' | 'delete', string, JSONObject]; + +/** A `harden.ops` mutation. */ +function ops(mutation: string, list: Op[], more: JSONObject = {}): Step { + return mutate(mutation, 'harden.ops', { + ops: list.map(([op, table, value]) => ({op, table, value})), + ...more, + }); +} + +/** A full `issue` row with client column names. */ +function row(id: string, fields: JSONObject = {}): JSONObject { + return { + id, + title: id, + description: '', + closed: false, + ownerId: null, + createdAt: 1, + ...fields, + }; +} + +function mutationResult( + id: number, + result: JSONObject, + clientID = '$clientID', +): ReadonlyJSONValue { + return {op: 'put', mutation: {id: {clientID, id}, result}}; +} + +export const hardenMutationScenarios: Scenario[] = [ + { + name: 'harden crud: insert null-fills omitted columns and the transaction reads its own writes', + steps: [ + viewAll, + connected, + ops('m1', [['insert', 'issue', {id: 'i1', title: 'only a title'}]], { + read: ['issue'], + }), + ops('m2', [['insert', 'issue', {id: 'i2', title: 't', ownerId: 'u9'}]], { + read: ['issue'], + }), + ], + }, + { + name: 'harden crud: a column the schema does not have', + steps: [ + viewAll, + connected, + ops('m1', [['insert', 'issue', row('i1', {extra: 'x'})]], { + read: ['issue'], + }), + ops('m2', [['update', 'issue', {id: 'i1', other: 5, title: 'u'}]], { + read: ['issue'], + }), + ops('m3', [['upsert', 'issue', row('i2', {more: true})]], { + read: ['issue'], + }), + ], + }, + { + name: 'harden crud: every operation on one row in one transaction', + steps: [ + viewAll, + connected, + ops( + 'm1', + [ + ['insert', 'issue', row('i1', {title: 'first'})], + ['insert', 'issue', row('i1', {title: 'dup'})], + ['upsert', 'issue', {id: 'i1', title: 'merged'}], + ['update', 'issue', {id: 'i9', title: 'missing'}], + ['delete', 'issue', {id: 'i9'}], + ['update', 'issue', {id: 'i1', ownerId: 'u1', closed: true}], + ], + {read: ['issue']}, + ), + ops( + 'm2', + [ + ['delete', 'issue', {id: 'i1', title: 'ignored'}], + ['update', 'issue', {id: 'i1', title: 'after delete'}], + ['insert', 'issue', {id: 'i1', title: 'again'}], + ['update', 'issue', {id: 'i1', title: null, ownerId: null}], + ], + {read: ['issue']}, + ), + // Changes nothing: the main branch sees no difference. + ops('m3', [['update', 'issue', {id: 'i1'}]]), + ops('m4', [['upsert', 'issue', {id: 'i1', title: null}]]), + ], + }, + { + name: 'harden crud: a compound primary key', + steps: [ + {op: 'materialize', view: 'l', query: 'issueLabels.all'}, + connected, + ...syncPoke( + 'p1', + null, + '01', + [labelsHash], + [put('issueLabel', {issueId: 'a', labelId: '2'})], + ), + ops( + 'm1', + [ + ['insert', 'issueLabel', {issueId: 'a', labelId: '1'}], + ['insert', 'issueLabel', {issueId: 'a', labelId: '2', extra: 1}], + ['upsert', 'issueLabel', {issueId: 'b', labelId: '1'}], + ['delete', 'issueLabel', {issueId: 'a', labelId: '2'}], + ], + {read: ['issueLabel']}, + ), + ...poke('p2', '01', '02', { + lastMutationIDChanges: {$clientID: 1}, + rowsPatch: [ + { + op: 'del', + tableName: 'issueLabel', + id: {issueId: 'a', labelId: '2'}, + }, + put('issueLabel', {issueId: 'a', labelId: '1'}), + put('issueLabel', {issueId: 'b', labelId: '1'}), + ], + }), + ], + }, + { + name: 'harden crud: a missing or null primary key', + steps: [ + viewAll, + connected, + ops('m1', [['insert', 'issue', {title: 'no id'}]]), + ops('m2', [['insert', 'issue', {id: null, title: 'null id'}]]), + ops('m3', [['delete', 'issue', {}]]), + ops('m4', [['update', 'issueLabel', {issueId: 'a'}]]), + ops('m5', [['insert', 'issue', {id: {nested: 1}}]]), + ops('m6', [['insert', 'issue', row('ok')]], {read: ['issue']}), + ], + }, + { + name: 'harden throw: a mutator that throws after updating and deleting leaves the rows alone', + steps: [ + viewAll, + connected, + ...syncPoke( + 'p1', + null, + '01', + [allHash], + [put('issues', issue('i1')), put('issues', issue('i2'))], + ), + ops( + 'm1', + [ + ['update', 'issue', {id: 'i1', closed: true, title: 'changed'}], + ['delete', 'issue', {id: 'i2'}], + ['insert', 'issue', row('i3')], + ['upsert', 'issue', {id: 'i1', title: 'changed again'}], + ], + {fail: 'midway', details: {step: 4}}, + ), + ops('m2', [], {read: ['issue']}), + advance(10), + ], + }, + { + name: 'harden throw: details of an app error the mutator throws', + steps: [ + start, + connected, + ops('m1', [], {fail: 'no details'}), + ops('m2', [], {fail: 'null details', details: null}), + ops('m3', [], {fail: 'zero details', details: 0}), + ops('m4', [], {fail: '', details: [1, 'two']}), + ], + }, + { + name: 'harden rebase: a mutator that throws only on rebase drops its writes for the next mutation', + steps: [ + viewAll, + connected, + ...syncPoke('p1', null, '01', [allHash], [put('issues', issue('i1'))]), + ops('m1', [['insert', 'issue', row('mine')]], {failOnRebase: true}), + ops('m2', [['update', 'issue', {id: 'mine', title: 'edited'}]]), + ops('m3', [], {read: ['issue']}), + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i2'))]), + ops('m4', [], {read: ['issue']}), + ...poke('p3', '02', '03', {lastMutationIDChanges: {$clientID: 2}}), + ...poke('p4', '03', '04', { + lastMutationIDChanges: {$clientID: 4}, + rowsPatch: [put('issues', issue('mine', {title: 'server'}))], + }), + ], + }, + { + name: 'harden restart: a pending mutation that throws on rebase, after a restart', + steps: [ + viewAll, + start, + connected, + ...syncPoke('p1', null, '01', [allHash], [put('issues', issue('i1'))]), + ops('m1', [['insert', 'issue', row('mine')]], {failOnRebase: true}), + advance(2000), + {op: 'restart'}, + viewAll, + start, + ops('m2', [], {read: ['issue']}), + connected, + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i2'))]), + ...poke('p3', '02', '03', { + lastMutationIDChanges: {'$clientID.0': 1, '$clientID': 1}, + }), + ], + }, + { + name: 'harden rebase: pending insert, delete and update over server changes to the same rows', + steps: [ + viewAll, + connected, + ...syncPoke( + 'p1', + null, + '01', + [allHash], + [put('issues', issue('b')), put('issues', issue('c'))], + ), + ops('m1', [['insert', 'issue', row('a', {title: 'mine'})]]), + ops('m2', [['delete', 'issue', {id: 'b'}]]), + ops('m3', [['update', 'issue', {id: 'c', title: 'edited'}]]), + ops('m4', [['upsert', 'issue', {id: 'd', title: 'upserted'}]]), + ...poke('p2', '01', '02', { + rowsPatch: [ + put('issues', issue('a', {title: 'server a'})), + { + op: 'update', + tableName: 'issues', + id: {id: 'b'}, + merge: {title: 'server b'}, + }, + {op: 'del', tableName: 'issues', id: {id: 'c'}}, + put('issues', issue('d', {title: 'server d', closed: true})), + ], + }), + ops('m5', [], {read: ['issue']}), + ...poke('p3', '02', '03', {lastMutationIDChanges: {$clientID: 4}}), + ], + }, + { + name: 'harden reconnect: mutations the client gave up on are pushed again and late results ignored', + options: {disconnectTimeoutMs: 10_000}, + steps: [ + viewAll, + connected, + ops('m1', [['insert', 'issue', row('a')]]), + ops('m2', [['insert', 'issue', row('b')]]), + {op: 'socketClose', code: 1006}, + advance(5000), + advance(5000), + advance(1000), + ops('m3', [['insert', 'issue', row('c')]]), + advance(5000), + connected, + pushResponse([ + {id: 1, result: {error: 'app', message: 'late'}}, + {id: 2, result: {data: 'late'}}, + ]), + ops('m4', [['insert', 'issue', row('d')]]), + ...poke('p1', null, '01', { + lastMutationIDChanges: {$clientID: 3}, + gotQueriesPatch: [gotPut(allHash)], + rowsPatch: [put('issues', issue('a')), put('issues', issue('b'))], + }), + ], + }, + { + name: 'harden result: what a mutator returns', + steps: [ + start, + connected, + ops('m1', []), + ops('m2', [], {return: null}), + ops('m3', [], {return: 0}), + ops('m4', [], {return: false}), + ops('m5', [], {return: ''}), + ops('m6', [], { + return: {'a': [1, {b: null}], '': 'empty key', '1': 'one'}, + }), + ops('m7', [], {return: -0}), + ops('m8', [], {return: 1e21}), + pushResponse([ + {id: 1, result: {}}, + {id: 2, result: {data: null}}, + {id: 3, result: {data: 0}}, + {id: 4, result: {data: false}}, + {id: 5, result: {data: ''}}, + {id: 6, result: {data: {x: 1}, extra: 'ignored'}}, + {id: 7, result: {data: -0}}, + {id: 8, result: {data: 5e-7}}, + ]), + ], + }, + { + name: 'harden result: mutation args with unusual numbers and keys are pushed as JS writes them', + steps: [ + start, + connected, + mutate('m1', 'harden.ops', { + 'ops': [], + 'return': 1, + 'n': [-0, 1e21, 1e-7, 0.1, 123456789012345680000, 2 ** 53 + 2], + '10': 'ten', + '2': 'two', + 'b': 'b', + 'a': 'a', + '\u2028': 'ls', + '\ud83d\ude00': 'emoji', + }), + ], + }, + { + name: 'harden push: results out of order, twice, and for unknown mutations', + steps: [ + start, + connected, + ops('m1', []), + ops('m2', []), + ops('m3', []), + pushResponse([ + {id: 3, result: {data: 'three'}}, + {id: 1, result: {data: 'one'}}, + {id: 1, result: {error: 'app', message: 'second answer for 1'}}, + {id: 99, result: {error: 'app', message: 'unknown'}}, + ]), + pushResponse([{id: 2, result: {error: 'alreadyProcessed'}}]), + pushResponse([{id: 2, result: {data: 'two'}}]), + ...poke('p1', null, '01', {lastMutationIDChanges: {$clientID: 3}}), + ], + }, + { + name: 'harden push: app errors whose details are falsy', + steps: [ + start, + connected, + ops('m1', []), + ops('m2', []), + ops('m3', []), + ops('m4', []), + ops('m5', []), + pushResponse([ + {id: 1, result: {error: 'app', message: 'zero', details: 0}}, + {id: 2, result: {error: 'app', message: 'empty', details: ''}}, + {id: 3, result: {error: 'app', message: 'false', details: false}}, + {id: 4, result: {error: 'app', message: 'null', details: null}}, + {id: 5, result: {error: 'app', details: {kept: true}}}, + ]), + ], + }, + { + name: 'harden push: stored app errors whose details are falsy', + steps: [ + start, + connected, + ops('m1', []), + ops('m2', []), + ...poke('p1', null, '01', { + lastMutationIDChanges: {$clientID: 2}, + mutationsPatch: [ + mutationResult(1, {error: 'app', message: 'zero', details: 0}), + mutationResult(2, {error: 'app', message: 'false', details: false}), + ], + }), + ], + }, + { + name: 'harden push: a pushResponse for another client is an error', + steps: [ + start, + connected, + ops('m1', []), + { + op: 'receive', + message: [ + 'pushResponse', + {mutations: [{id: {clientID: 'other', id: 1}, result: {}}]}, + ], + }, + advance(5000), + ], + }, + { + name: 'harden push: an app error pushResponse for another client is an error', + steps: [ + start, + connected, + ops('m1', []), + { + op: 'receive', + message: [ + 'pushResponse', + { + mutations: [ + { + id: {clientID: 'other', id: 1}, + result: {error: 'app', message: 'x'}, + }, + ], + }, + ], + }, + ], + }, + { + name: 'harden push: an error kind the client does not know', + steps: [ + start, + connected, + ops('m1', []), + pushResponse([{id: 1, result: {error: 'brandNew', message: 'x'}}]), + ], + }, + { + name: 'harden push: a pushResponse on a socket the client closed is ignored', + steps: [ + start, + connected, + ops('m1', []), + ops('m2', []), + {op: 'socketClose', code: 1006}, + advance(5000), + connected, + pushResponse([{id: 1, result: {data: 'late'}}]), + { + op: 'receive', + socket: 0, + message: [ + 'pushResponse', + {mutations: [{id: {clientID: '$clientID', id: 2}, result: {}}]}, + ], + }, + ], + }, + { + name: 'harden poke: stored oooMutation and alreadyProcessed results', + steps: [ + start, + connected, + ops('m1', []), + ops('m2', []), + ...poke('p1', null, '01', { + mutationsPatch: [mutationResult(1, {error: 'alreadyProcessed'})], + }), + ...poke('p2', '01', '02', { + mutationsPatch: [ + mutationResult(2, {error: 'oooMutation', details: 'gap'}), + ], + }), + ], + }, + { + name: 'harden poke: stored results for another client and deletes of unknown ones', + steps: [ + start, + connected, + ops('m1', []), + ...poke('p1', null, '01', { + mutationsPatch: [ + mutationResult(1, {data: 'not mine'}, 'other'), + {op: 'del', id: {clientID: '$clientID', id: 7}}, + ], + }), + ...poke('p2', '01', '02', { + mutationsPatch: [mutationResult(1, {data: 'mine'})], + }), + pushResponse([{id: 1, result: {data: 'late push'}}]), + ], + }, + { + name: 'harden poke: a stored result before its lmid and a pushResponse after', + steps: [ + viewAll, + connected, + ops('m1', [['insert', 'issue', row('i1')]]), + ops('m2', [['insert', 'issue', row('i2')]]), + ...poke('p1', null, '01', { + gotQueriesPatch: [gotPut(allHash)], + mutationsPatch: [mutationResult(2, {data: 'stored'})], + }), + ...poke('p2', '01', '02', { + lastMutationIDChanges: {$clientID: 2}, + rowsPatch: [put('issues', issue('i1')), put('issues', issue('i2'))], + }), + pushResponse([ + {id: 1, result: {error: 'app', message: 'too late'}}, + {id: 2, result: {error: 'app', message: 'too late'}}, + ]), + ], + }, + { + name: 'harden lmid: a lastMutationID past every pending mutation', + steps: [ + viewAll, + connected, + ops('m1', [['insert', 'issue', row('i1')]]), + ops('m2', [['insert', 'issue', row('i2')]]), + ...poke('p1', null, '01', { + lastMutationIDChanges: {$clientID: 5}, + gotQueriesPatch: [gotPut(allHash)], + }), + ops('m3', [['insert', 'issue', row('i3')]], {read: ['issue']}), + ...poke('p2', '01', '02', {lastMutationIDChanges: {$clientID: 6}}), + ], + }, + { + name: 'harden lmid: many pending mutations replay in order across reconnects', + steps: [ + viewAll, + connected, + ...syncPoke('p1', null, '01', [allHash]), + ops('m1', [['insert', 'issue', row('a')]]), + ops('m2', [['update', 'issue', {id: 'a', title: 'a2'}]]), + ops('m3', [['insert', 'issue', row('b')]]), + ops('m4', [['delete', 'issue', {id: 'a'}]]), + ops('m5', [['upsert', 'issue', {id: 'b', closed: true}]]), + {op: 'socketClose', code: 1006}, + advance(5000), + connected, + ...poke('p2', '01', '02', { + lastMutationIDChanges: {$clientID: 2}, + rowsPatch: [put('issues', issue('a', {title: 'a2'}))], + }), + {op: 'socketClose', code: 1006}, + advance(5000), + connected, + ops('m6', [['insert', 'issue', row('c')]]), + ...poke('p3', '02', '03', { + lastMutationIDChanges: {$clientID: 6}, + rowsPatch: [ + {op: 'del', tableName: 'issues', id: {id: 'a'}}, + put('issues', issue('b', {closed: true})), + put('issues', issue('c')), + ], + }), + ], + }, +]; diff --git a/packages/zero-conformance/src/client/scenarios/harden-queries.ts b/packages/zero-conformance/src/client/scenarios/harden-queries.ts new file mode 100644 index 0000000000..c2162d0bdc --- /dev/null +++ b/packages/zero-conformance/src/client/scenarios/harden-queries.ts @@ -0,0 +1,704 @@ +/** + * Hardening scenarios for views and queries: listeners that re-enter the + * client (remove or add listeners, destroy views, materialize, mutate) from + * inside a notification, TTL strings JS parses oddly, query change + * throttling, and pokes that do not change what a view shows. + * + * Emitted to `vectors/harden-queries.json` by `harden-queries.test.ts`. + */ +import type {Scenario, Step} from '../harness.ts'; +import { + advance, + connected, + del, + issue, + mutate, + poke, + put, + queryHash, + start, + syncPoke, + user, + comment, +} from './helpers.ts'; + +const allHash = queryHash('issues.all'); +const openHash = queryHash('issues.open'); +const byIDi1 = queryHash('issues.byID', 'i1'); + +const listen = ( + view: string, + listener: string, + actions?: Step[], + after?: number, +): Step => ({ + op: 'listen', + view, + listener, + ...(actions ? {actions} : {}), + ...(after !== undefined ? {after} : {}), +}); + +/** Connected, with `issues.all` in view `v` and complete with no rows. */ +const allView: Step[] = [ + start, + connected, + {op: 'materialize', view: 'v', query: 'issues.all'}, + advance(10), + ...syncPoke('p0', null, '00', [allHash]), +]; + +export const hardenQueryScenarios: Scenario[] = [ + { + name: 'listener: removing a later listener during a notification skips it', + steps: [ + ...allView, + listen('v', 'L1', [{op: 'unlisten', listener: 'L2'}]), + listen('v', 'L2'), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i2'))]), + ], + }, + { + name: 'listener: removing an earlier listener during a notification', + steps: [ + ...allView, + listen('v', 'L1'), + listen('v', 'L2', [{op: 'unlisten', listener: 'L1'}]), + listen('v', 'L3'), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i2'))]), + ], + }, + { + name: 'listener: adding a listener during a notification', + steps: [ + ...allView, + listen('v', 'L1', [listen('v', 'L2')]), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i2'))]), + ], + }, + { + name: 'listener: a listener that destroys its own view', + steps: [ + ...allView, + listen('v', 'L1', [{op: 'destroy', view: 'v'}]), + listen('v', 'L2'), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + advance(10), + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i2'))]), + ], + }, + { + name: 'listener: a mutation from a listener, with a later listener', + steps: [ + ...allView, + listen('v', 'L1', [mutate('m2', 'issue.create', {id: 'i2'})]), + listen('v', 'L2'), + mutate('m1', 'issue.create', {id: 'i1'}), + advance(10), + ], + }, + { + name: 'listener: a mutation from the listener of one view, seen by another', + steps: [ + ...allView, + {op: 'materialize', view: 'w', query: 'issues.all'}, + listen('v', 'L1', [mutate('m2', 'issue.create', {id: 'i2'})]), + listen('w', 'L2'), + mutate('m1', 'issue.create', {id: 'i1'}), + advance(10), + ], + }, + { + name: 'listener: a poke while a listener mutates', + steps: [ + ...allView, + listen('v', 'L1', [mutate('m1', 'issue.create', {id: 'i9'})]), + listen('v', 'L2'), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + advance(10), + ], + }, + { + name: 'listener: materializing the same query from a listener', + steps: [ + ...allView, + listen('v', 'L1', [ + {op: 'materialize', view: 'w', query: 'issues.all'}, + listen('w', 'L2'), + ]), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i2'))]), + advance(10), + ], + }, + { + name: 'listener: materializing another query from a listener', + steps: [ + ...allView, + listen('v', 'L1', [ + {op: 'materialize', view: 'w', query: 'issues.byID', args: 'i1'}, + listen('w', 'L2'), + ]), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + advance(10), + ...syncPoke('p2', '01', '02', [byIDi1], [del('issues', {id: 'i1'})]), + ], + }, + { + name: 'listener: destroying another view of the same commit', + steps: [ + start, + connected, + {op: 'materialize', view: 'a', query: 'issues.all'}, + {op: 'materialize', view: 'b', query: 'issues.open'}, + advance(10), + listen('a', 'La', [{op: 'destroy', view: 'b'}]), + listen('b', 'Lb'), + ...syncPoke( + 'p1', + null, + '01', + [allHash, openHash], + [put('issues', issue('i1'))], + ), + advance(10), + ], + }, + { + name: 'listener: destroying an earlier view of the same commit', + steps: [ + start, + connected, + {op: 'materialize', view: 'a', query: 'issues.all'}, + {op: 'materialize', view: 'b', query: 'issues.open'}, + advance(10), + listen('a', 'La'), + listen('b', 'Lb', [{op: 'destroy', view: 'a'}]), + ...syncPoke( + 'p1', + null, + '01', + [allHash, openHash], + [put('issues', issue('i1'))], + ), + advance(10), + ], + }, + { + name: 'listener: updateTTL and run from a listener', + steps: [ + ...allView, + listen('v', 'L1', [ + {op: 'updateTTL', view: 'v', ttl: '10m'}, + {op: 'run', run: 'r', query: 'issues.all'}, + ]), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + advance(10), + ], + }, + { + name: 'listener: preload and cleanup from a listener', + steps: [ + ...allView, + listen('v', 'L1', [ + {op: 'preload', preload: 'p', query: 'users.all'}, + {op: 'cleanup', preload: 'p'}, + {op: 'cleanup', preload: 'p'}, + ]), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + advance(10), + ], + }, + { + name: 'view: a poke that rewrites a row with the same values', + steps: [ + ...allView, + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ...syncPoke('p2', '01', '02', [], [put('issues', issue('i1'))]), + ], + }, + { + name: 'view: a poke that removes a row and puts it back', + steps: [ + ...allView, + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ...syncPoke( + 'p2', + '01', + '02', + [], + [del('issues', {id: 'i1'}), put('issues', issue('i1'))], + ), + ...syncPoke( + 'p3', + '02', + '03', + [], + [ + del('issues', {id: 'i1'}), + put('issues', issue('i1', {title: 'changed'})), + ], + ), + ], + }, + { + name: 'view: a poke that changes rows the view does not show', + steps: [ + start, + connected, + {op: 'materialize', view: 'v', query: 'issues.byID', args: 'i1'}, + {op: 'materialize', view: 'w', query: 'issues.open'}, + advance(10), + ...syncPoke( + 'p1', + null, + '01', + [byIDi1, openHash], + [put('issues', issue('i1'))], + ), + ...syncPoke( + 'p2', + '01', + '02', + [], + [put('issues', issue('i2', {closed: true}))], + ), + ...syncPoke('p3', '02', '03', [], [put('users', {id: 'u9', name: 'x'})]), + ], + }, + { + name: 'view: the poke that confirms an optimistic write with the same row', + steps: [ + ...allView, + mutate('m1', 'issue.create', {id: 'i1', title: 'I1'}), + ...poke('p1', '00', '01', { + lastMutationIDChanges: {$clientID: 1}, + rowsPatch: [put('issues', issue('i1'))], + }), + ], + }, + { + name: 'lifecycle: materialize and destroy before the store is loaded', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + {op: 'destroy', view: 'v'}, + start, + connected, + advance(10), + ], + }, + { + name: 'lifecycle: a listener added before the store is loaded destroys the view on hydration', + steps: [ + {op: 'materialize', view: 'v', query: 'issues.all'}, + {op: 'materialize', view: 'w', query: 'issues.all'}, + listen('v', 'L1', [{op: 'destroy', view: 'w'}]), + start, + connected, + advance(10), + ...syncPoke('p1', null, '01', [allHash], [put('issues', issue('i1'))]), + ], + }, + { + name: 'lifecycle: a listener destroys the other view of its query', + steps: [ + ...allView, + {op: 'materialize', view: 'w', query: 'issues.all'}, + listen('v', 'L1', [{op: 'destroy', view: 'w'}]), + listen('w', 'L2'), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + advance(10), + {op: 'destroy', view: 'v'}, + advance(10), + ], + }, + { + name: 'lifecycle: one() over a row deleted and put back in one poke', + steps: [ + start, + connected, + {op: 'materialize', view: 'v', query: 'issues.byID', args: 'i1'}, + advance(10), + ...syncPoke('p1', null, '01', [byIDi1], [put('issues', issue('i1'))]), + ...syncPoke( + 'p2', + '01', + '02', + [], + [ + del('issues', {id: 'i1'}), + put('issues', issue('i1', {ownerId: 'u1'})), + ], + ), + ...syncPoke( + 'p3', + '02', + '03', + [], + [ + put('issues', issue('i1', {ownerId: 'u2'})), + del('issues', {id: 'i1'}), + ], + ), + ], + }, + { + name: 'lifecycle: materialize, run, and preload after close', + steps: [ + ...allView, + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + {op: 'close'}, + {op: 'materialize', view: 'w', query: 'issues.all'}, + {op: 'run', run: 'r', query: 'issues.all'}, + {op: 'destroy', view: 'w'}, + advance(10), + ], + }, + { + name: 'lifecycle: a preload and a view of one query with different TTLs', + steps: [ + start, + connected, + {op: 'preload', preload: 'p', query: 'issues.all', ttl: '1m'}, + advance(10), + {op: 'materialize', view: 'v', query: 'issues.all', ttl: '30s'}, + advance(10), + {op: 'cleanup', preload: 'p'}, + advance(10), + {op: 'materialize', view: 'w', query: 'issues.all', ttl: '2m'}, + {op: 'destroy', view: 'v'}, + {op: 'destroy', view: 'w'}, + advance(10), + ], + }, + { + name: 'lifecycle: a transformError after the query is complete', + steps: [ + ...allView, + { + op: 'receive', + message: [ + 'transformError', + [{error: 'app', id: allHash, name: 'issues.all', message: 'late'}], + ], + }, + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + {op: 'run', run: 'r', query: 'issues.all', type: 'complete'}, + ], + }, + { + name: 'poke: materialize between pokeStart and pokeEnd', + steps: [ + ...allView, + {op: 'receive', message: ['pokeStart', {pokeID: 'p1', baseCookie: '00'}]}, + { + op: 'receive', + message: [ + 'pokePart', + {pokeID: 'p1', rowsPatch: [put('issues', issue('i1'))]}, + ], + }, + {op: 'materialize', view: 'w', query: 'issues.all'}, + {op: 'destroy', view: 'v'}, + {op: 'receive', message: ['pokeEnd', {pokeID: 'p1', cookie: '01'}]}, + advance(10), + ], + }, + { + name: 'poke: a got query deleted while views are complete and cached', + steps: [ + ...allView, + {op: 'materialize', view: 'w', query: 'users.all'}, + ...syncPoke('p1', '00', '01', [queryHash('users.all')]), + {op: 'socketClose', code: 1006}, + {op: 'materialize', view: 'x', query: 'users.all'}, + advance(5000), + connected, + ...poke('p2', '01', '02', { + gotQueriesPatch: [{op: 'del', hash: queryHash('users.all')}], + }), + advance(10), + ], + }, + { + name: 'run: complete pending over a dropped connection', + steps: [ + start, + connected, + {op: 'run', run: 'r', query: 'issues.all', type: 'complete'}, + {op: 'run', run: 'c', query: 'issues.all', type: 'cached'}, + advance(10), + {op: 'socketClose', code: 1006}, + advance(5000), + connected, + advance(10), + ...syncPoke('p1', null, '01', [allHash], [put('issues', issue('i1'))]), + advance(10), + ], + }, + { + name: 'throttle: the batch timer fires while disconnected', + steps: [ + start, + connected, + {op: 'materialize', view: 'a', query: 'issues.all'}, + {op: 'socketClose', code: 1006}, + advance(10), + {op: 'materialize', view: 'b', query: 'users.all'}, + advance(10), + advance(5000), + connected, + advance(10), + ], + }, + { + name: 'lifecycle: a removal deferred by a pending mutation over a reconnect', + steps: [ + ...allView, + mutate('m1', 'issue.create', {id: 'i1'}), + {op: 'destroy', view: 'v'}, + advance(10), + {op: 'socketClose', code: 1006}, + advance(5000), + connected, + advance(10), + ...poke('p1', '00', '01', {lastMutationIDChanges: {$clientID: 1}}), + advance(10), + ], + }, + { + name: 'error: a query that fails to hydrate throws from materialize and run', + steps: [ + // Registered first, so the failing calls below don't change what the + // server is sent (JS leaves their registrations behind; Swift drops + // them). + {op: 'preload', preload: 'p', query: 'issues.withFirstComment'}, + ...allView, + {op: 'materialize', view: 'w', query: 'issues.withFirstComment'}, + {op: 'run', run: 'r', query: 'issues.withFirstComment'}, + {op: 'run', run: 'c', query: 'issues.withFirstComment', type: 'complete'}, + advance(10), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ], + }, + { + name: 'error: a poke whose push throws', + steps: [ + ...allView, + // Already registered, so the materialize that fails below sends + // nothing (JS registers it before failing; Swift doesn't). + {op: 'materialize', view: 'u', query: 'users.all'}, + {op: 'materialize', view: 'w', query: 'issues.withAuthoredComments'}, + advance(10), + ...syncPoke( + 'p1', + '00', + '01', + [], + [ + put('users', user('u1', 'A')), + put('comments', comment('c1', 'i1')), + put('issues', issue('i1')), + ], + ), + advance(10), + {op: 'materialize', view: 'x', query: 'users.all'}, + mutate('m1', 'issue.create', {id: 'i2'}), + advance(5000), + connected, + advance(10), + ], + }, + { + name: 'error: a mutation whose push throws', + steps: [ + ...allView, + // Already registered, so the materialize that fails below sends + // nothing (JS registers it before failing; Swift doesn't). + {op: 'materialize', view: 'u', query: 'users.all'}, + {op: 'materialize', view: 'w', query: 'issues.withAuthoredComments'}, + advance(10), + ...syncPoke( + 'p1', + '00', + '01', + [], + [put('users', user('u1', 'A')), put('comments', comment('c1', 'i1'))], + ), + mutate('m1', 'issue.create', {id: 'i1'}), + advance(10), + mutate('m2', 'issue.create', {id: 'i2'}), + {op: 'materialize', view: 'x', query: 'users.all'}, + {op: 'run', run: 'r', query: 'issues.all'}, + advance(10), + ...poke('p2', '01', '02', {lastMutationIDChanges: {$clientID: 2}}), + advance(10), + ], + }, + { + name: 'error: a poke with two rows for a one relationship', + steps: [ + ...allView, + {op: 'materialize', view: 'w', query: 'issues.withOneComment'}, + advance(10), + ...syncPoke('p1', '00', '01', [], [put('issues', issue('i1'))]), + ...syncPoke('p2', '01', '02', [], [put('comments', comment('c1', 'i1'))]), + ...syncPoke('p3', '02', '03', [], [put('comments', comment('c2', 'i1'))]), + advance(10), + ], + }, + { + name: 'ttl: strings JS parses oddly', + steps: [ + start, + connected, + ...[ + '5x', + '', + 's', + 'Infinitys', + '0x10s', + '1e1m', + 'NaNs', + '-5m', + '1_0s', + ' 3s', + '3s ', + '+2m', + '.5m', + '5.m', + '0b11s', + '1e400s', + '-0s', + '5M', + '1h', + ].map((ttl, i): Step => ({ + op: 'materialize', + view: `v${i}`, + query: 'issues.byOwner', + args: `t${i}`, + // oxlint-disable-next-line @typescript-eslint/no-explicit-any + ttl: ttl as any, + })), + advance(10), + ], + }, + { + name: 'ttl: numbers at the edges', + steps: [ + start, + connected, + ...[ + 0, + -1, + -0.5, + 0.001, + 1e-7, + 599_999.5, + 600_000, + 600_000.5, + 1e21, + 2 ** 53, + ].map((ttl, i): Step => ({ + op: 'materialize', + view: `v${i}`, + query: 'issues.byOwner', + args: `n${i}`, + ttl, + })), + advance(10), + ], + }, + { + name: 'ttl: updateTTL from an unparsable TTL', + steps: [ + start, + connected, + // oxlint-disable-next-line @typescript-eslint/no-explicit-any + {op: 'materialize', view: 'v', query: 'issues.all', ttl: '5x' as any}, + advance(10), + {op: 'updateTTL', view: 'v', ttl: '1m'}, + advance(10), + {op: 'updateTTL', view: 'v', ttl: 'forever'}, + advance(10), + {op: 'materialize', view: 'w', query: 'issues.all', ttl: '2m'}, + advance(10), + ], + }, + { + name: 'ttl: updateTTL to an unparsable TTL, none, and a negative string', + steps: [ + start, + connected, + {op: 'materialize', view: 'v', query: 'issues.all', ttl: 'none'}, + advance(10), + // oxlint-disable-next-line @typescript-eslint/no-explicit-any + {op: 'updateTTL', view: 'v', ttl: 'abc' as any}, + advance(10), + {op: 'updateTTL', view: 'v', ttl: '-1m'}, + advance(10), + {op: 'updateTTL', view: 'v', ttl: -1}, + advance(10), + {op: 'updateTTL', view: 'v', ttl: 1}, + advance(10), + ], + }, + { + name: 'ttl: preload with odd TTLs', + steps: [ + start, + connected, + // oxlint-disable-next-line @typescript-eslint/no-explicit-any + {op: 'preload', preload: 'a', query: 'issues.all', ttl: 'x' as any}, + {op: 'preload', preload: 'b', query: 'issues.all', ttl: 'forever'}, + {op: 'preload', preload: 'c', query: 'users.all', ttl: 0}, + advance(10), + {op: 'cleanup', preload: 'a'}, + {op: 'cleanup', preload: 'b'}, + {op: 'cleanup', preload: 'b'}, + {op: 'cleanup', preload: 'c'}, + advance(10), + ], + }, + { + name: 'throttle: queryChangeThrottleMs 0', + options: {queryChangeThrottleMs: 0}, + steps: [ + start, + connected, + {op: 'materialize', view: 'a', query: 'issues.all'}, + {op: 'materialize', view: 'b', query: 'users.all'}, + {op: 'destroy', view: 'a'}, + advance(1), + {op: 'materialize', view: 'c', query: 'issues.open'}, + ], + }, + { + name: 'throttle: a negative queryChangeThrottleMs', + options: {queryChangeThrottleMs: -5}, + steps: [ + start, + connected, + {op: 'materialize', view: 'a', query: 'issues.all'}, + advance(1), + {op: 'materialize', view: 'b', query: 'users.all'}, + ], + }, + { + name: 'throttle: changes while connecting go in the connect message', + options: {queryChangeThrottleMs: 50}, + steps: [ + start, + {op: 'materialize', view: 'a', query: 'issues.all'}, + advance(20), + connected, + {op: 'materialize', view: 'b', query: 'users.all'}, + advance(20), + {op: 'destroy', view: 'a'}, + advance(40), + ], + }, +]; diff --git a/packages/zero-conformance/src/ivm/harden-ivm.test.ts b/packages/zero-conformance/src/ivm/harden-ivm.test.ts new file mode 100644 index 0000000000..c98066b37f --- /dev/null +++ b/packages/zero-conformance/src/ivm/harden-ivm.test.ts @@ -0,0 +1,796 @@ +// Hardening IVM vectors: deeper nesting (up to 5 levels of `related` and +// `whereExists`), junctions, `flip`/`scalar` exists, OR of EXISTS, empty IN +// lists, LIKE patterns with regex metacharacters, `start`, `limit` 0/1, +// `one()`, and pushes that edit sort keys and primary keys, applied in +// batches that flush once (as a poke does), including a remove and add of +// the same row in one batch. +// +// Writes `generated/harden-ivm-*.json` (not checked in), which +// `swift/Tests/ConformanceTests/HardenIVMTests.swift` replays. Seeded with +// `HARDEN_IVM_SEED` (default 1) and `HARDEN_IVM_SEEDS` (how many seeds from +// there, default 1), `HARDEN_IVM_CASES` cases per seed (default 2000). +// +// Numbers JSON cannot carry (NaN, ±Infinity, -0) are written as +// `{"$number": "NaN"}` and so on, in data, pushes, programs, and results. +import {test} from 'vitest'; +import type {JSONValue} from '../../../shared/src/json.ts'; +import type {AST, SimpleOperator} from '../../../zero-protocol/src/ast.ts'; +import type {Row} from '../../../zero-protocol/src/data.ts'; +import type {Format} from '../../../zero-types/src/format.ts'; +import type {Schema} from '../../../zero-types/src/schema.ts'; +import {buildPipeline} from '../../../zql/src/builder/builder.ts'; +import {TestBuilderDelegate} from '../../../zql/src/builder/test-builder-delegate.ts'; +import {ArrayView} from '../../../zql/src/ivm/array-view.ts'; +import type {Change} from '../../../zql/src/ivm/change.ts'; +import {MemorySource} from '../../../zql/src/ivm/memory-source.ts'; +import type { + FetchRequest, + Input, + InputBase, + Operator, + Output, +} from '../../../zql/src/ivm/operator.ts'; +import { + makeSourceChangeAdd, + makeSourceChangeEdit, + makeSourceChangeRemove, +} from '../../../zql/src/ivm/source.ts'; +import {consume} from '../../../zql/src/ivm/stream.ts'; +import {schema as testSchema} from '../../../zql/src/query/test/test-schemas.ts'; +import {Random} from '../random.ts'; +import {emitGeneratedVectors} from '../vectors.ts'; +import { + buildProgram, + schemaJSON, + type Expr, + type Op, + type Program, + type Push, + type RecordedChange, +} from './harness.ts'; + +const schema: Schema = testSchema; + +// Special numbers survive JSON as tagged objects. +function encode(v: unknown): JSONValue { + if (typeof v === 'number') { + if (Number.isNaN(v)) return {$number: 'NaN'}; + if (v === Infinity) return {$number: 'Infinity'}; + if (v === -Infinity) return {$number: '-Infinity'}; + if (Object.is(v, -0)) return {$number: '-0'}; + return v; + } + if (v === undefined) return null; + if (v === null || typeof v !== 'object') return v as JSONValue; + if (Array.isArray(v)) return v.map(encode); + const out: Record = {}; + for (const [k, e] of Object.entries(v)) { + if (e !== undefined) out[k] = encode(e); + } + return out; +} + +/** What the Swift runner reads back: `encode`, then a JSON round trip. */ +function roundTrip(v: T): T { + return decode(JSON.parse(JSON.stringify(encode(v)))) as T; +} + +function decode(v: unknown): unknown { + if (v === null || typeof v !== 'object') return v; + if (Array.isArray(v)) return v.map(decode); + const o = v as Record; + const keys = Object.keys(o); + if (keys.length === 1 && keys[0] === '$number') { + return Number(o.$number); + } + return Object.fromEntries(keys.map(k => [k, decode(o[k])])); +} + +// Values. Few distinct ones so sort keys and join keys collide. +const texts = [ + 'a', + 'a', + 'b', + '', + 'A', + '%', + '_', + '\\', + 'a.c', + 'a(c', + '[a]', + '^a$', + 'a*', + 'a+b', + 'a?', + '{1}', + 'a|b', + 'ß', + 'SS', + 'İ', + 'i', + 'ı', + 'É', + 'é', + 'K', // Kelvin sign, folds to k + 'k', + '\u{1f600}', + '\ue000', + 'a\nb', +]; +const plainNumbers = [ + 0, + 1, + 1, + 2, + -1, + 0.5, + 2 ** 53, + 2 ** 53 + 2, + -(2 ** 53), + 1e21, +]; +// NaN is left out: a NaN sort key breaks the indexes of JS (`node does not +// exist`) as well as Swift's. +const specialNumbers = [Infinity, -Infinity, -0]; + +const ids: Record = { + issue: ['i1', 'i2', 'i3', 'i4', 'i5', 'i6'], + user: ['u1', 'u2', 'u3'], + label: ['l1', 'l2', 'l3'], + comment: ['c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7'], + revision: ['r1', 'r2', 'r3', 'r4'], +}; + +type Mode = {special: boolean}; + +function numberValue(r: Random, mode: Mode): number { + return mode.special && r.bool(0.25) + ? r.pick(specialNumbers) + : r.pick(plainNumbers); +} + +function columnValue( + r: Random, + mode: Mode, + table: string, + column: string, +): JSONValue { + switch (`${table}.${column}`) { + case 'issue.ownerId': + return r.bool(0.3) ? null : r.pick(ids.user); + case 'issue.closed': + return r.bool(); + case 'issue.createdAt': + case 'comment.createdAt': + return numberValue(r, mode); + case 'comment.issueId': + case 'issueLabel.issueId': + return r.bool(0.05) ? 'i-missing' : r.pick(ids.issue); + case 'comment.authorId': + case 'revision.authorId': + return r.pick(ids.user); + case 'revision.commentId': + return r.pick(ids.comment); + case 'issueLabel.labelId': + return r.pick(ids.label); + case 'label.name': + // Unique in the schema: make it unique per id. + return null; + case 'user.metadata': + return r.bool(0.5) + ? null + : {registrar: r.pick(['github', 'google']), email: r.pick(texts)}; + default: + return r.pick(texts); + } +} + +function makeRow(r: Random, mode: Mode, table: string, id?: string): Row { + const row: Record = {}; + for (const column of Object.keys(schema.tables[table].columns)) { + row[column] = + column === 'id' && id !== undefined + ? id + : table === 'label' && column === 'name' + ? `${id}-${r.pick(texts)}` + : columnValue(r, mode, table, column); + } + return row as Row; +} + +const pk = (table: string, row: Row) => + JSON.stringify(schema.tables[table].primaryKey.map(k => row[k])); + +function makeData(r: Random, mode: Mode): Record { + const data: Record = {}; + for (const table of Object.keys(schema.tables)) { + const rows = new Map(); + if (table === 'issueLabel') { + for (let i = 0; i < 9; i++) { + const row = makeRow(r, mode, table); + rows.set(pk(table, row), row); + } + } else { + for (const id of ids[table]) { + if (r.bool(0.7)) rows.set(id, makeRow(r, mode, table, id)); + } + } + data[table] = [...rows.values()]; + } + return data; +} + +/** + * Batches of valid source changes. A batch is flushed once. Some batches + * remove a row and add it back changed; some edits change the primary key. + */ +function makeBatches( + r: Random, + mode: Mode, + data: Record, + count: number, +): Push[][] { + const current = new Map( + Object.entries(data).map(([t, rows]) => [ + t, + new Map(rows.map(row => [pk(t, row), row])), + ]), + ); + const tables = Object.keys(schema.tables); + const batches: Push[][] = []; + while (batches.length < count) { + const batch: Push[] = []; + const size = r.bool(0.6) ? 1 : 2 + r.int(3); + let guard = 0; + while (batch.length < size && guard++ < 50) { + const table = r.pick(tables); + const rows = current.get(table)!; + const kind = r.int(6); + if (kind === 0) { + const candidates = + table === 'issueLabel' + ? [makeRow(r, mode, table)] + : ids[table].map(id => makeRow(r, mode, table, id)); + const row = candidates.find(c => !rows.has(pk(table, c))); + if (row) { + rows.set(pk(table, row), row); + batch.push({table, type: 'add', row}); + } + continue; + } + if (rows.size === 0) continue; + const existing = r.pick([...rows.values()]); + const key = pk(table, existing); + if (kind === 1) { + rows.delete(key); + batch.push({table, type: 'remove', row: existing}); + } else if (kind === 2) { + // Remove and add back, changed, in one batch. + const row = {...existing} as Record; + const columns = nonPK(table); + if (columns.length > 0) { + const column = r.pick(columns); + row[column] = columnValue(r, mode, table, column); + } + rows.set(key, row as Row); + batch.push({table, type: 'remove', row: existing}); + batch.push({table, type: 'add', row: row as Row}); + } else if (kind === 3 && table !== 'issueLabel' && r.bool(0.4)) { + // An edit that changes the primary key. + const free = ids[table].filter(id => !rows.has(JSON.stringify([id]))); + if (free.length === 0) continue; + const row = {...existing, id: r.pick(free)} as Row; + rows.delete(key); + rows.set(pk(table, row), row); + batch.push({table, type: 'edit', row, oldRow: existing}); + } else { + const columns = nonPK(table); + if (columns.length === 0) continue; + const row = {...existing} as Record; + for (let i = 0, n = 1 + r.int(2); i < n; i++) { + const column = r.pick(columns); + row[column] = columnValue(r, mode, table, column); + } + rows.set(key, row as Row); + batch.push({table, type: 'edit', row: row as Row, oldRow: existing}); + } + } + if (batch.length > 0) batches.push(batch); + } + return batches; +} + +function nonPK(table: string): string[] { + const pkCols = schema.tables[table].primaryKey; + return Object.keys(schema.tables[table].columns).filter( + c => !pkCols.includes(c) && !(table === 'label' && c === 'name'), + ); +} + +// Queries. +const likePatterns = [ + '%', + '_', + 'a%', + '%b', + 'a.c', + 'a_c', + '%(%', + '[a]', + '^a$', + 'a*', + 'a+b', + 'a?', + '{1}', + 'a|b', + '\\%', + '\\_', + '\\\\', + '%\\\\%', + 'ss', + 'SS', + 'ß', + 'i', + 'İ', + 'ı', + 'é', + 'É', + 'k', + 'a%b', + '%\u{1f600}%', +]; + +function comparison( + r: Random, + mode: Mode, + table: string, +): [string, SimpleOperator, JSONValue] { + const columns = Object.entries(schema.tables[table].columns); + const [column, value] = r.pick(columns); + if (value.type === 'json') { + return [column, r.pick(['IS', 'IS NOT'] as const), null]; + } + if (value.type === 'boolean') { + const op = r.pick(['=', '!=', 'IS', 'IS NOT'] as const); + return [column, op, op.startsWith('IS') && r.bool(0.3) ? null : r.bool()]; + } + const domain: JSONValue[] = + value.type === 'number' + ? [...plainNumbers, ...(mode.special ? specialNumbers : [])] + : [...(ids[table] ?? []), ...texts, ...ids.user, ...ids.issue]; + const op = r.pick([ + '=', + '!=', + '<', + '>', + '<=', + '>=', + 'IN', + 'NOT IN', + 'IS', + 'IS NOT', + ...(value.type === 'string' + ? (['LIKE', 'NOT LIKE', 'ILIKE', 'NOT ILIKE'] as const) + : []), + ]); + switch (op) { + case 'LIKE': + case 'NOT LIKE': + case 'ILIKE': + case 'NOT ILIKE': + return [column, op, r.pick(likePatterns)]; + case 'IN': + case 'NOT IN': { + const n = r.pick([0, 0, 1, 2, 3]); + return [column, op, Array.from({length: n}, () => r.pick(domain))]; + } + case 'IS': + case 'IS NOT': + return [column, op, r.bool(0.6) ? null : r.pick(domain)]; + default: + return [column, op, r.pick(domain)]; + } +} + +type Ctx = { + depth: number; + maxDepth: number; + inJunction: boolean; + inExists: boolean; + mode: Mode; +}; + +const rels = (table: string) => + Object.entries(schema.relationships[table] ?? {}); + +function existsOp(r: Random, table: string, ctx: Ctx) { + const all = rels(table); + if (all.length === 0 || ctx.depth >= ctx.maxDepth) return undefined; + const [rel, hops] = r.pick(all); + const dest = hops.at(-1)!.destSchema; + const sub = r.bool(0.7) + ? subOps(r, dest, { + ...ctx, + depth: ctx.depth + 1, + inJunction: hops.length > 1, + inExists: true, + }) + : undefined; + return { + rel, + sub, + flip: r.bool(0.4) ? r.bool() : undefined, + scalar: r.bool(0.15) ? true : undefined, + }; +} + +function expr( + r: Random, + table: string, + ctx: Ctx, + depth = 0, + negated = false, +): Expr { + const k = r.int(depth > 2 ? 3 : 7); + if (k <= 1) return {cmp: comparison(r, ctx.mode, table)}; + if (k === 2 && (!negated || r.bool(0.1))) { + // A negated EXISTS is rejected by the client builder: keep a few. + const e = existsOp(r, table, ctx); + if (e) return {exists: e.rel, sub: e.sub, flip: e.flip, scalar: e.scalar}; + return {cmp: comparison(r, ctx.mode, table)}; + } + if (k === 6) return {not: expr(r, table, ctx, depth + 1, true)}; + const n = r.pick([0, 1, 2, 2, 3]); + const parts = Array.from({length: n}, () => + expr(r, table, ctx, depth + 1, negated), + ); + return r.bool() ? {and: parts} : {or: parts}; +} + +function subOps(r: Random, table: string, ctx: Ctx): Op[] { + const ops = randomOps(r, table, ctx, r.int(4)); + // Most subqueries go one level deeper, so chains reach `maxDepth`. + if (ctx.depth < ctx.maxDepth && r.bool(0.75)) { + const deeper = deepOp(r, table, ctx); + if (deeper) ops.splice(r.int(ops.length + 1), 0, deeper); + } + return ops; +} + +/** A `related` or `whereExists` op whose subquery nests further. */ +function deepOp(r: Random, table: string, ctx: Ctx): Op | undefined { + if (!ctx.inExists && r.bool(0.5)) { + const all = rels(table); + if (all.length === 0) return undefined; + const [rel, hops] = r.pick(all); + const dest = hops.at(-1)!.destSchema; + return { + op: 'related', + rel, + sub: subOps(r, dest, { + ...ctx, + depth: ctx.depth + 1, + inJunction: hops.length > 1, + }), + }; + } + const e = existsOp(r, table, ctx); + return e && {op: 'whereExists', ...e}; +} + +function randomOps( + r: Random, + table: string, + ctx: Ctx, + count: number, + data?: Record, +): Op[] { + const ops: Op[] = []; + for (let i = 0; i < count; i++) { + const k = r.int(12); + if (k <= 1) { + const c = comparison(r, ctx.mode, table); + ops.push( + c[1] === '=' && r.bool() + ? {op: 'where', field: c[0], value: c[2]} + : {op: 'where', field: c[0], cmp: c[1], value: c[2]}, + ); + } else if (k === 2) { + ops.push({op: 'whereExpr', expr: expr(r, table, ctx)}); + } else if (k === 3) { + const e = existsOp(r, table, ctx); + if (e) ops.push({op: 'whereExists', ...e}); + } else if (k === 4 || k === 5) { + // OR of EXISTS, the shape that splits into UnionFanOut/FanIn. + if (k === 5) { + const a = existsOp(r, table, ctx); + const b = existsOp(r, table, ctx); + if (a && b) { + ops.push({ + op: 'whereExpr', + expr: { + or: [ + {exists: a.rel, sub: a.sub, flip: a.flip, scalar: a.scalar}, + r.bool() + ? {exists: b.rel, sub: b.sub, flip: b.flip, scalar: b.scalar} + : {cmp: comparison(r, ctx.mode, table)}, + ], + }, + }); + } + continue; + } + if (ctx.inExists && !r.bool(0.1)) continue; + if (ctx.depth >= ctx.maxDepth) continue; + const all = rels(table); + if (all.length === 0) continue; + const [rel, hops] = r.pick(all); + const dest = hops.at(-1)!.destSchema; + ops.push({ + op: 'related', + rel, + sub: r.bool(0.8) + ? subOps(r, dest, { + ...ctx, + depth: ctx.depth + 1, + inJunction: hops.length > 1, + }) + : undefined, + }); + } else if (k === 6 || k === 7) { + if (ctx.inJunction) continue; + const columns = Object.entries(schema.tables[table].columns) + .filter(([, v]) => v.type !== 'json') + .map(([c]) => c); + ops.push({ + op: 'orderBy', + field: r.pick(columns), + dir: r.pick(['asc', 'desc']), + }); + } else if (k === 8) { + if (ctx.inJunction) continue; + ops.push({op: 'limit', n: r.pick([0, 1, 1, 2, 3])}); + } else if (k === 9) { + if (ctx.inJunction || ctx.depth !== 0 || !data) continue; + const rows = data[table]; + if (rows.length === 0) continue; + const row = r.pick(rows); + ops.push({ + op: 'start', + row: r.bool(0.2) ? {...row, id: `${String(row.id)}x`} : row, + inclusive: r.bool(0.6) ? r.bool() : undefined, + }); + } else if (k === 10) { + if (ctx.inJunction || ctx.inExists) continue; + ops.push({op: 'one'}); + } + } + return ops; +} + +function makeProgram( + r: Random, + mode: Mode, + data: Record, +): Program { + const table = r.pick([ + 'issue', + 'issue', + 'comment', + 'user', + 'label', + 'revision', + ]); + const ctx: Ctx = { + depth: 0, + maxDepth: r.pick([2, 3, 4, 5, 5]), + inJunction: false, + inExists: false, + mode, + }; + const program: Program = { + table, + ops: randomOps(r, table, ctx, 1 + r.int(6), data), + }; + // Mostly a chain that nests to `maxDepth`. + if (r.bool(0.7)) { + const deeper = deepOp(r, table, ctx); + if (deeper) program.ops.splice(r.int(program.ops.length + 1), 0, deeper); + } + return program; +} + +// Running. + +function record(change: Change): RecordedChange { + switch (change[0]) { + case 0: + return {type: 'add', row: change[1].row}; + case 1: + return {type: 'remove', row: change[1].row}; + case 2: + return {type: 'edit', row: change[1].row, oldRow: change[2].row}; + case 3: + return { + type: 'child', + row: change[1].row, + child: { + relationshipName: change[2].relationshipName, + change: record(change[2].change), + }, + }; + } +} + +class Recorder implements Operator { + readonly #input: Input; + #output: Output | undefined; + readonly changes: RecordedChange[] = []; + constructor(input: Input) { + this.#input = input; + input.setOutput(this); + } + setOutput(output: Output): void { + this.#output = output; + } + getSchema() { + return this.#input.getSchema(); + } + destroy(): void { + this.#input.destroy(); + } + fetch(req: FetchRequest) { + return this.#input.fetch(req); + } + *push(change: Change, _pusher: InputBase) { + this.changes.push(record(change)); + yield* this.#output!.push(change, this); + } + *reconcile(_pusher: InputBase) { + if (this.#output?.reconcile) yield* this.#output.reconcile(this); + } +} + +type Step = {changes: JSONValue; data: JSONValue; notified: boolean}; +/** `batch` is set for an error thrown by a push rather than by hydrating. */ +type Result = + | {error: string; batch?: number} + | {initial: JSONValue; steps: Step[]}; + +function errorText(e: unknown) { + return String(e instanceof Error ? e.message : e); +} + +/** + * Hydrates a view, applies each batch of pushes, flushes once per batch, and + * records the changes, the view, and whether listeners were notified. + */ +function runBatches( + data: Record, + ast: AST, + format: Format, + batches: readonly Push[][], +): Result { + const sources: Record = {}; + for (const [name, table] of Object.entries(schema.tables)) { + const source = new MemorySource(name, table.columns, table.primaryKey); + for (const row of data[name] ?? []) { + consume(source.push(makeSourceChangeAdd(row))); + } + sources[name] = source; + } + let recorder: Recorder; + let view: ArrayView; + let notified = 0; + try { + const input = buildPipeline(ast, new TestBuilderDelegate(sources), 'q'); + recorder = new Recorder(input); + view = new ArrayView(recorder, format, true, () => {}); + view.addListener(() => notified++); + } catch (e) { + return {error: errorText(e)}; + } + const initial = encode(view.data); + const steps: Step[] = []; + for (const [i, batch] of batches.entries()) { + recorder.changes.length = 0; + notified = 0; + try { + for (const push of batch) { + const change = + push.type === 'edit' + ? makeSourceChangeEdit(push.row, push.oldRow!) + : push.type === 'add' + ? makeSourceChangeAdd(push.row) + : makeSourceChangeRemove(push.row); + consume(sources[push.table].push(change)); + } + } catch (e) { + return {error: errorText(e), batch: i}; + } + view.flush(); + steps.push({ + changes: encode(recorder.changes), + data: encode(view.data), + notified: notified > 0, + }); + } + return {initial, steps}; +} + +type Dataset = {data: Record; batches: Push[][]}; + +function generate( + name: string, + seeds: number[], + casesPerSeed: number, + mode: Mode, +) { + const sets: Dataset[] = []; + const cases: JSONValue[] = []; + let errors = 0; + for (const seed of seeds) { + const r = new Random(seed); + const first = sets.length; + for (let i = 0; i < 8; i++) { + const data = roundTrip(makeData(r, mode)); + sets.push({data, batches: roundTrip(makeBatches(r, mode, data, 30))}); + } + let made = 0; + let attempts = 0; + while (made < casesPerSeed && attempts++ < casesPerSeed * 8) { + const dataset = first + r.int(8); + const program = roundTrip(makeProgram(r, mode, sets[dataset].data)); + let built; + try { + built = buildProgram(schema, program); + } catch { + continue; + } + const result = runBatches( + sets[dataset].data, + built.ast, + built.format, + sets[dataset].batches, + ); + if ('error' in result) { + // Keep a few, so errors are compared too. + if (errors++ % 10 !== 0) continue; + } + made++; + cases.push( + encode({ + name: `seed ${seed} case ${attempts}`, + dataset, + program, + ast: built.ast, + format: built.format, + result, + }), + ); + } + } + emitGeneratedVectors( + name, + 'ivm-batches', + 'Hardening: deep nesting, junctions, flip/scalar exists, batched pushes with one flush each.', + cases, + {schema: schemaJSON(schema), datasets: encode(sets)}, + ); +} + +const seed = Number(process.env.HARDEN_IVM_SEED ?? 1); +const seedCount = Number(process.env.HARDEN_IVM_SEEDS ?? 1); +const casesPerSeed = Number(process.env.HARDEN_IVM_CASES ?? 2000); +const seeds = Array.from({length: seedCount}, (_, i) => seed + i); + +test('harden ivm fuzz', {timeout: 3_600_000}, () => { + generate('harden-ivm-fuzz', seeds, casesPerSeed, {special: false}); +}); + +test('harden ivm special numbers', {timeout: 3_600_000}, () => { + generate('harden-ivm-special', seeds, Math.ceil(casesPerSeed / 4), { + special: true, + }); +}); diff --git a/packages/zero-conformance/src/primitives/harden-json.test.ts b/packages/zero-conformance/src/primitives/harden-json.test.ts new file mode 100644 index 0000000000..91af4615e3 --- /dev/null +++ b/packages/zero-conformance/src/primitives/harden-json.test.ts @@ -0,0 +1,368 @@ +/** + * Differential fuzz corpora for the Swift client's untrusted-input paths: + * JSON texts mutated character by character (`JSON.parse` acceptance and + * `JSON.stringify` of the result), downstream messages mutated as text, and + * binary poke chunks mutated byte by byte. The Swift side must accept and + * reject exactly what JS does, and never trap. + */ +import {readFileSync} from 'node:fs'; +import {fileURLToPath} from 'node:url'; +import {test} from 'vitest'; +import * as v from '../../../shared/src/valita.ts'; +import {downstreamSchema} from '../../../zero-protocol/src/down.ts'; +import { + POKE_CHUNK_MESSAGE_TYPE, + pokePartsSchema, +} from '../../../zero-protocol/src/poke.ts'; +import {Random} from '../random.ts'; +import {emitGeneratedVectors, emitVectors} from '../vectors.ts'; + +// Seeds: change one to get a new corpus. +const JSON_SEED = 0x4a50e; +const JSON_FUZZ_SEED = 0x4a50f; +const DOWNSTREAM_SEED = 0xd0e7; +const POKE_SEED = 0xc4c7; + +const ATOMS = [ + '0', + '-0', + '1', + '-1', + '1.5', + '1e400', + '-1e400', + '1e-400', + '2.2250738585072011e-308', + '9007199254740993', + '-9223372036854775808', + '123456789012345678901234567890', + '1E+2', + 'true', + 'false', + 'null', + '""', + '"a"', + '"\\u00e9"', + '"e\\u0301"', + '"\\ud800"', + '"\\udc00"', + '"\\ud800\\ud800"', + '"\\udc00\\ud800"', + '"\\ud83d\\ude00"', + '"\\u0000"', + '"\\/"', + '"\u2028"', + '"\u{1f600}"', + '"\u007f"', +]; + +const KEYS = [ + '"a"', + '"__proto__"', + '"constructor"', + '""', + '"0"', + '"1"', + '"10"', + '"01"', + '"4294967295"', + '"4294967294"', + '"-1"', + '"\\u0000"', + '"\\ud800"', + '"\\u00e9"', + '"e\\u0301"', +]; + +/** Pieces a mutation inserts or substitutes. */ +const TOKENS = [ + '"', + '\\', + '\\u', + '\\ud800', + '\\udc00', + '\\uDBFF\\uDFFF', + '\\u00', + '\\x', + '{', + '}', + '[', + ']', + ',', + ':', + '-', + '+', + '.', + 'e', + 'E', + '0', + '00', + '9', + 'e999999999', + 'e-999999999', + 'true', + 'null', + 'nul', + 'NaN', + 'Infinity', + '\ufeff', + ' ', + '\t', + '\n', + '\r', + '\u00a0', + '\u2028', + '\u000b', + '\u000c', + '\u0000', + '\u001f', + '\u{1f600}', + 'é', + '"__proto__":', + '"a":1,"a":', +]; + +function value(r: Random, depth: number): string { + if (depth > 3 || r.bool(0.4)) { + return r.pick(ATOMS); + } + const n = r.int(4); + if (r.bool()) { + const items: string[] = []; + for (let i = 0; i < n; i++) items.push(value(r, depth + 1)); + return '[' + items.join(',') + ']'; + } + const entries: string[] = []; + for (let i = 0; i < n; i++) { + entries.push(r.pick(KEYS) + ':' + value(r, depth + 1)); + } + return '{' + entries.join(',') + '}'; +} + +/** A string edited a few times: insertions, deletions, substitutions. */ +function mutateText(r: Random, s: string): string { + const edits = 1 + r.int(3); + for (let e = 0; e < edits; e++) { + const i = r.int(s.length + 1); + switch (r.int(5)) { + case 0: + s = s.slice(0, i) + r.pick(TOKENS) + s.slice(i); + break; + case 1: + s = s.slice(0, i) + s.slice(i + 1 + r.int(3)); + break; + case 2: + s = s.slice(0, i) + r.pick(TOKENS) + s.slice(i + 1); + break; + case 3: { + const j = i + r.int(8); + s = s.slice(0, j) + s.slice(i, j) + s.slice(j); + break; + } + default: + s = s.slice(0, i); + } + } + return s; +} + +/** Whether `s` has no lone surrogate (`String#isWellFormed`). */ +function isWellFormed(s: string): boolean { + return !/\p{Cs}/u.test(s); +} + +type JSONCase = {input: string; ok: boolean; stringify?: string}; + +function jsonCase(input: string): JSONCase { + let parsed: unknown; + try { + parsed = JSON.parse(input); + } catch (e) { + if (!(e instanceof SyntaxError)) throw e; + return {input, ok: false}; + } + return {input, ok: true, stringify: JSON.stringify(parsed)}; +} + +function jsonCorpus(r: Random, count: number): JSONCase[] { + const cases: JSONCase[] = []; + const seen = new Set(); + const add = (input: string) => { + // The vector file is UTF-8: a lone surrogate would not survive it. + if (!isWellFormed(input) || seen.has(input)) return; + seen.add(input); + cases.push(jsonCase(input)); + }; + while (cases.length < count) { + const base = value(r, 0); + add(r.bool(0.25) ? base : mutateText(r, base)); + } + return cases; +} + +/** Nesting at the Swift parser's limit of 1,500, then mutated. */ +function deepCases(r: Random): JSONCase[] { + const cases: JSONCase[] = []; + for (const depth of [1499, 1500]) { + const arrays = '['.repeat(depth) + ']'.repeat(depth); + const objects = '{"a":'.repeat(depth - 1) + '{}' + '}'.repeat(depth - 1); + cases.push(jsonCase(arrays), jsonCase(objects)); + for (let i = 0; i < 4; i++) { + // Edits near the middle, where the innermost value is. + const m = arrays.length / 2; + cases.push( + jsonCase( + arrays.slice(0, m) + + mutateText(r, arrays.slice(m - 2, m + 2)) + + arrays.slice(m), + ), + ); + } + } + return cases; +} + +test('harden-json', () => { + const r = new Random(JSON_SEED); + emitVectors( + 'harden-json', + 'sweep-json', + 'Random and randomly edited JSON texts. `ok` is whether `JSON.parse` accepts the text; `stringify` is `JSON.stringify` of what it parsed.', + [...deepCases(r), ...jsonCorpus(r, 1500)], + undefined, + {compact: true}, + ); + emitGeneratedVectors( + 'harden-json-fuzz', + 'sweep-json', + 'Random and randomly edited JSON texts; see vectors/harden-json.json.', + jsonCorpus(new Random(JSON_FUZZ_SEED), 20000), + ); +}); + +type VectorCase = Record; + +function checkedInCases(name: string): VectorCase[] { + const path = fileURLToPath( + new URL(`../../vectors/${name}.json`, import.meta.url), + ); + return (JSON.parse(readFileSync(path, 'utf8')) as {cases: VectorCase[]}) + .cases; +} + +test('harden-downstream-text', () => { + const r = new Random(DOWNSTREAM_SEED); + const valid = checkedInCases('protocol-downstream') + .filter(c => c['ok'] === true && 'message' in c) + .map(c => JSON.stringify(c['message'])); + const cases: {text: string; ok: boolean}[] = []; + for (let i = 0; i < 6000; i++) { + const text = mutateText(r, r.pick(valid)); + if (!isWellFormed(text)) continue; + let ok = false; + try { + ok = v.test(JSON.parse(text), downstreamSchema, 'passthrough').ok; + } catch (e) { + if (!(e instanceof SyntaxError)) throw e; + } + cases.push({text, ok}); + } + emitGeneratedVectors( + 'harden-downstream-text', + 'harden-downstream-text', + 'Downstream messages edited as text. `ok` is whether `JSON.parse` and then `downstreamSchema` accept it.', + cases, + ); +}); + +function hex(b: Uint8Array): string { + return Buffer.from(b).toString('hex'); +} + +const INVALID_UTF8 = [ + [0xff], + [0xc0, 0xaf], + [0xed, 0xa0, 0x80], + [0xf4, 0x90, 0x80, 0x80], + [0xe4, 0xb8], + [0x80], + [0xef, 0xbb, 0xbf], + [0x00], +]; + +function mutateBytes(r: Random, b: number[]): number[] { + b = [...b]; + const edits = 1 + r.int(3); + for (let e = 0; e < edits; e++) { + const i = r.int(b.length + 1); + switch (r.int(5)) { + case 0: + if (i < b.length) b[i] ^= 1 << r.int(8); + break; + case 1: + if (i < b.length) b[i] = r.int(256); + break; + case 2: + b.splice(i, 0, ...r.pick(INVALID_UTF8)); + break; + case 3: + b.splice(i, 1 + r.int(4)); + break; + default: + b.length = Math.min(b.length, i); + } + } + return b; +} + +function pokeChunksOK(messages: Uint8Array[]): boolean { + const decoder = new TextDecoder('utf-8', {fatal: true}); + let text = ''; + try { + for (const m of messages) { + if (m[0] !== POKE_CHUNK_MESSAGE_TYPE) return false; + text += decoder.decode(m.subarray(1), {stream: true}); + } + text += decoder.decode(); + const parsed: unknown = JSON.parse(text); + return v.test(parsed, pokePartsSchema, 'passthrough').ok; + } catch { + return false; + } +} + +test('harden-poke-bytes', () => { + const r = new Random(POKE_SEED); + const valid = checkedInCases('protocol-poke-chunks') + .filter(c => c['ok'] === true) + .map(c => + (c['messages'] as string[]).flatMap(m => [ + ...Buffer.from(m, 'hex').subarray(1), + ]), + ) + .filter(p => p.length > 2); + const cases: {messages: string[]; ok: boolean}[] = []; + for (let i = 0; i < 4000; i++) { + const payload = mutateBytes(r, r.pick(valid)); + // Cut into up to three chunks, anywhere, even inside a UTF-8 sequence. + const cuts = [r.int(payload.length + 1), r.int(payload.length + 1)].sort( + (a, b) => a - b, + ); + const pieces = [ + payload.slice(0, cuts[0]), + payload.slice(cuts[0], cuts[1]), + payload.slice(cuts[1]), + ].filter((p, j) => j === 0 || p.length > 0); + const messages = pieces.map(p => { + const type = r.bool(0.02) ? r.int(256) : POKE_CHUNK_MESSAGE_TYPE; + return Uint8Array.from([type, ...p]); + }); + cases.push({messages: messages.map(hex), ok: pokeChunksOK(messages)}); + } + emitGeneratedVectors( + 'harden-poke-bytes', + 'harden-poke-bytes', + 'Binary poke chunks of valid pokes edited byte by byte, each message in hex. `ok` is whether `TextDecoder` (fatal), `JSON.parse`, and `pokePartsSchema` accept them.', + cases, + ); +}); diff --git a/packages/zero-conformance/vectors/client-harden-connection.json b/packages/zero-conformance/vectors/client-harden-connection.json new file mode 100644 index 0000000000..927b50aaf9 --- /dev/null +++ b/packages/zero-conformance/vectors/client-harden-connection.json @@ -0,0 +1,59 @@ +{"formatVersion":1,"kind":"client-scenarios","description":"Hardening scenarios for the connection and the sync protocol, run against the JS zero-client like vectors/client.json. See src/client/scenarios/harden-connection.ts.","shared":{"startTime":1700000000000},"cases":[ +{"name":"handshake: the initial poke right behind connected in one read","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":0},{"op":"receiveAll","messages":[["connected",{"wsid":"w1"}],["pokeStart",{"pokeID":"p1","baseCookie":null}],["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}],["pokeEnd",{"pokeID":"p1","cookie":"01"}]]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"handshake: a close right after connected in the same turn","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"socketClose","code":1006},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"ping: a pong that was never asked for, and one while connecting","steps":[{"op":"advance","ms":0},{"op":"receive","message":["pong",{}]},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pong",{}]},{"op":"advance","ms":5000},{"op":"receive","message":["pong",{}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"sent":[["ping",{}]],"state":{"name":"connected"}},{"state":{"name":"connected"}}]}, +{"name":"ping: a poke while waiting for the pong does not end the wait","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":5000},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1"}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":4999},{"op":"advance","ms":1}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["ping",{}]],"state":{"name":"connected"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"connecting","reason":"Server ping request failed"}}]}, +{"name":"error: an unknown error kind is an invalid message","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"Teapot","message":"short and stout"}]},{"op":"connect"},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Invalid message received from server: Expected literal value \"AuthInvalidated\", \"ClientNotFound\", \"InvalidConnectionRequest\", \"InvalidConnectionRequestBaseCookie\", \"InvalidConnectionRequestLastMutationID\", \"InvalidConnectionRequestClientDeleted\", \"InvalidMessage\", \"InvalidPush\", \"MutationRateLimited\", \"MutationFailed\", \"Unauthorized\", \"VersionNotSupported\", \"SchemaVersionNotSupported\", \"Internal\", \"Rebalance\", \"Rehome\", \"ServerOverloaded\", \"PushFailed\" or \"TransformFailed\" at 1.kind Got \"Teapot\"[\"error\",{\"kind\":\"Teapot\",\"message\":\"short and stout\"}]"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"error: an error without a message is an invalid message","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"Internal"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Invalid message received from server: Missing property message at 1[\"error\",{\"kind\":\"Internal\"}]"}}]}, +{"name":"error: ServerOverloaded with minBackoffMs above maxBackoffMs","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"ServerOverloaded","message":"busy","minBackoffMs":9000,"maxBackoffMs":2000}]},{"op":"advance","ms":1999},{"op":"advance","ms":1},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unexpected internal error: busy"}},{"state":{"name":"error","reason":"Unexpected internal error: busy"}},{"state":{"name":"error","reason":"Unexpected internal error: busy"}},{"state":{"name":"error","reason":"Unexpected internal error: busy"}}]}, +{"name":"error: ServerOverloaded with a zero maxBackoffMs reconnects at once","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"ServerOverloaded","message":"busy","maxBackoffMs":0}]},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unexpected internal error: busy"}},{"state":{"name":"error","reason":"Unexpected internal error: busy"}}]}, +{"name":"error: reconnectParams keep their order and skip the standard ones","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"Rehome","message":"go","maxBackoffMs":10,"reconnectParams":{"9":"y","10":"x","zone":"b","clientID":"evil","a":"z"}}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unexpected internal error: go"}},{"state":{"name":"error","reason":"Unexpected internal error: go"}}]}, +{"name":"error: a backoff error while in backoff from another","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"Rebalance","message":"a","minBackoffMs":8000,"reconnectParams":{"first":"1"}}]},{"op":"advance","ms":8000},{"op":"receive","message":["error",{"kind":"Rebalance","message":"b","maxBackoffMs":100}]},{"op":"advance","ms":100},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unexpected internal error: a"}},{"state":{"name":"error","reason":"Unexpected internal error: a"}},{"state":{"name":"error","reason":"Unexpected internal error: a"}},{"state":{"name":"error","reason":"Unexpected internal error: a"}},{"state":{"name":"error","reason":"Unexpected internal error: a"}}]}, +{"name":"error: MutationRateLimited while connecting","steps":[{"op":"advance","ms":0},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"receive","message":["error",{"kind":"MutationRateLimited","message":"slow"}]},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}}]}, +{"name":"error: ClientNotFound while connecting","steps":[{"op":"advance","ms":0},{"op":"receive","message":["error",{"kind":"ClientNotFound","message":"gone","origin":"zeroCache"}]},{"op":"advance","ms":10000},{"op":"connect"},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"closed":[0],"state":{"name":"error","reason":"gone"},"events":["onClientStateNotFound"]},{"state":{"name":"error","reason":"gone"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"closed":[1],"state":{"name":"connecting","reason":"Server did not acknowledge the connection within 10 seconds. Connect events: [[\"2023-11-14T22:13:30.000Z\",\"starting the connection\"],[\"2023-11-14T22:13:30.000Z\",\"reading the cookie\"],[\"2023-11-14T22:13:30.000Z\",\"reading the client group ID\"],[\"2023-11-14T22:13:30.000Z\",\"initializing active clients\"],[\"2023-11-14T22:13:30.000Z\",\"reading the profile ID\"],[\"2023-11-14T22:13:30.000Z\",\"reading deleted clients\"],[\"2023-11-14T22:13:30.000Z\",\"reading desired queries\"],[\"2023-11-14T22:13:30.000Z\",\"creating the WebSocket\"],[\"2023-11-14T22:13:30.000Z\",\"waiting for the server acknowledgement\"]]"}}]}, +{"name":"error: InvalidConnectionRequestBaseCookie while connecting","steps":[{"op":"advance","ms":0},{"op":"receive","message":["error",{"kind":"InvalidConnectionRequestBaseCookie","message":"ahead","origin":"zeroCache"}]},{"op":"connect"},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"closed":[0],"state":{"name":"error","reason":"ahead"},"events":["onClientStateNotFound"]},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"}}]}, +{"name":"error: SchemaVersionNotSupported while connecting, then connect()","steps":[{"op":"advance","ms":0},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"receive","message":["error",{"kind":"SchemaVersionNotSupported","message":"old","origin":"zeroCache"}]},{"op":"connect"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m2","mutator":"issue.create","args":{"id":"i2"}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"closed":[0],"state":{"name":"error","reason":"old"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"old"}}}},"events":[{"onUpdateNeeded":{"type":"SchemaVersionNotSupported","message":"old"}}]},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"old"}}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"old"}}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"old"}}},"m2":{"client":{"type":"success"},"server":"pending"}}}]}, +{"name":"error: VersionNotSupported then connect()","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"VersionNotSupported","message":"old"}]},{"op":"connect"},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unexpected internal error: old"},"events":[{"onUpdateNeeded":{"type":"VersionNotSupported","message":"old"}}]},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"error: two errors in one read","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receiveAll","messages":[["error",{"kind":"Internal","message":"first","origin":"zeroCache"}],["error",{"kind":"AuthInvalidated","message":"second","origin":"zeroCache"}]]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"first"}}]}, +{"name":"error: TransformFailed 401 from the API server, connect() with a token","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"TransformFailed","origin":"zeroCache","reason":"http","status":401,"bodyPreview":"","message":"no","queryIDs":["1y7zr0i1xir4ek"]}]},{"op":"connect","auth":"token-2"},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"closed":[0],"state":{"name":"needs-auth","reason":{"type":"query","status":401}},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}],"authToken":"token-2"}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"auth: connect() with an empty token while needs-auth","options":{"auth":"token-1"},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"AuthInvalidated","message":"x","origin":"zeroCache"}]},{"op":"connect","auth":""},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"connect","auth":"token-3"}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}],"authToken":"token-1"}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"needs-auth","reason":{"type":"zero-cache","reason":"x"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["updateAuth",{"auth":"token-3"}]],"state":{"name":"connected"}}]}, +{"name":"auth: connect() twice while needs-auth","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"AuthInvalidated","message":"x","origin":"zeroCache"}]},{"op":"connect","auth":"a"},{"op":"connect","auth":"b"},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"needs-auth","reason":{"type":"zero-cache","reason":"x"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}],"authToken":"a"}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"auth: connect() in needs-auth while hidden","options":{"hiddenTabDisconnectDelay":0},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"AuthInvalidated","message":"x","origin":"zeroCache"}]},{"op":"visibility","state":"hidden"},{"op":"advance","ms":10},{"op":"connect","auth":"a"},{"op":"advance","ms":10000},{"op":"visibility","state":"visible"},{"op":"advance","ms":10},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"needs-auth","reason":{"type":"zero-cache","reason":"x"}}},{"state":{"name":"needs-auth","reason":{"type":"zero-cache","reason":"x"}}},{"state":{"name":"needs-auth","reason":{"type":"zero-cache","reason":"x"}}},{"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}],"authToken":"a"}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"visibility: hidden and visible again within one turn","options":{"hiddenTabDisconnectDelay":0},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"visibility","state":"hidden"},{"op":"visibility","state":"visible"},{"op":"advance","ms":10},{"op":"visibility","state":"hidden"},{"op":"advance","ms":1},{"op":"visibility","state":"visible"},{"op":"advance","ms":1},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"disconnected","reason":"Connection closed because tab was hidden"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"visibility: hidden during the backoff","options":{"hiddenTabDisconnectDelay":0},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"socketClose","code":1006},{"op":"socketClose","code":1006},{"op":"visibility","state":"hidden"},{"op":"advance","ms":5000},{"op":"advance","ms":60000},{"op":"visibility","state":"visible"},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"}},{"closed":[1],"state":{"name":"connecting","reason":"WebSocket connection closed abruptly"}},{"state":{"name":"connecting","reason":"WebSocket connection closed abruptly"}},{"state":{"name":"connecting","reason":"WebSocket connection closed abruptly"}},{"state":{"name":"disconnected","reason":"Zero was unable to connect for 60 seconds and was disconnected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"disconnected","reason":"Zero was unable to connect for 60 seconds and was disconnected"}},{"state":{"name":"connected"}}]}, +{"name":"visibility: hidden in the error state, then connect() and visible","options":{"hiddenTabDisconnectDelay":0},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"Internal","message":"x","origin":"zeroCache"}]},{"op":"visibility","state":"hidden"},{"op":"advance","ms":1},{"op":"connect"},{"op":"advance","ms":1},{"op":"visibility","state":"visible"},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"x"}},{"state":{"name":"error","reason":"x"}},{"state":{"name":"error","reason":"x"}},{"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"timeout: connect timeout, then the late connected on the old socket","steps":[{"op":"advance","ms":10000},{"op":"receive","message":["connected",{"wsid":"w1"}],"socket":0},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting","reason":"Server did not acknowledge the connection within 10 seconds. Connect events: [[\"2023-11-14T22:13:20.000Z\",\"starting the connection\"],[\"2023-11-14T22:13:20.000Z\",\"reading the cookie\"],[\"2023-11-14T22:13:20.000Z\",\"reading the client group ID\"],[\"2023-11-14T22:13:20.000Z\",\"initializing active clients\"],[\"2023-11-14T22:13:20.000Z\",\"reading the profile ID\"],[\"2023-11-14T22:13:20.000Z\",\"reading deleted clients\"],[\"2023-11-14T22:13:20.000Z\",\"reading desired queries\"],[\"2023-11-14T22:13:20.000Z\",\"creating the WebSocket\"],[\"2023-11-14T22:13:20.000Z\",\"waiting for the server acknowledgement\"]]"}},{"state":{"name":"connecting","reason":"Server did not acknowledge the connection within 10 seconds. Connect events: [[\"2023-11-14T22:13:20.000Z\",\"starting the connection\"],[\"2023-11-14T22:13:20.000Z\",\"reading the cookie\"],[\"2023-11-14T22:13:20.000Z\",\"reading the client group ID\"],[\"2023-11-14T22:13:20.000Z\",\"initializing active clients\"],[\"2023-11-14T22:13:20.000Z\",\"reading the profile ID\"],[\"2023-11-14T22:13:20.000Z\",\"reading deleted clients\"],[\"2023-11-14T22:13:20.000Z\",\"reading desired queries\"],[\"2023-11-14T22:13:20.000Z\",\"creating the WebSocket\"],[\"2023-11-14T22:13:20.000Z\",\"waiting for the server acknowledgement\"]]"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"disconnect timeout: offline, then an error on the next attempt","options":{"disconnectTimeoutMs":5000},"steps":[{"op":"advance","ms":10000},{"op":"advance","ms":5000},{"op":"receive","message":["error",{"kind":"Internal","message":"x","origin":"zeroCache"}]},{"op":"connect"},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"disconnected","reason":"Zero was unable to connect for 5 seconds and was disconnected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"disconnected","reason":"Zero was unable to connect for 5 seconds and was disconnected"}},{"closed":[1],"state":{"name":"error","reason":"x"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"disconnect timeout: offline, then a Rebalance on the next attempt","options":{"disconnectTimeoutMs":5000},"steps":[{"op":"advance","ms":10000},{"op":"advance","ms":5000},{"op":"receive","message":["error",{"kind":"Rebalance","message":"x","minBackoffMs":7000}]},{"op":"advance","ms":6999},{"op":"advance","ms":1},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"disconnected","reason":"Zero was unable to connect for 5 seconds and was disconnected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"disconnected","reason":"Zero was unable to connect for 5 seconds and was disconnected"}},{"closed":[1],"state":{"name":"error","reason":"Unexpected internal error: x"}},{"state":{"name":"error","reason":"Unexpected internal error: x"}},{"state":{"name":"error","reason":"Unexpected internal error: x"}},{"state":{"name":"error","reason":"Unexpected internal error: x"}}]}, +{"name":"close(): messages after close are ignored","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"close"},{"op":"receive","socket":0,"message":["pong",{}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}],"socket":0},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}],"socket":0},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}],"socket":0},{"op":"socketClose","code":1006,"socket":0},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"closed":[0],"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[],"resultType":"error","notifications":1}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[],"resultType":"error","notifications":0}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[],"resultType":"error","notifications":0}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[],"resultType":"error","notifications":0}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[],"resultType":"error","notifications":0}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[],"resultType":"error","notifications":0}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[],"resultType":"error","notifications":0}}}]}, +{"name":"close(): during the backoff","steps":[{"op":"advance","ms":0},{"op":"socketClose","code":1006},{"op":"close"},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"closed":[0],"state":{"name":"connecting","reason":"WebSocket connection closed abruptly"}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}}]}, +{"name":"close(): in needs-auth, then connect()","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"AuthInvalidated","message":"x","origin":"zeroCache"}]},{"op":"close"},{"op":"connect","auth":"b"},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"needs-auth","reason":{"type":"zero-cache","reason":"x"}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}}]}, +{"name":"poke: a canceled pokeEnd for another poke is an error","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"01","cancel":true}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Poke processing error: pokeEnd for p2, when receiving p1"}}]}, +{"name":"poke: a canceled pokeEnd without a poke is an error","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"01","cancel":true}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Poke processing error: pokeEnd for p2, when receiving undefined"}}]}, +{"name":"poke: rows missing primary key columns","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"title":"no id"}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"closed":[0],"state":{"name":"error","reason":"Poke processing error: TypeError: Expected string, number or boolean. Got undefined"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"error","reason":"Poke processing error: TypeError: Expected string, number or boolean. Got undefined"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"poke: update of a row the client does not have","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"update","tableName":"issues","id":{"id":"nope"},"merge":{"title":"T"}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"title":"T"}],"resultType":"complete","notifications":2}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"title":"T"}],"resultType":"complete","notifications":0}}}]}, +{"name":"poke: a row with a numeric primary key in a string column","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":1,"title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}},{"op":"put","tableName":"issues","value":{"id":"1","title":"1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"1","title":"1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"1","title":"1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"poke: desiredQueriesPatches are applied in the order the server sent them","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","desiredQueriesPatches":{"~other":[{"op":"clear"}],"$clientID":[{"op":"put","hash":"1y7zr0i1xir4ek"}]}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"socketClose","code":1006},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie","01"],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"poke: an empty pokeID and an empty cookie","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"","cookie":""}]},{"op":"receive","message":["pokeStart",{"pokeID":"","baseCookie":""}]},{"op":"receive","message":["pokePart",{"pokeID":"","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"poke: a poke while connecting, before connected","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":0},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"poke: a poke that spans a reconnect is dropped","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"socketClose","code":1006},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"closed":[1],"state":{"name":"error","reason":"Poke processing error: pokePart for p1, when receiving undefined"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"error","reason":"Poke processing error: pokePart for p1, when receiving undefined"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"poke: an applied poke whose frame is still waiting when the socket drops","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receiveAll","messages":[["pokeStart",{"pokeID":"p1","baseCookie":null}],["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}],["pokeEnd",{"pokeID":"p1","cookie":"01"}]]},{"op":"socketClose","code":1006},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie","01"],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"message: a pushResponse for another client is ignored","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"other","id":1},"result":{}}]}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"closed":[0],"state":{"name":"error","reason":"received mutation for the wrong client"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"received mutation for the wrong client"}}}}},{"state":{"name":"error","reason":"received mutation for the wrong client"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"received mutation for the wrong client"}}}}}]}, +{"name":"message: a pushResponse error for a mutation not yet sent","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pushResponse",{"error":"unsupportedPushVersion","mutationIDs":[{"clientID":"$clientID","id":7}]}]},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unsupported push version"}},{"state":{"name":"error","reason":"Unsupported push version"},"mutations":{"m1":{"client":{"type":"error","error":{"type":"zero","message":"Unsupported push version"}},"server":{"type":"error","error":{"type":"zero","message":"Unsupported push version"}}}}}]}, +{"name":"message: a pull response with no pending pull","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":4000},{"op":"receive","message":["pull",{"cookie":"05","requestID":"r","lastMutationIDChanges":{}}]},{"op":"advance","ms":4000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}}]}, +{"name":"message: a transformError for a query nobody asked for","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["transformError",[{"error":"app","id":"zzz","name":"nope","message":"x"}]]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"message: deleteClients from the server","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["deleteClients",{"clientIDs":["x"]}]},{"op":"socketClose","code":1006},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"message: a binary frame of an unknown type","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receiveBinary","bytes":[7,1,2]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unknown binary message type: 7"}}]}, +{"name":"message: an empty binary frame","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receiveBinary","bytes":[]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Unknown binary message type: undefined"}}]}, +{"name":"message: a tuple with an extra element","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pong",{},1]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"Invalid message received from server: Invalid union value: [\"pong\",{},1][\"pong\",{},1]"}}]}, +{"name":"socket: a close code 1009 while connecting","steps":[{"op":"advance","ms":0},{"op":"socketClose","code":1009},{"op":"advance","ms":10000},{"op":"connect"},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"closed":[0],"state":{"name":"error","reason":"A WebSocket message exceeded the server message size limit. This is usually caused by a mutation's args containing a large value such as a base64-encoded image. Consider uploading large files to object storage and storing only the URL in Zero."},"events":["onClientStateNotFound"]},{"state":{"name":"error","reason":"A WebSocket message exceeded the server message size limit. This is usually caused by a mutation's args containing a large value such as a base64-encoded image. Consider uploading large files to object storage and storing only the URL in Zero."}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"closed":[1],"state":{"name":"connecting","reason":"Server did not acknowledge the connection within 10 seconds. Connect events: [[\"2023-11-14T22:13:30.000Z\",\"starting the connection\"],[\"2023-11-14T22:13:30.000Z\",\"reading the cookie\"],[\"2023-11-14T22:13:30.000Z\",\"reading the client group ID\"],[\"2023-11-14T22:13:30.000Z\",\"initializing active clients\"],[\"2023-11-14T22:13:30.000Z\",\"reading the profile ID\"],[\"2023-11-14T22:13:30.000Z\",\"reading deleted clients\"],[\"2023-11-14T22:13:30.000Z\",\"reading desired queries\"],[\"2023-11-14T22:13:30.000Z\",\"creating the WebSocket\"],[\"2023-11-14T22:13:30.000Z\",\"waiting for the server acknowledgement\"]]"}}]}, +{"name":"socket: a close reported twice","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"socketClose","code":1006},{"op":"socketClose","code":1006,"socket":0},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"socket: a close code 1009 after an error","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["error",{"kind":"Internal","message":"x","origin":"zeroCache"}]},{"op":"socketClose","code":1009,"socket":0}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"error","reason":"x"}},{"state":{"name":"error","reason":"x"}}]}, +{"name":"header: an auth token over maxHeaderLength is still sent","options":{"maxHeaderLength":10,"auth":"a-long-token-for-a-tiny-header"},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"authToken":"a-long-token-for-a-tiny-header"}}],"state":{"name":"connecting"}},{"sent":[["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}}}]],"state":{"name":"connected"}}]}, +{"name":"reentrancy: connect({auth}) from a needs-auth listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"onState","state":"needs-auth","then":{"op":"connect","auth":"token-2"}},{"op":"receive","message":["error",{"kind":"AuthInvalidated","message":"x","origin":"zeroCache"}]},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}],"authToken":"token-2"}}],"closed":[0],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"reentrancy: connect() from an error listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"onState","state":"error","then":{"op":"connect"}},{"op":"receive","message":["error",{"kind":"Internal","message":"x","origin":"zeroCache"}]},{"op":"receive","message":["connected",{"wsid":"w1"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"}},{"state":{"name":"connected"}}]}, +{"name":"reentrancy: close() from a connected listener","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"advance","ms":0},{"op":"onState","state":"connected","then":{"op":"close"}},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"closed":[0],"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"error","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was explicitly closed by calling zero.close()"}}}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"error","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was explicitly closed by calling zero.close()"}}}}}]}, +{"name":"reentrancy: close() from a connecting listener after a drop","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"onState","state":"connecting","then":{"op":"close"}},{"op":"socketClose","code":1006},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"}},{"closed":[0],"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}}]}, +{"name":"reentrancy: close() from a disconnected listener","options":{"disconnectTimeoutMs":5000},"steps":[{"op":"advance","ms":0},{"op":"onState","state":"disconnected","then":{"op":"close"}},{"op":"advance","ms":10000},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"}}]}, +{"name":"reentrancy: close() from a needs-auth listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"onState","state":"needs-auth","then":{"op":"close"}},{"op":"receive","message":["error",{"kind":"Unauthorized","message":"x","origin":"zeroCache"}]},{"op":"advance","ms":10000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"closed":[0],"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was explicitly closed by calling zero.close()"}}}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was explicitly closed by calling zero.close()"}}}}}]} +]} diff --git a/packages/zero-conformance/vectors/client-harden-mutations.json b/packages/zero-conformance/vectors/client-harden-mutations.json new file mode 100644 index 0000000000..f0ca25bcbd --- /dev/null +++ b/packages/zero-conformance/vectors/client-harden-mutations.json @@ -0,0 +1,27 @@ +{"formatVersion":1,"kind":"client-scenarios","description":"Hardening scenarios for mutations, run against the JS zero-client like client.json (see src/client/harness.ts and src/client/scenarios/harden-mutations.ts).","shared":{"startTime":1700000000000},"cases":[ +{"name":"harden crud: insert null-fills omitted columns and the transaction reads its own writes","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"only a title"}}],"read":["issue"]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i2","title":"t","ownerId":"u9"}}],"read":["issue"]}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"only a title"}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"only a title","description":null,"closed":null,"ownerId":null,"createdAt":null}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"only a title","description":null,"closed":null,"ownerId":null,"createdAt":null}]}}},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i2","title":"t","ownerId":"u9"}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"only a title","description":null,"closed":null,"ownerId":null,"createdAt":null},{"id":"i2","title":"t","ownerId":"u9","description":null,"closed":null,"createdAt":null}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"only a title","description":null,"closed":null,"ownerId":null,"createdAt":null}]}}},"server":"pending"},"m2":{"client":{"type":"success","data":{"mutationID":2,"read":{"issue":[{"id":"i1","title":"only a title","description":null,"closed":null,"ownerId":null,"createdAt":null},{"id":"i2","title":"t","ownerId":"u9","description":null,"closed":null,"createdAt":null}]}}},"server":"pending"}}}]}, +{"name":"harden crud: a column the schema does not have","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x"}}],"read":["issue"]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"update","table":"issue","value":{"id":"i1","other":5,"title":"u"}}],"read":["issue"]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[{"op":"upsert","table":"issue","value":{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1,"more":true}}],"read":["issue"]}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x"}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x"}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x"}]}}},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"update","table":"issue","value":{"id":"i1","other":5,"title":"u"}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"u","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x","other":5}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x"}]}}},"server":"pending"},"m2":{"client":{"type":"success","data":{"mutationID":2,"read":{"issue":[{"id":"i1","title":"u","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x","other":5}]}}},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"upsert","table":"issue","value":{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1,"more":true}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"u","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x","other":5},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1,"more":true}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x"}]}}},"server":"pending"},"m2":{"client":{"type":"success","data":{"mutationID":2,"read":{"issue":[{"id":"i1","title":"u","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x","other":5}]}}},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"u","description":"","closed":false,"ownerId":null,"createdAt":1,"extra":"x","other":5},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1,"more":true}]}}},"server":"pending"}}}]}, +{"name":"harden crud: every operation on one row in one transaction","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"first","description":"","closed":false,"ownerId":null,"createdAt":1}},{"op":"insert","table":"issue","value":{"id":"i1","title":"dup","description":"","closed":false,"ownerId":null,"createdAt":1}},{"op":"upsert","table":"issue","value":{"id":"i1","title":"merged"}},{"op":"update","table":"issue","value":{"id":"i9","title":"missing"}},{"op":"delete","table":"issue","value":{"id":"i9"}},{"op":"update","table":"issue","value":{"id":"i1","ownerId":"u1","closed":true}}],"read":["issue"]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"delete","table":"issue","value":{"id":"i1","title":"ignored"}},{"op":"update","table":"issue","value":{"id":"i1","title":"after delete"}},{"op":"insert","table":"issue","value":{"id":"i1","title":"again"}},{"op":"update","table":"issue","value":{"id":"i1","title":null,"ownerId":null}}],"read":["issue"]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[{"op":"update","table":"issue","value":{"id":"i1"}}]}},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[{"op":"upsert","table":"issue","value":{"id":"i1","title":null}}]}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"first","description":"","closed":false,"ownerId":null,"createdAt":1}},{"op":"insert","table":"issue","value":{"id":"i1","title":"dup","description":"","closed":false,"ownerId":null,"createdAt":1}},{"op":"upsert","table":"issue","value":{"id":"i1","title":"merged"}},{"op":"update","table":"issue","value":{"id":"i9","title":"missing"}},{"op":"delete","table":"issue","value":{"id":"i9"}},{"op":"update","table":"issue","value":{"id":"i1","ownerId":"u1","closed":true}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"merged","description":"","closed":true,"ownerId":"u1","createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"merged","description":"","closed":true,"ownerId":"u1","createdAt":1}]}}},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"delete","table":"issue","value":{"id":"i1","title":"ignored"}},{"op":"update","table":"issue","value":{"id":"i1","title":"after delete"}},{"op":"insert","table":"issue","value":{"id":"i1","title":"again"}},{"op":"update","table":"issue","value":{"id":"i1","title":null,"ownerId":null}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":null,"description":null,"closed":null,"ownerId":null,"createdAt":null}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"merged","description":"","closed":true,"ownerId":"u1","createdAt":1}]}}},"server":"pending"},"m2":{"client":{"type":"success","data":{"mutationID":2,"read":{"issue":[{"id":"i1","title":null,"description":null,"closed":null,"ownerId":null,"createdAt":null}]}}},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"update","table":"issue","value":{"id":"i1"}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":null,"description":null,"closed":null,"ownerId":null,"createdAt":null}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"merged","description":"","closed":true,"ownerId":"u1","createdAt":1}]}}},"server":"pending"},"m2":{"client":{"type":"success","data":{"mutationID":2,"read":{"issue":[{"id":"i1","title":null,"description":null,"closed":null,"ownerId":null,"createdAt":null}]}}},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"upsert","table":"issue","value":{"id":"i1","title":null}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":null,"description":null,"closed":null,"ownerId":null,"createdAt":null}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"merged","description":"","closed":true,"ownerId":"u1","createdAt":1}]}}},"server":"pending"},"m2":{"client":{"type":"success","data":{"mutationID":2,"read":{"issue":[{"id":"i1","title":null,"description":null,"closed":null,"ownerId":null,"createdAt":null}]}}},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"}}}]}, +{"name":"harden crud: a compound primary key","steps":[{"op":"materialize","view":"l","query":"issueLabels.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"04vzt4r1u04msd"}],"rowsPatch":[{"op":"put","tableName":"issueLabel","value":{"issueId":"a","labelId":"2"}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issueLabel","value":{"issueId":"a","labelId":"1"}},{"op":"insert","table":"issueLabel","value":{"issueId":"a","labelId":"2","extra":1}},{"op":"upsert","table":"issueLabel","value":{"issueId":"b","labelId":"1"}},{"op":"delete","table":"issueLabel","value":{"issueId":"a","labelId":"2"}}],"read":["issueLabel"]}},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","lastMutationIDChanges":{"$clientID":1},"rowsPatch":[{"op":"del","tableName":"issueLabel","id":{"issueId":"a","labelId":"2"}},{"op":"put","tableName":"issueLabel","value":{"issueId":"a","labelId":"1"}},{"op":"put","tableName":"issueLabel","value":{"issueId":"b","labelId":"1"}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"04vzt4r1u04msd","name":"issueLabels.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"l":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"l":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"l":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"l":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"l":{"data":[{"issueId":"a","labelId":"2"}],"resultType":"complete","notifications":2}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issueLabel","value":{"issueId":"a","labelId":"1"}},{"op":"insert","table":"issueLabel","value":{"issueId":"a","labelId":"2","extra":1}},{"op":"upsert","table":"issueLabel","value":{"issueId":"b","labelId":"1"}},{"op":"delete","table":"issueLabel","value":{"issueId":"a","labelId":"2"}}],"read":["issueLabel"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"l":{"data":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issueLabel":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"l":{"data":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issueLabel":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"l":{"data":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issueLabel":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"l":{"data":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success","data":{"mutationID":1,"read":{"issueLabel":[{"issueId":"a","labelId":"1"},{"issueId":"b","labelId":"1"}]}}},"server":{"type":"success"}}}}]}, +{"name":"harden crud: a missing or null primary key","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"title":"no id"}}]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":null,"title":"null id"}}]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[{"op":"delete","table":"issue","value":{}}]}},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[{"op":"update","table":"issueLabel","value":{"issueId":"a"}}]}},{"op":"mutate","mutation":"m5","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":{"nested":1}}}]}},{"op":"mutate","mutation":"m6","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"ok","title":"ok","description":"","closed":false,"ownerId":null,"createdAt":1}}],"read":["issue"]}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}}}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}}},"m3":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}}},"m3":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m4":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}}},"m3":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m4":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m5":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got object","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got object","details":{"name":"TypeError"}}}}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"ok","title":"ok","description":"","closed":false,"ownerId":null,"createdAt":1}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"ok","title":"ok","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got null","details":{"name":"TypeError"}}}},"m3":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m4":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got undefined","details":{"name":"TypeError"}}}},"m5":{"client":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got object","details":{"name":"TypeError"}}},"server":{"type":"error","error":{"type":"app","message":"Expected string, number or boolean. Got object","details":{"name":"TypeError"}}}},"m6":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"ok","title":"ok","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}}]}, +{"name":"harden throw: a mutator that throws after updating and deleting leaves the rows alone","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}},{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"update","table":"issue","value":{"id":"i1","closed":true,"title":"changed"}},{"op":"delete","table":"issue","value":{"id":"i2"}},{"op":"insert","table":"issue","value":{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}},{"op":"upsert","table":"issue","value":{"id":"i1","title":"changed again"}}],"fail":"midway","details":{"step":4}}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[],"read":["issue"]}},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"midway","details":{"step":4}}},"server":{"type":"error","error":{"type":"app","message":"midway","details":{"step":4}}}}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"midway","details":{"step":4}}},"server":{"type":"error","error":{"type":"app","message":"midway","details":{"step":4}}}},"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"midway","details":{"step":4}}},"server":{"type":"error","error":{"type":"app","message":"midway","details":{"step":4}}}},"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}}]}, +{"name":"harden throw: details of an app error the mutator throws","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[],"fail":"no details"}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[],"fail":"null details","details":null}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[],"fail":"zero details","details":0}},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[],"fail":"","details":[1,"two"]}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"no details"}},"server":{"type":"error","error":{"type":"app","message":"no details"}}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"no details"}},"server":{"type":"error","error":{"type":"app","message":"no details"}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"null details"}},"server":{"type":"error","error":{"type":"app","message":"null details"}}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"no details"}},"server":{"type":"error","error":{"type":"app","message":"no details"}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"null details"}},"server":{"type":"error","error":{"type":"app","message":"null details"}}},"m3":{"client":{"type":"error","error":{"type":"app","message":"zero details","details":0}},"server":{"type":"error","error":{"type":"app","message":"zero details","details":0}}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"no details"}},"server":{"type":"error","error":{"type":"app","message":"no details"}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"null details"}},"server":{"type":"error","error":{"type":"app","message":"null details"}}},"m3":{"client":{"type":"error","error":{"type":"app","message":"zero details","details":0}},"server":{"type":"error","error":{"type":"app","message":"zero details","details":0}}},"m4":{"client":{"type":"error","error":{"type":"app","message":"","details":[1,"two"]}},"server":{"type":"error","error":{"type":"app","message":"","details":[1,"two"]}}}}}]}, +{"name":"harden rebase: a mutator that throws only on rebase drops its writes for the next mutation","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}}],"failOnRebase":true}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"update","table":"issue","value":{"id":"mine","title":"edited"}}]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[],"read":["issue"]}},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[],"read":["issue"]}},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","lastMutationIDChanges":{"$clientID":2}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p4","baseCookie":"03"}]},{"op":"receive","message":["pokePart",{"pokeID":"p4","lastMutationIDChanges":{"$clientID":4},"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"mine","title":"server","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p4","cookie":"04"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}}],"failOnRebase":true}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"update","table":"issue","value":{"id":"mine","title":"edited"}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"},"m4":{"client":{"type":"success","data":{"mutationID":4,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"},"m4":{"client":{"type":"success","data":{"mutationID":4,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"},"m4":{"client":{"type":"success","data":{"mutationID":4,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"},"m4":{"client":{"type":"success","data":{"mutationID":4,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"},"m4":{"client":{"type":"success","data":{"mutationID":4,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"},"m4":{"client":{"type":"success","data":{"mutationID":4,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"server","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":3,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":{"type":"success"}},"m4":{"client":{"type":"success","data":{"mutationID":4,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":{"type":"success"}}}}]}, +{"name":"harden restart: a pending mutation that throws on rebase, after a restart","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}}],"failOnRebase":true}},{"op":"advance","ms":2000},{"op":"restart"},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":0},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[],"read":["issue"]}},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","lastMutationIDChanges":{"$clientID.0":1,"$clientID":1}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}}],"failOnRebase":true}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie","01"],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":0}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":0}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}],["push",{"timestamp":1700000002000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID.0","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"mine","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}}],"failOnRebase":true}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000002000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000002000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":0}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":0}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":0}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m2":{"client":{"type":"success","data":{"mutationID":1,"read":{"issue":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":{"type":"success"}}}}]}, +{"name":"harden rebase: pending insert, delete and update over server changes to the same rows","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"b","title":"B","description":"","closed":false,"owner_id":null,"createdAt":1}},{"op":"put","tableName":"issues","value":{"id":"c","title":"C","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"delete","table":"issue","value":{"id":"b"}}]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[{"op":"update","table":"issue","value":{"id":"c","title":"edited"}}]}},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[{"op":"upsert","table":"issue","value":{"id":"d","title":"upserted"}}]}},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"a","title":"server a","description":"","closed":false,"owner_id":null,"createdAt":1}},{"op":"update","tableName":"issues","id":{"id":"b"},"merge":{"title":"server b"}},{"op":"del","tableName":"issues","id":{"id":"c"}},{"op":"put","tableName":"issues","value":{"id":"d","title":"server d","description":"","closed":true,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"mutate","mutation":"m5","mutator":"harden.ops","args":{"ops":[],"read":["issue"]}},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","lastMutationIDChanges":{"$clientID":4}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"B","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"c","title":"C","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"B","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"c","title":"C","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"delete","table":"issue","value":{"id":"b"}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"c","title":"C","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"update","table":"issue","value":{"id":"c","title":"edited"}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"c","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"upsert","table":"issue","value":{"id":"d","title":"upserted"}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"c","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":null,"closed":null,"ownerId":null,"createdAt":null}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"c","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":null,"closed":null,"ownerId":null,"createdAt":null}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"mine","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"c","title":"edited","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":null,"closed":null,"ownerId":null,"createdAt":null}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":5,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success","data":{"mutationID":5,"read":{"issue":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success","data":{"mutationID":5,"read":{"issue":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success","data":{"mutationID":5,"read":{"issue":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"server b","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"server d","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":{"type":"success"}},"m4":{"client":{"type":"success"},"server":{"type":"success"}},"m5":{"client":{"type":"success","data":{"mutationID":5,"read":{"issue":[{"id":"a","title":"server a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"upserted","description":"","closed":true,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}}]}, +{"name":"harden reconnect: mutations the client gave up on are pushed again and late results ignored","options":{"disconnectTimeoutMs":10000},"steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"socketClose","code":1006},{"op":"advance","ms":5000},{"op":"advance","ms":5000},{"op":"advance","ms":1000},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"c","title":"c","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":1},"result":{"error":"app","message":"late"}},{"id":{"clientID":"$clientID","id":2},"result":{"data":"late"}}]}]},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"d","title":"d","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","lastMutationIDChanges":{"$clientID":3},"gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"a","title":"A","description":"","closed":false,"owner_id":null,"createdAt":1}},{"op":"put","tableName":"issues","value":{"id":"b","title":"B","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"closed":[1],"state":{"name":"disconnected","reason":"Zero was unable to connect for 10 seconds and was disconnected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}}}},{"state":{"name":"disconnected","reason":"Zero was unable to connect for 10 seconds and was disconnected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}}}},{"state":{"name":"disconnected","reason":"Zero was unable to connect for 10 seconds and was disconnected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"disconnected","reason":"Zero was unable to connect for 10 seconds and was disconnected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}}}},{"sent":[["push",{"timestamp":1700000016000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000016000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}}}},{"sent":[["push",{"timestamp":1700000016000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000016000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"d","title":"d","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"d","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m4":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"d","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m4":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"d","title":"d","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m4":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"A","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"B","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m3":{"client":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}},"server":{"type":"error","error":{"type":"zero","message":"Zero was unable to connect for 10 seconds and was disconnected"}}},"m4":{"client":{"type":"success"},"server":{"type":"success"}}}}]}, +{"name":"harden result: what a mutator returns","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[],"return":null}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[],"return":0}},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[],"return":false}},{"op":"mutate","mutation":"m5","mutator":"harden.ops","args":{"ops":[],"return":""}},{"op":"mutate","mutation":"m6","mutator":"harden.ops","args":{"ops":[],"return":{"1":"one","a":[1,{"b":null}],"":"empty key"}}},{"op":"mutate","mutation":"m7","mutator":"harden.ops","args":{"ops":[],"return":0}},{"op":"mutate","mutation":"m8","mutator":"harden.ops","args":{"ops":[],"return":1e+21}},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":1},"result":{}},{"id":{"clientID":"$clientID","id":2},"result":{"data":null}},{"id":{"clientID":"$clientID","id":3},"result":{"data":0}},{"id":{"clientID":"$clientID","id":4},"result":{"data":false}},{"id":{"clientID":"$clientID","id":5},"result":{"data":""}},{"id":{"clientID":"$clientID","id":6},"result":{"data":{"x":1},"extra":"ignored"}},{"id":{"clientID":"$clientID","id":7},"result":{"data":0}},{"id":{"clientID":"$clientID","id":8},"result":{"data":5e-7}}]}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"return":null}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success","data":null},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"return":0}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success","data":null},"server":"pending"},"m3":{"client":{"type":"success","data":0},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"return":false}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success","data":null},"server":"pending"},"m3":{"client":{"type":"success","data":0},"server":"pending"},"m4":{"client":{"type":"success","data":false},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":5,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"return":""}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success","data":null},"server":"pending"},"m3":{"client":{"type":"success","data":0},"server":"pending"},"m4":{"client":{"type":"success","data":false},"server":"pending"},"m5":{"client":{"type":"success","data":""},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":6,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"return":{"1":"one","a":[1,{"b":null}],"":"empty key"}}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success","data":null},"server":"pending"},"m3":{"client":{"type":"success","data":0},"server":"pending"},"m4":{"client":{"type":"success","data":false},"server":"pending"},"m5":{"client":{"type":"success","data":""},"server":"pending"},"m6":{"client":{"type":"success","data":{"1":"one","a":[1,{"b":null}],"":"empty key"}},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":7,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"return":0}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success","data":null},"server":"pending"},"m3":{"client":{"type":"success","data":0},"server":"pending"},"m4":{"client":{"type":"success","data":false},"server":"pending"},"m5":{"client":{"type":"success","data":""},"server":"pending"},"m6":{"client":{"type":"success","data":{"1":"one","a":[1,{"b":null}],"":"empty key"}},"server":"pending"},"m7":{"client":{"type":"success","data":0},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":8,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[],"return":1e+21}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success","data":null},"server":"pending"},"m3":{"client":{"type":"success","data":0},"server":"pending"},"m4":{"client":{"type":"success","data":false},"server":"pending"},"m5":{"client":{"type":"success","data":""},"server":"pending"},"m6":{"client":{"type":"success","data":{"1":"one","a":[1,{"b":null}],"":"empty key"}},"server":"pending"},"m7":{"client":{"type":"success","data":0},"server":"pending"},"m8":{"client":{"type":"success","data":1e+21},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success","data":null},"server":{"type":"success","data":null}},"m3":{"client":{"type":"success","data":0},"server":{"type":"success","data":0}},"m4":{"client":{"type":"success","data":false},"server":{"type":"success","data":false}},"m5":{"client":{"type":"success","data":""},"server":{"type":"success","data":""}},"m6":{"client":{"type":"success","data":{"1":"one","a":[1,{"b":null}],"":"empty key"}},"server":{"type":"success","data":{"x":1}}},"m7":{"client":{"type":"success","data":0},"server":{"type":"success","data":0}},"m8":{"client":{"type":"success","data":1e+21},"server":{"type":"success","data":5e-7}}}}]}, +{"name":"harden result: mutation args with unusual numbers and keys are pushed as JS writes them","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"2":"two","10":"ten","ops":[],"return":1,"n":[0,1e+21,1e-7,0.1,123456789012345680000,9007199254740994],"b":"b","a":"a","
":"ls","😀":"emoji"}}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"2":"two","10":"ten","ops":[],"return":1,"n":[0,1e+21,1e-7,0.1,123456789012345680000,9007199254740994],"b":"b","a":"a","
":"ls","😀":"emoji"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success","data":1},"server":"pending"}}}]}, +{"name":"harden push: results out of order, twice, and for unknown mutations","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":3},"result":{"data":"three"}},{"id":{"clientID":"$clientID","id":1},"result":{"data":"one"}},{"id":{"clientID":"$clientID","id":1},"result":{"error":"app","message":"second answer for 1"}},{"id":{"clientID":"$clientID","id":99},"result":{"error":"app","message":"unknown"}}]}]},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":2},"result":{"error":"alreadyProcessed"}}]}]},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":2},"result":{"data":"two"}}]}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","lastMutationIDChanges":{"$clientID":3}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"one"}},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":{"type":"success","data":"three"}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"one"}},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":{"type":"success","data":"three"}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"one"}},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"two"}},"m3":{"client":{"type":"success"},"server":{"type":"success","data":"three"}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"one"}},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"two"}},"m3":{"client":{"type":"success"},"server":{"type":"success","data":"three"}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"one"}},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"two"}},"m3":{"client":{"type":"success"},"server":{"type":"success","data":"three"}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"one"}},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"two"}},"m3":{"client":{"type":"success"},"server":{"type":"success","data":"three"}}}}]}, +{"name":"harden push: app errors whose details are falsy","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m5","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":1},"result":{"error":"app","message":"zero","details":0}},{"id":{"clientID":"$clientID","id":2},"result":{"error":"app","message":"empty","details":""}},{"id":{"clientID":"$clientID","id":3},"result":{"error":"app","message":"false","details":false}},{"id":{"clientID":"$clientID","id":4},"result":{"error":"app","message":"null","details":null}},{"id":{"clientID":"$clientID","id":5},"result":{"error":"app","details":{"kept":true}}}]}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":5,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"app","message":"zero"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"app","message":"empty"}}},"m3":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"app","message":"false"}}},"m4":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"app","message":"null"}}},"m5":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"app","message":"Unknown application error: app","details":{"kept":true}}}}}}]}, +{"name":"harden push: stored app errors whose details are falsy","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","lastMutationIDChanges":{"$clientID":2},"mutationsPatch":[{"op":"put","mutation":{"id":{"clientID":"$clientID","id":1},"result":{"error":"app","message":"zero","details":0}}},{"op":"put","mutation":{"id":{"clientID":"$clientID","id":2},"result":{"error":"app","message":"false","details":false}}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["ackMutationResponses",{"clientID":"$clientID","id":2}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"app","message":"zero"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"app","message":"false"}}}}}]}, +{"name":"harden push: a pushResponse for another client is an error","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"other","id":1},"result":{}}]}]},{"op":"advance","ms":5000}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"closed":[0],"state":{"name":"error","reason":"received mutation for the wrong client"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"received mutation for the wrong client"}}}}},{"state":{"name":"error","reason":"received mutation for the wrong client"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"received mutation for the wrong client"}}}}}]}, +{"name":"harden push: an app error pushResponse for another client is an error","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"other","id":1},"result":{"error":"app","message":"x"}}]}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"closed":[0],"state":{"name":"error","reason":"received mutation for the wrong client"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"received mutation for the wrong client"}}}}}]}, +{"name":"harden push: an error kind the client does not know","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":1},"result":{"error":"brandNew","message":"x"}}]}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Unknown fallback error with mutation ID 1: brandNew"}}}}}]}, +{"name":"harden push: a pushResponse on a socket the client closed is ignored","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[]}},{"op":"socketClose","code":1006},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":1},"result":{"data":"late"}}]}]},{"op":"receive","socket":0,"message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":2},"result":{}}]}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000005000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000005000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"late"}},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"late"}},"m2":{"client":{"type":"success"},"server":"pending"}}}]}, +{"name":"harden poke: stored oooMutation and alreadyProcessed results","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","mutationsPatch":[{"op":"put","mutation":{"id":{"clientID":"$clientID","id":1},"result":{"error":"alreadyProcessed"}}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","mutationsPatch":[{"op":"put","mutation":{"id":{"clientID":"$clientID","id":2},"result":{"error":"oooMutation","details":"gap"}}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["ackMutationResponses",{"clientID":"$clientID","id":1}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"closed":[0],"state":{"name":"error","reason":"Server reported an out-of-order mutation"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Server reported an out-of-order mutation"}}},"m2":{"client":{"type":"success"},"server":{"type":"error","error":{"type":"zero","message":"Server reported an out-of-order mutation"}}}}}]}, +{"name":"harden poke: stored results for another client and deletes of unknown ones","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[]}},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","mutationsPatch":[{"op":"put","mutation":{"id":{"clientID":"other","id":1},"result":{"data":"not mine"}}},{"op":"del","id":{"clientID":"$clientID","id":7}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","mutationsPatch":[{"op":"put","mutation":{"id":{"clientID":"$clientID","id":1},"result":{"data":"mine"}}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":1},"result":{"data":"late push"}}]}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["ackMutationResponses",{"clientID":"$clientID","id":1}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"mine"}}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success","data":"mine"}}}}]}, +{"name":"harden poke: a stored result before its lmid and a pushResponse after","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"mutationsPatch":[{"op":"put","mutation":{"id":{"clientID":"$clientID","id":2},"result":{"data":"stored"}}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","lastMutationIDChanges":{"$clientID":2},"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}},{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"receive","message":["pushResponse",{"mutations":[{"id":{"clientID":"$clientID","id":1},"result":{"error":"app","message":"too late"}},{"id":{"clientID":"$clientID","id":2},"result":{"error":"app","message":"too late"}}]}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["ackMutationResponses",{"clientID":"$clientID","id":2}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"stored"}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"stored"}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"stored"}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"stored"}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success","data":"stored"}}}}]}, +{"name":"harden lmid: a lastMutationID past every pending mutation","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","lastMutationIDChanges":{"$clientID":5},"gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}}],"read":["issue"]}},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","lastMutationIDChanges":{"$clientID":6}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"i1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"i2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":2}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":6,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}}],"read":["issue"]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":6,"read":{"issue":[{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":6,"read":{"issue":[{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":6,"read":{"issue":[{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success","data":{"mutationID":6,"read":{"issue":[{"id":"i3","title":"i3","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},"server":{"type":"success"}}}}]}, +{"name":"harden lmid: many pending mutations replay in order across reconnects","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m1","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"mutate","mutation":"m2","mutator":"harden.ops","args":{"ops":[{"op":"update","table":"issue","value":{"id":"a","title":"a2"}}]}},{"op":"mutate","mutation":"m3","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"mutate","mutation":"m4","mutator":"harden.ops","args":{"ops":[{"op":"delete","table":"issue","value":{"id":"a"}}]}},{"op":"mutate","mutation":"m5","mutator":"harden.ops","args":{"ops":[{"op":"upsert","table":"issue","value":{"id":"b","closed":true}}]}},{"op":"socketClose","code":1006},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","lastMutationIDChanges":{"$clientID":2},"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"a","title":"a2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"socketClose","code":1006},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"mutate","mutation":"m6","mutator":"harden.ops","args":{"ops":[{"op":"insert","table":"issue","value":{"id":"c","title":"c","description":"","closed":false,"ownerId":null,"createdAt":1}}]}},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","lastMutationIDChanges":{"$clientID":6},"rowsPatch":[{"op":"del","tableName":"issues","id":{"id":"a"}},{"op":"put","tableName":"issues","value":{"id":"b","title":"B","description":"","closed":true,"owner_id":null,"createdAt":1}},{"op":"put","tableName":"issues","value":{"id":"c","title":"C","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"update","table":"issue","value":{"id":"a","title":"a2"}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"a","title":"a2","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"delete","table":"issue","value":{"id":"a"}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000000000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":5,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"upsert","table":"issue","value":{"id":"b","closed":true}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie","01"],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000005000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":1,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"a","title":"a","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000005000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":2,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"update","table":"issue","value":{"id":"a","title":"a2"}}]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000005000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000005000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"delete","table":"issue","value":{"id":"a"}}]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000005000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":5,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"upsert","table":"issue","value":{"id":"b","closed":true}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie","02"],["lmid","2"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"activeClients":["$clientID"]}]}}],"closed":[1],"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000010000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":3,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"b","title":"b","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000010000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":4,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"delete","table":"issue","value":{"id":"a"}}]}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000010000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000000,"id":5,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"upsert","table":"issue","value":{"id":"b","closed":true}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000010000,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000010000,"id":6,"clientID":"$clientID","name":"harden.ops","args":[{"ops":[{"op":"insert","table":"issue","value":{"id":"c","title":"c","description":"","closed":false,"ownerId":null,"createdAt":1}}]}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1},{"id":"c","title":"c","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"},"m6":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1},{"id":"c","title":"c","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"},"m6":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"b","description":"","closed":true,"ownerId":null,"createdAt":1},{"id":"c","title":"c","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":"pending"},"m4":{"client":{"type":"success"},"server":"pending"},"m5":{"client":{"type":"success"},"server":"pending"},"m6":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"b","title":"B","description":"","closed":true,"ownerId":null,"createdAt":1},{"id":"c","title":"C","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}},"m2":{"client":{"type":"success"},"server":{"type":"success"}},"m3":{"client":{"type":"success"},"server":{"type":"success"}},"m4":{"client":{"type":"success"},"server":{"type":"success"}},"m5":{"client":{"type":"success"},"server":{"type":"success"}},"m6":{"client":{"type":"success"},"server":{"type":"success"}}}}]} +]} diff --git a/packages/zero-conformance/vectors/harden-json.json b/packages/zero-conformance/vectors/harden-json.json new file mode 100644 index 0000000000..b417b19691 --- /dev/null +++ b/packages/zero-conformance/vectors/harden-json.json @@ -0,0 +1,1514 @@ +{"formatVersion":1,"kind":"sweep-json","description":"Random and randomly edited JSON texts. `ok` is whether `JSON.parse` accepts the text; `stringify` is `JSON.stringify` of what it parsed.","cases":[ +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":true,"stringify":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}, +{"input":"{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}","ok":true,"stringify":"{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\t]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":false}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":false}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[00[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":false}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":false}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":true,"stringify":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}, +{"input":"{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}","ok":true,"stringify":"{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":true,"stringify":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\udc00]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":false}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":false}, +{"input":"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]][]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]","ok":false}, +{"input":"[[2.2250738585072011e-308,1,-\\1]]","ok":false}, +{"input":"[]+","ok":false}, +{"input":"\",","ok":false}, +{"input":"[{\"__proto__\":123456789}12345678901234567890,\"4294967295\":1E+2,\"\\u00e9\":\"\\\\udc00\\ud800\"}]","ok":false}, +{"input":"dc{0","ok":false}, +{"input":"\"\\/\"\"__pre-999999999oto__\"","ok":false}, +{"input":"[]","ok":true,"stringify":"[]"}, +{"input":"nul\\x","ok":false}, +{"input":"{\"0\":fale00}","ok":false}, +{"input":"[\u0000[\u0000","ok":false}, +{"input":"[{\"\\u0000\":{\"10\":nul\"10\":[]}}]","ok":false}, +{"input":" ","ok":false}, +{"input":"\"e\\u0301\"","ok":true,"stringify":"\"é\""}, +{"input":"{\"10\":{\"4294967294\":9007199254740993,\"a\":[\"\\udc00\",1e400,\"\\udc00\\ud800\"],\"\\u00e9\":[]}}","ok":true,"stringify":"{\"10\":{\"4294967294\":9007199254740992,\"a\":[\"\\udc00\",null,\"\\udc00\\ud800\"],\"é\":[]}}"}, +{"input":"[[\"\\u0000\",{\"4294967294\":2.2250738585072011e-308,\"a\":-0,\"\\ud800\":\"\\udc00\"},{\"4294967295\":{},\"10\":[9007199254740993,2.2250738585072011e-308,1E+2]}],\"\\ud800\\ud800\"]","ok":true,"stringify":"[[\"\\u0000\",{\"4294967294\":2.225073858507201e-308,\"a\":0,\"\\ud800\":\"\\udc00\"},{\"10\":[9007199254740992,2.225073858507201e-308,100],\"4294967295\":{}}],\"\\ud800\\ud800\"]"}, +{"input":"
}","ok":false}, +{"input":"\n","ok":false}, +{"input":"{}","ok":true,"stringify":"{}"}, +{"input":"{\"\\u00e9\":{\"\\u00e9\":{},\"e\\u0301\":\"e\\u03\",\"0\":[{\t,[\"\\u0000\"],[]]},\"e\\u0301\":{\"e\\u0301\":{\"4294967294\":{},\"\\u0000\":\"\\u00e9\"},\"\\u0000\":{\"constructor\":[],\"-1\":1E+2,\"\"constructor\":0}}}","ok":false}, +{"input":"1.5","ok":true,"stringify":"1.5"}, +{"input":"{\"a\":[]}\":\\uDBFF\\uDFFF[]}","ok":false}, +{"input":"\"a\"\"\n","ok":false}, +{"input":"{\"4294967295\":{\"0\":-0,\"\":[{},{\"a\"\\udc00:\"\\ud800\\ud800\",\"\\u00e9\":1}]},\"10\":{\"constructor\":","ok":false}, +{"input":"tr\\,uee","ok":false}, +{"input":"{\"__proto__\":[],\"01\":{\"4294967295\":[],\"\\ud800\":\"\\udc00\",\"\\u00e9\":[[9007199254740993],\"\\u00e9\",-9223372036854775808]},\"ud800:[]}","ok":false}, +{"input":"1e400","ok":true,"stringify":"null"}, +{"input":"{\"-1\":[{\"e\\u\"e\\u0301😀\":false,\"a\":[\"\",\"
\"]},\"\\u00e9\"],\"constructor\":[1e400,{","ok":false}, +{"input":"\\ud800\"a\":1,\"a\":[+","ok":false}, +{"input":"{\"__ptrueroto__\":{\"1\":1e-400},\"4294967294\":{\"e\\u0301\":21\":22250738585072011e-308}}","ok":false}, +{"input":"\"\f0","ok":false}, +{"input":"\\u00😀\"00😀\"","ok":false}, +{"input":"{\"4294967295\":[1e-400],\"a\":[{\"1\":-9223372036854775808,\"-1\":[123456789012345678901234567890]}],\"a\":[true,\"\\/\",{}]}","ok":true,"stringify":"{\"4294967295\":[0],\"a\":[true,\"/\",{}]}"}, +{"input":"{","ok":false}, +{"input":"{\"e\\u0301\":{\"4294967294\":[],\"01\":{\"4294967294\":{\"\\u00e9\":\"\"","ok":false}, +{"input":"{\"a\":0,\"a\":123456789012345678901234567890,\"1\":{\"0\":{\"a\":\"\\ud800\\ud800\",\"-1\"\"
\"},\"0\":[\"\\/\",[1.5],[\"\\/\",2.2250738585072011e","ok":false}, +{"input":"{\"__to__\":[],\"constructor\"ul}","ok":false}, +{"input":"[[{\"\\u0000\":{\"__proto__\":false},\"__proto__\":{\"__proto__\":1,\"4294967295\":\"e\\u0\u001f301\"},\"e\\u0301\":{}},\"
\",true],{\"e\\u0301\":false,\"4294967295\":[{},-1e400,\"\"]},1e-400]","ok":false}, +{"input":"[[\\u00","ok":false}, +{"input":"\"\\ud800\\ud800\"","ok":true,"stringify":"\"\\ud800\\ud800\""}, +{"input":"{\"\\u00e9\":[]}","ok":true,"stringify":"{\"é\":[]}"}, +{"input":"[e-9999999991E","ok":false}, +{"input":"\"\\udc00\"","ok":true,"stringify":"\"\\udc00\""}, +{"input":"[{\"constructor\":null,\"\\ud80+0\":{\"constructor\":[-1e400,null],\"e\\u0301\":[\"\\ud800\\ud800\"]}},false,0]","ok":false}, +{"input":"{\"\\u0000\":\"😀\",\"__pr__\":[]}","ok":true,"stringify":"{\"\\u0000\":\"😀\",\"__pr__\":[]}"}, +{"input":"[2.2250738585072011e-","ok":false}, +{"input":"{\"e\\u0301\":{}}","ok":true,"stringify":"{\"é\":{}}"}, +{"input":"{\"10\":[\"\\uc00\"],\"a\"\\:[]}","ok":false}, +{"input":"é[]+","ok":false}, +{"input":"[{\"\\u0000\":\"😀\",\"-1\":\"\"\t},\"e\\u0301\",ud800\",1,[-0]]]","ok":false}, +{"input":"{true","ok":false}, +{"input":"\"
\"\t ","ok":true,"stringify":"\"
\""}, +{"input":"{\"\":[1.5,{\"\\u0000\":{\"__proto__\":1E+2}}],\"e\\u0301\":\"\\ud83d\\ude00\"}","ok":true,"stringify":"{\"\":[1.5,{\"\\u0000\":{\"__proto__\":100}}],\"é\":\"😀\"}"}, +{"input":"\"\\ud800\"","ok":true,"stringify":"\"\\ud800\""}, +{"input":"{\":[true],\"__proto__\":\"\\ud00\"}","ok":false}, +{"input":"nultltruel","ok":false}, +{"input":"[9007199254740993]","ok":true,"stringify":"[9007199254740992]"}, +{"input":"0","ok":true,"stringify":"0"}, +{"input":"\\e-999999999","ok":false}, +{"input":"[-1e4Infintruety00,\"\",[]]","ok":false}, +{"input":"{}}{}}","ok":false}, +{"input":"[1.5,\"a\":1,\"a\":\"\\/\"]","ok":false}, +{"input":"[[{\"10\":\"a\",\"constructor\":{\"1\":1e400},\"4294967295\":{\"a\":false,\"constructor\":1.5,\"\\u00e9\":\"\\udc00\"}},1e-400,false],[1E+2,{}]]","ok":true,"stringify":"[[{\"10\":\"a\",\"constructor\":{\"1\":null},\"4294967295\":{\"a\":false,\"constructor\":1.5,\"é\":\"\\udc00\"}},0,false],[100,{}]]"}, +{"input":"{\"4294967295\":{\"\\u00e9\":1e400,\"4294967295\":{\"e\\u0301\":[\"\\/\",\"
\",1E+2],0\":[1,-1e400,1e400]}}}","ok":false}, +{"input":"null\"\\/\"","ok":false}, +{"input":"[\"\\ud800\\ud800\",\"\\u00e9\",[{\"-1\":[2.2250738585072011+-308],\"\":{\"__proto__\":\"a\"}},[[]]]]","ok":false}, +{"input":"{\"0\":[[],[{\"1\":\"e\\u0301\"}]],\"\":1,\"\\u0000\":{\"__proto__\":1.5,\"-1\":0}","ok":false}, +{"input":"]","ok":false}, +{"input":"{\"0\":[{},\"\",2.2250738585072011e-308]}","ok":true,"stringify":"{\"0\":[{},\"\",2.225073858507201e-308]}"}, +{"input":"[2.2250738585072011NaN-308,[[\"\\udc00\\ud800\"]],{}]","ok":false}, +{"input":"{\"10\":1e400,\"10\":{\"constructor\":[{\"__proto__\":2.2250738585072011e-308,\"\\u0000\":1,\"10\":true},1,[1e400,9007199254740993,\"\\u00\"]],\"4294967294\":{\"-1\":[1],\"__proto__\":o__\":[\"\",0,\"\\/\"],\"\\u00e9\":\"\\udc00\"},\"constructor\":\"😀\"},\"4294967294\":[1.5,1e400]}","ok":false}, +{"input":"","ok":false}, +{"input":"-922e3720","ok":true,"stringify":"null"}, +{"input":"{\"0\":[[\"\\ud800\\ud800NaN\",[\"\\ud800\\ud800\",false]],{\"constructor\":{\"-1\":true,\"4294967295\":\"\"}}],\"1\":1E+2,\"01\":\"\"}","ok":true,"stringify":"{\"0\":[[\"\\ud800\\ud800NaN\",[\"\\ud800\\ud800\",false]],{\"constructor\":{\"-1\":true,\"4294967295\":\"\"}}],\"1\":100,\"01\":\"\"}"}, +{"input":"{\"e\\u0Infenity\\uDBFF\\uDFFF01\":\"\\/\"}","ok":false}, +{"input":"{\"constructor\":[{\"10\":-1,\"e\\u0301\":{\"1\":1e400,\"4294967295\":\"a\"}}],\"01\":[0,[],[{\"0\":1e400,\"4294967294967295\":\"\\ud83d\\ude00\",\"e\\u0301\":\"\\udc00\"},[","ok":false}, +{"input":"1","ok":true,"stringify":"1"}, +{"input":"{\"a:\"\\","ok":false}, +{"input":"truue","ok":false}, +{"input":"2.00.2250738585072011e-1e-308","ok":false}, +{"input":"\u0000","ok":false}, +{"input":"\"\\/\"","ok":true,"stringify":"\"/\""}, +{"input":"{\"10\":\"\\ud83d\\ude00\",\"e\\u0301\":\"\\udc0}","ok":false}, +{"input":"-\\x0","ok":false}, +{"input":"-1e400","ok":true,"stringify":"null"}, +{"input":"1e-4000040000","ok":true,"stringify":"0"}, +{"input":"\"
","ok":false}, +{"input":"{\"_\"__proto__\":_proto__\":[[{\"0\":\"\\u00e9\",\"4294967294\":\"a\",\"\\u0000\":\"e\\u0301\"},[\"a\"]]9],\"4294967294\":[[]],\"]],\"\\u0000\":\"\\ud800\"}","ok":false}, +{"input":"{\"\\u0000\":[[\"\\udc00\"]],\"1\":1.5,\"\\u00e9\":-92233720368547758\"__proto__\":08}","ok":false}, +{"input":"\"😀\"\"","ok":false}, +{"input":"
-1e\"4000","ok":false}, +{"input":"9\r0}199254740\f993","ok":false}, +{"input":"{\"1\":[],\"\\u0000\":{\"01\":\"a\",\"constructor\":[null,{}]}}","ok":true,"stringify":"{\"1\":[],\"\\u0000\":{\"01\":\"a\",\"constructor\":[null,{}]}}"}, +{"input":"[{\"4294967294\":0,\"e\\u0301\":123456789012345678901234567890,\"\":{}}]","ok":true,"stringify":"[{\"4294967294\":0,\"é\":1.2345678901234568e+29,\"\":{}}]"}, +{"input":"[{},\u000b{\"4294967294\":{}}]","ok":false}, +{"input":"{\"a\":\"a\",\"\":{\"\\ud800\":-92233720364775808}}","ok":true,"stringify":"{\"a\":\"a\",\"\":{\"\\ud800\":-92233720364775800}}"}, +{"input":"\"e\\u030 1\"","ok":false}, +{"input":"[\"\\0e9\"","ok":false}, +{"input":"\"\\u","ok":false}, +{"input":"[\"\\ud800\\ud800\",\"a\"]","ok":true,"stringify":"[\"\\ud800\\ud800\",\"a\"]"}, +{"input":"[[1ee-400]]","ok":false}, +{"input":"{\"1\":\"\\udc00\"}","ok":true,"stringify":"{\"1\":\"\\udc00\"}"}, +{"input":"[\"\\udc00\\ud800\",{\"e\\u0301\":2.225073855850\\ud80072011e-308},[[1E+2true]]]","ok":false}, +{"input":"{\"-1\":{\"0\":2.2250738585072011e-308,\"\\u0\":[{\"a\":\"\\udc00\"}],\"a\":[]}}","ok":false}, +{"input":"{\"01\":","ok":false}, +{"input":"\"","ok":false}, +{"input":"{\"\\","ok":false}, +{"input":"\"a😀Infinity","ok":false}, +{"input":"[]0","ok":false}, +{"input":"[9007199254740:993,[1234567890123345678901234567890]","ok":false}, +{"input":"{\"constructor\":1e-400,0,\"e\\u0301\":","ok":false}, +{"input":"\"\"\"\"","ok":false}, +{"input":"-00","ok":false}, +{"input":"{\"a\":\"a\",\"01\":\"\\ud800\",:false}","ok":false}, +{"input":"{\"4294","ok":false}, +{"input":"{\"\":-9223372036854775808}","ok":true,"stringify":"{\"\":-9223372036854776000}"}, +{"input":"{\"__pro__proto__\":[\"\\u00e9\"]}","ok":true,"stringify":"{\"__pro__proto__\":[\"é\"]}"}, +{"input":"[\"\\ud\u001f00\",\"😀\",1.5]","ok":false}, +{"input":"\\[]\u000b","ok":false}, +{"input":"[[1e400,[],1E+2]]","ok":true,"stringify":"[[null,[],100]]"}, +{"input":"[2.22502250738585,072011e-308]","ok":false}, +{"input":"e-9999999","ok":false}, +{"input":"\"\\u\\u.c00e9\"","ok":false}, +{"input":"\"\\ud800\\u[NaN800\"","ok":false}, +{"input":"{\"\\u00e9\":{},\"\":{\"0\":\"\\u0000\",\"\":{\"a\":{\"42949]67294\":\"\\u00e9\"},\"4","ok":false}, +{"input":"{\"-1\":[],\"__proto__\":[1e400],\"4294967294\":[[[2.2250738585072011e-308,true],{\"\\ud800\":1e-400,\"\\ud800\":1e-400}]]}","ok":true,"stringify":"{\"4294967294\":[[[2.225073858507201e-308,true],{\"\\ud800\":0}]],\"-1\":[],\"__proto__\":[null]}"}, +{"input":"0e-99999999","ok":true,"stringify":"0"}, +{"input":"1.\r5\\uD.\r5\\uDBFF\\uDFFF","ok":false}, +{"input":"{\"-1\":1.5,\"\\ud800\":{\"a\":[],\"-1\":{\"-1\":[],\"e\\u0301\":[]},\"0\":{\"__proto__\":[1e-400,\"\\ud800\\ud800\",-9223372036854775808],\"\\u00e9\":{\"01\":\"\"}}},\"4294967295\":{}}","ok":true,"stringify":"{\"-1\":1.5,\"\\ud800\":{\"0\":{\"__proto__\":[0,\"\\ud800\\ud800\",-9223372036854776000],\"é\":{\"01\":\"\"}},\"a\":[],\"-1\":{\"-1\":[],\"é\":[]}},\"4294967295\":{}}"}, +{"input":"2.22507225073858507201111e","ok":false}, +{"input":"rue","ok":false}, +{"input":"[[],éE+2,0]","ok":false}, +{"input":"{\"1\":[{\"\\ud800\":{\"\\u00e9\":123456789012345678901234567890,\"\\u0000\":\"\\ud800\",\"10\":-1},\"__proto__\":{\"0\":\"
\",\"\":\"e\\u0301\"}},-00]}","ok":false}, +{"input":"[false]","ok":true,"stringify":"[false]"}, +{"input":"{\"1\":\"\",\"\\u00e9\":[[{},\"
\",\"\\u\"\\udud800\"]],\"4294967295\u001f:{}}","ok":false}, +{"input":"{\"__proto__\":{\"__proto__\":0,\"e\\u0301\":1},\"-1\":[\"\",[\"\\/\",[\"e\\u0301\",1e400],\"\\udc00\"]],\"1\":[{},\"\\/\",2.2250738585072011e-308]}","ok":true,"stringify":"{\"1\":[{},\"/\",2.225073858507201e-308],\"__proto__\":{\"__proto__\":0,\"é\":1},\"-1\":[\"\",[\"/\",[\"é\",null],\"\\udc00\"]]}"}, +{"input":".","ok":false}, +{"input":"\"\\u00e9\"","ok":true,"stringify":"\"é\""}, +{"input":"90071992519925","ok":true,"stringify":"90071992519925"}, +{"input":"[90071992\\ud80]04740993,[[{},\"e\\u0301\",[]],[1E+2]],{\"-1\":[[-1],{\"1\":123456789012345678901234567890,\"4294967295\":-1e400,\"\\u0000\":\"e\\u0301\"}],\"4294967295\":null,\"0\":\"\\u00e9\"}]","ok":false}, +{"input":"{\"4294967294\"729{\":\"\\","ok":false}, +{"input":"{{{","ok":false}, +{"input":"[1e400400,[],}]]","ok":false}, +{"input":"{\"\\ud800\":{},\"4294967294\":[[\"a\",[-1e400]]]}","ok":true,"stringify":"{\"4294967294\":[[\"a\",[null]]],\"\\ud800\":{}}"}, +{"input":"[1e-400,{\"e\\u0301\":{\"4294967294\":1e-400,\"a\":-922337203854775808,\"01\":[\"\",123456789012345678901234567890]},\"4294967294\":[],\"1\":\"\\ud800\\ud800\"},-0]","ok":true,"stringify":"[0,{\"1\":\"\\ud800\\ud800\",\"4294967294\":[],\"é\":{\"4294967294\":0,\"a\":-922337203854775800,\"01\":[\"\",1.2345678901234568e+29]}},0]"}, +{"input":"\"\\ud83d\\ude}00\"","ok":false}, +{"input":"\"\\000e9\"","ok":false}, +{"input":"[\"a\",f,false]","ok":false}, +{"input":"\"\\/\\/\"\"","ok":false}, +{"input":"\"\"","ok":true,"stringify":"\"\""}, +{"input":"\"
\"\"a\":1,\"a\"\\ud800","ok":false}, +{"input":"[{\"\":1.5,\"]__proto__\":[\"\",{\"01\":1}]},{}]","ok":true,"stringify":"[{\"\":1.5,\"]__proto__\":[\"\",{\"01\":1}]},{}]"}, +{"input":"[-1,[[\"\\ud800\\ud00\",\"\"],{\"a\":\"e\\u0301\",\"\\u0000\":{\"\\ud800\":\"\\ud800\"}},[]]","ok":false}, +{"input":"\"0.","ok":false}, +{"input":"{\"e\\u0301\":[1E+2,{+2,{\"\\u00e9\":[true,1E+2,-0]}]}","ok":false}, +{"input":"{\"10\":Infinity10\":","ok":false}, +{"input":"{\"__proto__proto__\":\"\\udc0\u000b\"}","ok":false}, +{"input":"123456789012345678901234567890","ok":true,"stringify":"1.2345678901234568e+29"}, +{"input":"\"a\"\u0000\\","ok":false}, +{"input":"{\"__proto__\":[\"\\/\",[[\"e\\u0301\",\"\\u0000\",true],[1,\"
\",1e-400]],[{\"a\":123456789012345678901234567890,\"\\u00e9\":-1},[false,\"e\\u0301\",-1e400],{\"10\":\"\\udc00\\ud800\",\"10\":\"\",\"10\":1}]]}","ok":true,"stringify":"{\"__proto__\":[\"/\",[[\"é\",\"\\u0000\",true],[1,\"
\",0]],[{\"a\":1.2345678901234568e+29,\"é\":-1},[false,\"é\",null],{\"10\":1}]]}"}, +{"input":"true","ok":true,"stringify":"true"}, +{"input":"{\"__proto__\":{\"constructor\":\"\\u00e9\"},\"\\ud8001e400}","ok":false}, +{"input":"\"\"","ok":true,"stringify":"\"\""}, +{"input":"nullull","ok":false}, +{"input":"900NaN71992547409974099","ok":false}, +{"input":"[{},\"\\u00e9\",{\"1\":{\"-1\":{\":9007199254740993,\"__proto__\":-1},\"4294967967294\":{\"e\\u0301\":\"\"}},\"__proto__\":[]}]","ok":false}, +{"input":"f","ok":false}, +{"input":"{\"01\":-1400}00}","ok":false}, +{"input":"[[{}","ok":false}, +{"input":"[[\u0000],[]]","ok":false}, +{"input":"[{\"1\":\"\\ud800\\ud80\n0\",\"a\":[[1e400],[]]},[\"\\ud83d\\ude00\",{\"e\\u03","ok":false}, +{"input":"{\"e\\u0301\":{\"1\":\"\\ud800\"},\"__proto__\":0}","ok":true,"stringify":"{\"é\":{\"1\":\"\\ud800\"},\"__proto__\":0}"}, +{"input":"{\"\"\"{\"\"\":1\u001fE+2}","ok":false}, +{"input":"[{\"\\u00:[\"\\/\"E,","ok":false}, +{"input":"{\\ dc00dc00","ok":false}, +{"input":"{\"__proto__\":{\"e\\u0301\":[],\"a\":{\"__proto__\":{\"__proto__\":-0},\"10\":\"\",\"10\":\"\\ud800\\ud800\"},\"constructor\":[]},\"10\":[{\"1\":{\"4294967294\":0},\"1\":0,\"constructor\":\"\\u0000\"},{\"0\":1e-400}],\"1\":{\"-1\":{\":{\"\\u0000\":{},\"\\u0000\":{\"\\u00e9\":\"\\ud800\"},\"constructor\":9007199254740993},\"e\\u0301\":[1e-400,[\"\\/\",1,\"
\"],\"\\ud800\\ud800\"],\"\\u00e9\":{\"\\ud800\":-0NaN}}}","ok":false}, +{"input":"{\"__proto__\":{\"\\u0000\":[],\"1\u000b\":[],\"\\ud800\":[1e400,\"\\ud83d\\ude00\",\"\"]}\u0000,\"\\u0000\":[[],\"\\ud83d\\ude00\",123456789012345678901234567890]}","ok":false}, +{"input":"\"a\"","ok":true,"stringify":"\"a\""}, +{"input":"e40","ok":false}, +{"input":"{\"\\u0000\":[\"\\udc00\"],\"1\":1E+2,\"01\":{\"4294967295\":[\"\\u00e9\",{\"4294967294\":\"\\u00e9\",\"\":\"\"},[\"\\ud800\\ud800\"]],\"-1\":{\"constructor\":[\"\\ud83d\\ude00\",1]},\"\\u0000\":{}}}","ok":true,"stringify":"{\"1\":100,\"\\u0000\":[\"\\udc00\"],\"01\":{\"4294967295\":[\"é\",{\"4294967294\":\"é\",\"\":\"\"},[\"\\ud800\\ud800\"]],\"-1\":{\"constructor\":[\"😀\",1]},\"\\u0000\":{}}}"}, +{"input":"{\"1\":2.2250738585072011e-308,\"-1\":[],\"1\":[[\"\\u00e9\",[\"\\ud800\",\"
\"]],1E+2]}","ok":true,"stringify":"{\"1\":[[\"é\",[\"\\ud800\",\"
\"]],100],\"-1\":[]}"}, +{"input":"{\"10\":{\"\":{},\"\\ud800\":{}},\"-1\":null}","ok":true,"stringify":"{\"10\":{\"\":{},\"\\ud800\":{}},\"-1\":null}"}, +{"input":"[[1,[,[{\"constructor\":\"a\",\"0\":\"\\ud83d\\ude00\",\"\\u00e9\":\"
\"}],1E+2],\"😀\",[0,\"\\udc00\\ud800\"]]","ok":false}, +{"input":"[[[[0,\"
\"],{}]],\"\\u","ok":false}, +{"input":"[\"\\u0000\"😀,\"\\u00e9\",{\"constructor\":[\"\\/\",{\"4294967294\":2.2250738585072011e-308},\"\\/\"],\"-1\":[]}]","ok":false}, +{"input":"[]\"e_prot","ok":false}, +{"input":"[0,{\"\":[{\"01\":-1e400},{\"__proto__\":1e-400,\"1\":\"\\ud83d\\ude00\",\"4294967294\":1},1},\"\\ud800\"],\"\\u0000\":[{\"a\":\"e\\u0301\",\"\"","ok":false}, +{"input":"\"e\\u0301\"é","ok":false}, +{"input":"[12345678890123456789012}4567890,\"a\"]","ok":false}, +{"input":"1234567890123456789012345670\t90","ok":false}, +{"input":"n😀ull","ok":false}, +{"input":"1237012345678901678901234567890","ok":true,"stringify":"1.2370123456789016e+30"}, +{"input":"-0400","ok":false}, +{"input":"[\"\",{\"consnultructor\":[0,\"\\/\"","ok":false}, +{"input":"\"\\ud\\x83d\\ude00\"","ok":false}, +{"input":"{\"\":\"","ok":false}, +{"input":"nullllullllll","ok":false}, +{"input":"\"\\u0😀00e-999999999\"","ok":false}, +{"input":"1234567890123458901234567890","ok":true,"stringify":"1.2345678901234589e+27"}, +{"input":"{\"\\u00e9\"true[{\"\":{\"-1\":null},\"01\":\"\\ud}800\\ud800\",\"-1\":{\"\\u0000\":null}},[{\"a\":false,\"0\":1}]],\"01\":[],\"01\":[true,1.5]}","ok":false}, +{"input":"[[[{\"1\":\"\\u0000\"}],{\"-1\":{\"\\u00e9\":false},\"\\ud800\":false,\"__proto__\":[1e-400,1e-400,1e-400]}]]","ok":true,"stringify":"[[[{\"1\":\"\\u0000\"}],{\"-1\":{\"é\":false},\"\\ud800\":false,\"__proto__\":[0,0,0]}]]"}, +{"input":"null1","ok":false}, +{"input":"[\"\\ud800\\ud800\",12345678901234561234567890123 4567890,[[[],1,\"e\\u0301\"],[[1]]]]","ok":false}, +{"input":"-23372036854775808","ok":true,"stringify":"-23372036854775810"}, +{"input":"{\"01\":1e400,\"a\":{\"-1\":[],\"\":{\"\\u00e9\":123456789012345678901234567890},\"constructo\"\\ud83d\\ude00\"}}","ok":false}, +{"input":"{\"01\":\"\\u0000\",\"\\ud8","ok":false}, +{"input":"{\"__proto__\":{\"e\\u0301\":\"\\ud800\\ud800\"},800\"},\"10\":\"😀\",\"10\":\"\\u00e9\"}","ok":false}, +{"input":"[{\"-1\":{\"\":{\"\\u0000\":2.2250738585072011e-308},\"-1\":-1e400,\"\\u0000\":1},\"__proto__\":[-1e400,[2.2250738585072011e-308,12345E789012345678901234567890]]},-1,{\"0\":{},\"constructor\":1e400,\"a\":1234567890123456789012347890}]","ok":true,"stringify":"[{\"-1\":{\"\":{\"\\u0000\":2.225073858507201e-308},\"-1\":null,\"\\u0000\":1},\"__proto__\":[null,[2.225073858507201e-308,null]]},-1,{\"0\":{},\"constructor\":null,\"a\":1.2345678901234569e+27}]"}, +{"input":"1e-","ok":false}, +{"input":"[\"\\ud0\\ud80","ok":false}, +{"input":"-92true","ok":false}, +{"input":"\"\\udc00\\ud80","ok":false}, +{"input":"{\"\":{\"\\u00e9\":{\"\\ud800\":[],\"a\":-922337 036\"a\":1,\"a\":854775808,\"a\":{}\"a\":{}},\"1\":[[]]},\"\\ud800\":\"😀\"}","ok":false}, +{"input":"[[\"\\ud800\",\"\",[\"
\",[],[\"\\ud83d\\ude00\",\"\\/\",-9223372036854775808]]]]","ok":true,"stringify":"[[\"\\ud800\",\"\",[\"
\",[],[\"😀\",\"/\",-9223372036854776000]]]]"}, +{"input":"{\"10\"\\uDBFF\\uDFFF[\"\\ud800\",","ok":false}, +{"input":"{}}","ok":false}, +{"input":"[{\"\\ud800\":[false],\"\\u0000\":{\"\\ud800\":\"\\ud83d\\ude00\",\"10\":123456789012345678901234567890,\"__proto__\":[\"\",null,123456789012345678901234567890]},\"\":[{\"42949672955\":1e400,\"e\\u0301\":null,\"-1\":-19},{},{+]},1E+2,null]","ok":false}, +{"input":"[null__proto__\":0},1e-","ok":false}, +{"input":"{\"","ok":false}, +{"input":"[12356789012","ok":false}, +{"input":"[{.a\":null}]","ok":false}, +{"input":"[[{},-9223372036854775808,{\"-1\":[true,123456789012345678901234567890,\"\\/\"],\"\\ud800\":[-1],\"4294967295\":-1{}],null]NaN","ok":false}, +{"input":"1e-400","ok":true,"stringify":"0"}, +{"input":"[1e400,[\"\\udc00\\ud800\",[9007199254740993],[-9223372036854775808]],\"\\ud83d\\ude000]","ok":false}, +{"input":"{\"\\u0000\":{\"1\":{\"\\u00e9\":[\"\"],\"-1","ok":false}, +{"input":"\"\\ud8\"\\ud 8\"","ok":false}, +{"input":"falsls","ok":false}, +{"input":"[","ok":false}, +{"input":"90071992547409254740993","ok":true,"stringify":"9.007199254740925e+22"}, +{"input":"N\u001fN\u001faN1e-400","ok":false}, +{"input":"E\"\\uNaN0\"]","ok":false}, +{"input":"{\"\\u00e9\":\"\\\\udc00ud800\",\"4294967295\":\"\",\"4294967295\":{\"__pro\\xo__\":\"\\udc00\\ud800\"\\x}","ok":false}, +{"input":"10}","ok":false}, +{"input":"\"\\ud800","ok":false}, +{"input":"[[d800\\ud800\\u00,\"😀\"]]","ok":false}, +{"input":"[[],{\"429496729295\":\"\\\"\\/\"},[]]","ok":true,"stringify":"[[],{\"429496729295\":\"\\\"/\"},[]]"}, +{"input":"{}]","ok":false}, +{"input":"{\"\":[],,\"4294967295\":{},\"\":[\"\\/\",[[\"e\\u0301\"],{}]]}","ok":false}, +{"input":"\"udc00\"","ok":true,"stringify":"\"udc00\""}, +{"input":"\"\\0e9\"","ok":false}, +{"input":"[[{\"__proto__\":\"e","ok":false}, +{"input":"{\"\\u00e9\":\"\\ud83d\\ude00\",\"constructor\":\"\\ud800\",\"\\ud800\":{\"4294967295\":{\"e\\u0301\":[\"\\udc00\",\"e\\u0301\",\"\\ud800\"]}}}","ok":true,"stringify":"{\"é\":\"😀\",\"constructor\":\"\\ud800\",\"\\ud800\":{\"4294967295\":{\"é\":[\"\\udc00\",\"é\",\"\\ud800\"]}}}"}, +{"input":"\"e\\u0301","ok":false}, +{"input":"\\uDBFF\\uDFFFfalstrue","ok":false}, +{"input":"{\"a\":1e400}","ok":true,"stringify":"{\"a\":null}"}, +{"input":"\\\"\\/\"","ok":false}, +{"input":"[{\"\":[[\"\\/\"]],\"01\":[[\"\",0,\"\\u0000\"]]},{\"-1\":{\"10\":[\"\\ud800\\ud800\",\"\\ud800\\ud800\"],\"-1\":{\"01\":nul01\":null,\"0\":1E+2,\"-1\":0}}}]","ok":false}, +{"input":"[[[\"\\/\"],[{\"\\u0000\":[123456789012345678901234567890,true],\"1{\":[-92233720\\ud80036854775808,\"
\",-1e400],\"4294967295\":[0]}]]","ok":false}, +{"input":"{\"-1\":{},\"\\u00e9\":[{\"\\ud800\":{\"10\":\"\\u0000\",\"1\":\"\\u0000\",\"a\":-0}}],\"4294967294\":{\"e\\u0301\":[\"\"]}}","ok":true,"stringify":"{\"4294967294\":{\"é\":[\"\"]},\"-1\":{},\"é\":[{\"\\ud800\":{\"1\":\"\\u0000\",\"10\":\"\\u0000\",\"a\":0}}]}"}, +{"input":"1e","ok":false}, +{"input":"-0-0","ok":false}, +{"input":"{\"\":[\"\\ud83d\\ude00\",\"a\"],\"\":\"\\/\",\"a\":90071:9007199254793}","ok":false}, +{"input":"\"😀\"","ok":true,"stringify":"\"😀\""}, +{"input":"[2.2250738585072011e-308]","ok":true,"stringify":"[2.225073858507201e-308]"}, +{"input":"{\"-1\":[\"
\",{},[]],\"\\u0000\":{\"10\":[\"
\",false]},\"1\":[-9223372036854775808]}","ok":true,"stringify":"{\"1\":[-9223372036854776000],\"-1\":[\"
\",{},[]],\"\\u0000\":{\"10\":[\"
\",false]}}"}, +{"input":"I,finity\"\\ud800\"","ok":false}, +{"input":"[[],[],\"e\\u0301\"]","ok":true,"stringify":"[[],[],\"é\"]"}, +{"input":"{\"-1\":\"
\",\"01\":[[true,[\"😀\",1e-400]]]}","ok":true,"stringify":"{\"-1\":\"
\",\"01\":[[true,[\"😀\",0]]]}"}, +{"input":"1.","ok":false}, +{"input":"\u001fnull","ok":false}, +{"input":"-9223,37685477580[","ok":false}, +{"input":"[[],{}]NaN","ok":false}, +{"input":"-92233720368547758088","ok":true,"stringify":"-92233720368547760000"}, +{"input":"[\"e\\u0301\",[]]-\"a\":
1,\"a\":]","ok":false}, +{"input":"\"\"a\":1,\"a\":","ok":false}, +{"input":" t+rue","ok":false}, +{"input":"[\"\\ud800\"]","ok":true,"stringify":"[\"\\ud800\"]"}, +{"input":"{\"4294967294\":{\"constructor\":[falsee,true,1e400],\"10\":{}},\"\":\"😀\",\"\\u00e9\":{\"4","ok":false}, +{"input":"{\"constructor\":[{},{}],\\uDBFF\\uDFFF4294967294\":9007199254740993,\"aNaN\":{\"constructor\":[],\"0\":{\"4294967294\":1.5,\"\\u00e9\":123456789012345678901234567890,\"\":{}}}}","ok":false}, +{"input":"\"\\udc00\\e-999999999ud800\"","ok":false}, +{"input":"{\"_{\"__prnu","ok":false}, +{"input":"\"\\/\\u\"..","ok":false}, +{"input":"\"\\udc000\"","ok":true,"stringify":"\"\\udc000\""}, +{"input":"{\"01\":[],\"\":\"\"}}","ok":false}, +{"input":"{\"4294967294\":{\"\\u0000\":\"
\",\"10\":nullnull,\"\\u00e9\":{\"0\":{\"4294967294\":[],\"\\u0000\":1e400,\"a\"\\uDBFF\\uDFFF9007199254740993},\"1\":[]},\"\":{}}","ok":false}, +{"input":"[[[1,\"\\ud800\\ud800\"]],[],{\"10\":[],\"01\":\\u0000\":\"\"},{\"0\":\"\",\"__proto__\":\"\\u00e9\"},9007199254740993],\"constructor\":{\"\\u00e9\":{\"-1\":\"\\ud800\",\"1\":123456789012345678901234567890},\"-1\":2.2250738585072011e-308,\"\\ud800\":[true,2.2250738585072011e-","ok":false}, +{"input":"false","ok":true,"stringify":"false"}, +{"input":"NaN]","ok":false}, +{"input":"[{\"\":\"\\udc00\\ud800\",\"10\":{\"\\ud800\":[]}}]","ok":true,"stringify":"[{\"10\":{\"\\ud800\":[]},\"\":\"\\udc00\\ud800\"}]"}, +{"input":"{\"E294967295\":123456789012345678901234567890,\"constructor\":{\"\\ud80","ok":false}, +{"input":"123456789012","ok":true,"stringify":"123456789012"}, +{"input":"\"\\ud800\\ud8800\\ud80\",","ok":false}, +{"input":",-1","ok":false}, +{"input":"[{\"\":{\"\\u00e9\":\"\\u00e9\"},\"01\":\"\\ud800\\ud800\"{,\"4294967295\":{\"0\":[1e400,\"\",\"a\"],\"1\":1E+2,\"0\":{\"4294967294\":\"\\/\"}}},-0]","ok":false}, +{"input":"nu
","ok":false}, +{"input":"[-0]","ok":true,"stringify":"[0]"}, +{"input":"{\"a\":{},\"__proto__\":[{\"10\":301\"},\"e\\u0301\"]}","ok":false}, +{"input":"[[1e-400,{\"1\":{\"\\u00e9\":1,\"4294967294\":null,\"__proto__\":\"\\udc00\"},\"10\":[9007199254740993],\"e\\u0301\":[\"\\udc00\",\"\\ud83d\\ude00\"]}],[null]]","ok":true,"stringify":"[[0,{\"1\":{\"4294967294\":null,\"é\":1,\"__proto__\":\"\\udc00\"},\"10\":[9007199254740992],\"é\":[\"\\udc00\",\"😀\"]}],[null]]"}, +{"input":"[{\"4294967295\":\"\"},[[\"\\/\"],1E+2,[]]]","ok":true,"stringify":"[{\"4294967295\":\"\"},[[\"/\"],100,[]]]"}, +{"input":"[\"\\/\",{\"4294967295\":[400,2.2250738585072011e-308],{\"10\":\"
\"00},[0,\"\\/\"]],\"\\ud800\":900719925474099003}]","ok":false}, +{"input":"\"\\ud800\"d800\"","ok":false}, +{"input":"{\"\\u0000\":{},\"a\":{\"__proto__\":{\"\":\"a\",\"0\":[\"e\\u0301\"]},\"4294967295\":[{\"4294967295\":123456789012345678901234567890,\"4294967295\"udc00\\ud800\"},[\"
\",\"\\ud83d\\ude00\",9007199254740993]]}}","ok":false}, +{"input":"{\"\\u0000\":[[{\"\\ud800\":-1,\"0\":\"\\ud800\\ud800\",\"1\":true},[]],0,{\"e\\u0301\":2.2250738585072011e-308}],\"01\":[{}],\"4294967295\":[{\"\\ud800\":[-1e400,\"\\/\"],\"a\":{}}]}","ok":true,"stringify":"{\"\\u0000\":[[{\"0\":\"\\ud800\\ud800\",\"1\":true,\"\\ud800\":-1},[]],0,{\"é\":2.225073858507201e-308}],\"01\":[{}],\"4294967295\":[{\"\\ud800\":[null,\"/\"],\"a\":{}}]}"}, +{"input":"{\"10\":[\"a\"],\"01\":[[\"a\",[\"\\udc00\\ud800\"],{}],[1.5,[-1e400,\"\"]],[[1,9007199254740993,1.5]]],\"\\ud800\"9[{\"__proto__\":{}}]}","ok":false}, +{"input":"[[\"\\u00e9\",\"e\\u0301\"],[1e400,\"\\u00e9\",true]]","ok":true,"stringify":"[[\"é\",\"é\"],[null,\"é\",true]]"}, +{"input":"{910\":null}","ok":false}, +{"input":"\"[","ok":false}, +{"input":"[{\"10\":1,\"00","ok":false}, +{"input":"\"\\ud8e999999999","ok":false}, +{"input":"{\"\\u00e99\":{\"-1\":{\"constructor\":[\"\\ud800\",\"
\"],\"-1\":[\"
\",\"e\\u0301\",-1],\"a\":[]},\"01\":{}}}","ok":true,"stringify":"{\"é9\":{\"-1\":{\"constructor\":[\"\\ud800\",\"
\"],\"-1\":[\"
\",\"é\",-1],\"a\":[]},\"01\":{}}}"}, +{"input":"[[\"\\udc00\",{\"__proto__\":\"\\udc00\",\"10\":[]}],{\"\"","ok":false}, +{"input":"{\"\\ud800\":[],\"4294967295\":{\"0\":{\"_roto__\":[\"\\udc00\\ud800\"","ok":false}, +{"input":"{\"10\":[],\"\":{\"1\":[[\"\\u[\"\\ud800\\ud800\",\"\\/\"],{\"1\":\"a\",\"\":true},1]},\"\\ud800\":-\"4294967295\":-1,\"4294967294\":[{},9007199254740993]}}","ok":false}, +{"input":"\"\\u00e9\"00","ok":false}, +{"input":"-923372036854708","ok":true,"stringify":"-923372036854708"}, +{"input":"9007199254740993","ok":true,"stringify":"9007199254740992"}, +{"input":"\"\\u0000\"","ok":true,"stringify":"\"\\u0000\""}, +{"input":"\ralse","ok":false}, +{"input":"0NaN","ok":false}, +{"input":"00","ok":false}, +{"input":"\"\\udc00\\\\ud800\"","ok":true,"stringify":"\"\\udc00\\\\ud800\""}, +{"input":"é","ok":false}, +{"input":"[{\"\\ud800\":2.22507385
5072011e-308,\"4967295\":{}},-1e400]","ok":false}, +{"input":"{Infinity\"01\":[]}","ok":false}, +{"input":"e\\x0","ok":false}, +{"input":"😀[]","ok":false}, +{"input":"[Infi]ity]","ok":false}, +{"input":"-","ok":false}, +{"input":".5","ok":false}, +{"input":"{\"0\":123456789012345678901234567890,\"0\":[[[-0,-1e400],{\"0\":\"\\ud83d\\ude00\",\"1\":\"e\\u0301\",\"-1\":1E+2},1]],\"\\u0000\":[1.5,[[\"\\u0000\"],\"\\/\",[\"\\ud83d\\ude00\",\"\\ud800\\ud800\"]e],{\"-1\":\"
\",\"10\":[\"a\",true,-1],\"01\":{\"e\\u0301\":2.2250738585072011e-308}308}}]}","ok":false}, +{"input":"{\"01\":[{\"01\":{\"4294967295\":123456789012345678901234567890,\"4294967294\":\"","ok":false}, +{"input":"[\\u","ok":false}, +{"input":"{\"4294967294\":{},\"1\":[\"😀\",{},false],\"constructor\":[\"\\/\"]}","ok":true,"stringify":"{\"1\":[\"😀\",{},false],\"4294967294\":{},\"constructor\":[\"/\"]}"}, +{"input":"0
","ok":false}, +{"input":"\"😀","ok":false}, +{"input":"{00a\":{}0}","ok":false}, +{"input":"{\"42949672:94\":{\"0\":0,\"-1\":\"a\",\"__proto__\":{e},\"1\":[],\"\\u00e9\":1.5}","ok":false}, +{"input":"\"\n","ok":false}, +{"input":"[\"\\/\",","ok":false}, +{"input":"\\udc00\"\r","ok":false}, +{"input":"{\"10\":{},\"4294967294\":123456789012345678901234567890}","ok":true,"stringify":"{\"10\":{},\"4294967294\":1.2345678901234568e+29}"}, +{"input":"{\"1\":{\"10\":{\"constructor\":-1e400,\"e\\u0301\":{\"4294967295\":1E+2},\"1\":[\"\\u0000\",\"a\",1E+2]},\"10\":[[\"\\ud800\\ud800\"],{\"\\ud8\":9007199254740993,\"10\":1E+2,\"0\":\"\\u00e9\"}]}}","ok":false}, +{"input":"[{\"constructor\":{},\"1\":\"\\udc00\\ud800\"},1.5]","ok":true,"stringify":"[{\"1\":\"\\udc00\\ud800\",\"constructor\":{}},1.5]"}, +{"input":"{\"__proto\u000b__\"to__\":[]}","ok":false}, +{"input":"{\"a\":{\"01\":\"😀\",\"\\ud800\":[[\"\\/\",\"\"],123456789012345678901234567890[[]],\"4294967294\":[[-0,-9223372036854775808],\"\\/\"]},\"a\":{\"\":{},\"4294967294\":[[\"\\ud800\\ud800\",\"\\0e9\",\"\\ud800\"]]},\"__proto__\":[]}","ok":false}, +{"input":"\"\\x+/\"","ok":false}, +{"input":"{\"constructor\":{\"\\u0000\":[\"\\udc00\\ud800\",{},[\"\\u00","ok":false}, +{"input":"{\"10\":[\"\\ud0\"__","ok":false}, +{"input":"{\"e\\u0301\":\"\\u0000\",\"4294967295\":\"\\ud83d\\ude00\",\"4294967294\":{\"constructor\":\"\\ud800\\ud800\",\"a\":1e400}}","ok":true,"stringify":"{\"4294967294\":{\"constructor\":\"\\ud800\\ud800\",\"a\":null},\"é\":\"\\u0000\",\"4294967295\":\"😀\"}"}, +{"input":"-0","ok":true,"stringify":"0"}, +{"input":"\"\"\u000b","ok":false}, +{"input":"{\"construr","ok":false}, +{"input":"[]Infinity[]","ok":false}, +{"input":"\"\\ud800\\ud8800\"00\"","ok":false}, +{"input":"{\"1\":{\"10\":[{\"e\\u0301\":-0},\"\\/\",[]]},\"constructor\":[\"\\/\"],\"4294967295\":1e-400}","ok":true,"stringify":"{\"1\":{\"10\":[{\"é\":0},\"/\",[]]},\"constructor\":[\"/\"],\"4294967295\":0}"}, +{"input":"\"\\ud","ok":false}, +{"input":"{\"0\":{\"4294967295\":\"a\",\"0\":{\"10\":{\"\":1,\"__proto__\":1e400},\"4294967295\":\"\"},\"a\":\"
\"},\"-1\":{\"-1\":123456789012345678901234567890,\"0\":\"\"},\"0\":[]}","ok":true,"stringify":"{\"0\":[],\"-1\":{\"0\":\"\",\"-1\":1.2345678901234568e+29}}"}, +{"input":"-922323372036854775808","ok":true,"stringify":"-922323372036854800000"}, +{"input":"[{\"4294967294\"","ok":false}, +{"input":"\"\\\u000bdc00\"","ok":false}, +{"input":"\"\u000b\\ud800","ok":false}, +{"input":"e9999999999990","ok":false}, +{"input":"\"\\\fd80\"","ok":false}, +{"input":"E\"nul a\"","ok":false}, +{"input":"[[]\"a\":1,\"a\":,{\"__pronulto__\":false,\"\":true},{\"__proto__\":[[1e400],{\"__proto__\":\"\",\"10\":1,\"\\u0000\":123456789012345678901234567890},\"\\ud83d\\ude00\"],\"constructor\":1E+2}]","ok":false}, +{"input":"[1e400,{\"1\":{\"\\ud800\":\"\\udc00\",\"01\":[true],\"constructor\":1e400},\"\\ud800\":-0,\"e\\u0301\":\"\\ud800\\ud800\"},{\"\\u00e9\":{\"a\":{\"e\\u0301\":-0,\"e\\u0301\":\"😀\"},\"01\":\"😀\"},\"-1\":\"\",\"4294967294\":{\"__proto__\":[\"e\\u0301\",\"e\\u0301\",1]}}]","ok":true,"stringify":"[null,{\"1\":{\"\\ud800\":\"\\udc00\",\"01\":[true],\"constructor\":null},\"\\ud800\":0,\"é\":\"\\ud800\\ud800\"},{\"4294967294\":{\"__proto__\":[\"é\",\"é\",1]},\"é\":{\"a\":{\"é\":\"😀\"},\"01\":\"😀\"},\"-1\":\"\"}]"}, +{"input":"[1e400,-0]","ok":true,"stringify":"[null,0]"}, +{"input":"1.5.","ok":false}, +{"input":"12345678901234567\u001f901234567890","ok":false}, +{"input":"[[{\"\\ud800\":123456789012345678901234567890,\"0\":\"\\ud800\\ud800\",\"e\\u0301\":1e400},\"\"],\"a\"]","ok":true,"stringify":"[[{\"0\":\"\\ud800\\ud800\",\"\\ud800\":1.2345678901234568e+29,\"é\":null},\"\"],\"a\"]"}, +{"input":"{}{}","ok":false}, +{"input":"1234569012345678901234567890","ok":true,"stringify":"1.2345690123456789e+27"}, +{"input":"{NaNN","ok":false}, +{"input":"{\"\":[[],9007199254740993],\"1\":2.225}07385850725072011e-308}","ok":false}, +{"input":"{\"e\\u0301\":\"\\ud80","ok":false}, +{"input":"{\"0\"\\\"\\u00e9\"}","ok":false}, +{"input":"[0,{\"01\":\"\\udc00\\ud800\",\"a\":[[-0,-1e400],{\"e\\u0301\":\"e\\u0301\",\"\\u0000\":\"\\ud800\",\"0\":1E+2},{}],\"\\u0000","ok":false}, +{"input":"{\"\\ud800\":{\"constructor\":{\"4294967294967295\":9007199254740e93,\"\\u0000\":\"\\/\"}}}","ok":true,"stringify":"{\"\\ud800\":{\"constructor\":{\"4294967294967295\":9.00719925474e+105,\"\\u0000\":\"/\"}}}"}, +{"input":"{\"\\u0000\":[{\"\\u0000\":{\"1\":\"\\u00e9\",\"__proto__\"roto_oto__\":\"a\"},\"-1\":null}]}","ok":false}, +{"input":"trNaNe{}","ok":false}, +{"input":"{\"0\":12345678901234567890","ok":false}, +{"input":"{\"4294967295\":\"
:\"
\"}","ok":false}, +{"input":"[{\"__proto__\":{\"01\":\"
\"}},\"\\ud800\"]","ok":true,"stringify":"[{\"__proto__\":{\"01\":\"
\"}},\"\\ud800\"]"}, +{"input":"{\"\\u0000\":{\"\\u0000\":\"\\ud800\",\"4294967294\":\"\\ud800\\ud800\"},\"10\":\"\\u0000\",\"\":\"\\ud83d\\ude00\"}","ok":true,"stringify":"{\"10\":\"\\u0000\",\"\\u0000\":{\"4294967294\":\"\\ud800\\ud800\",\"\\u0000\":\"\\ud800\"},\"\":\"😀\"}"}, +{"input":"falsee","ok":false}, +{"input":"{\"10\":[[{\"\\u00etrue\":\"😀\",\"10\":","ok":false}, +{"input":"\\","ok":false}, +{"input":"trtru+e","ok":false}, +{"input":"9007Infinity992547409933","ok":false}, +{"input":"\"\nud800\\ud800\"","ok":false}, +{"input":"{\"10\":{\"__proto__\":[],\"01\":\"\\ud83d\\ude00\",\"\\u0000\":{\"4294967294\":[-1e400],\"429496767294\":[\"\\ud800\\ud800\",\"\\/\",-9223372036854775808],\"e\\u0301\":[\"e\\u0301\",\"a\",\"
\"]}},\"4294967294\":\"\"}","ok":true,"stringify":"{\"10\":{\"__proto__\":[],\"01\":\"😀\",\"\\u0000\":{\"4294967294\":[null],\"429496767294\":[\"\\ud800\\ud800\",\"/\",-9223372036854776000],\"é\":[\"é\",\"a\",\"
\"]}},\"4294967294\":\"\"}"}, +{"input":"1E","ok":false}, +{"input":"1-00","ok":false}, +{"input":"{\"10\":{\"\\u00e9\":[],\"4294967295\":[-922337203685e4775808],\"4294967294\":[{\"0\":\"0\":1e-400,\"4294967295\":0},[2.2250738585072011e-308],2.2250738585072011e-308]}}","ok":false}, +{"input":"[9007199\u001f54740993]","ok":false}, +{"input":"[{\"42949}67295\":[]],\"0\"\"0\":\"😀\",\"\":\"
\"},1]","ok":false}, +{"input":"{\"a\":\"\\u\\u00800\\ud800\",\"\\u000\":1E+2,\"4294967299\":\"\"}","ok":false}, +{"input":"[\"\"]","ok":true,"stringify":"[\"\"]"}, +{"input":"{\"\\ud800\":[[]],\"01\":[{\"4294967294\":\"\"},[]]}","ok":true,"stringify":"{\"\\ud800\":[[]],\"01\":[{\"4294967294\":\"\"},[]]}"}, +{"input":"\"\\udNaN800\"","ok":false}, +{"input":"{\"0\":[[],{}],\"-1\":{},\"\\u0000\":1e-\"__proto__\":400}","ok":false}, +{"input":"{\"4294967295\":{\"__proto__\":{},\"1\":[\"a\"],\"1\":{}},\"a\":{\"\\u0000\":\"\\ud800\\ud800\",\"__proto__\":\"\\u0000\"}}","ok":true,"stringify":"{\"4294967295\":{\"1\":{},\"__proto__\":{}},\"a\":{\"\\u0000\":\"\\ud800\\ud800\",\"__proto__\":\"\\u0000\"}}"}, +{"input":"[[9007199254740993],{\"4294967294\":[{\"4294967294\":\"\",\"\\ud800\":-1}]},[{},\"\\/\",[{}]]]","ok":true,"stringify":"[[9007199254740992],{\"4294967294\":[{\"4294967294\":\"\",\"\\ud800\":-1}]},[{},\"/\",[{}]]]"}, +{"input":"1E+2","ok":true,"stringify":"100"}, +{"input":"e400","ok":false}, +{"input":"1-400","ok":false}, +{"input":"[12345678012345678934567890,false]","ok":true,"stringify":"[1.234567801234568e+25,false]"}, +{"input":"[\"__proto__\":","ok":false}, +{"input":"\"\\ud80e\"","ok":true,"stringify":"\"\\ud80e\""}, +{"input":"{\"e\\u0301\":{\"\\ud800\":\"\",\"\\u0000\":true},\"\":{\"__proto__\":[],\"\\ud800\":\"😀\",\"e\\u0301\":{\"1\":123456789012345678901234567890}},\"__proto__\":-1}","ok":true,"stringify":"{\"é\":{\"\\ud800\":\"\",\"\\u0000\":true},\"\":{\"__proto__\":[],\"\\ud800\":\"😀\",\"é\":{\"1\":1.2345678901234568e+29}},\"__proto__\":-1}"}, +{"input":"-1","ok":true,"stringify":"-1"}, +{"input":"{\"\\\u0000u0000000\":[[[\"\",null,\"a\"]]]}","ok":false}, +{"input":"[-1e400]","ok":true,"stringify":"[null]"}, +{"input":"[[],{\"-1\":{\"4294967294\":\"e\\u0301\",\"\\ud\":{\"constructor\":-1e400,\"constructor\":\"\\u0000\",\"01\":-1},\"constructor\":\"\\ud800\"},\"__proto__\":{\"a\":\"e\\u0301\",\"constructor\":{\"42947295\":-0,\"\\u00e9\":\"a\"}}}]","ok":false}, +{"input":"{\"\f0\":[{}]}","ok":false}, +{"input":"{\"cons","ok":false}, +{"input":"{\"\\u00e9\":\"\",\"0\":-92233720368547758Infinity8,\"-1\":[-9223372036854775808]}","ok":false}, +{"input":"{\"-1\":123456789012345678901234567890,\"\\ud80","ok":false}, +{"input":"1\\DFF\\uDFFF","ok":false}, +{"input":"{\"\\u00e9\":[],\"4294967\r94\":\"\\u0000\"\\u0000\"}","ok":false}, +{"input":"e999999999\"d83d\\ude00 ","ok":false}, +{"input":"[\r]","ok":true,"stringify":"[]"}, +{"input":"[[[{\"__proto__\":123456789012345678901234567890,\"\\u0000\":1E+2,2,\"\\ud800\":1","ok":false}, +{"input":"{\\x","ok":false}, +{"input":"[{\"\\u0000\":\"\\ud800\"},{},{\"constructor\":\"\",\"10\":[[-1],{}]}]","ok":true,"stringify":"[{\"\\u0000\":\"\\ud800\"},{},{\"10\":[[-1],{}],\"constructor\":\"\"}]"}, +{"input":"-[],{}]","ok":false}, +{"input":"\"\\ud80 0","ok":false}, +{"input":"{\"e\\u03\":{}}","ok":false}, +{"input":"[{\"-1\":{\"\\u00e9\":[],\"0\":\"\\\\xd800\",\"4294967294\":0\":1e400,\"01\":\"\\u0000\",\"\\ud800\":2.2250738585072011e-308}}},\"\\ud83d\\ude00\"]","ok":false}, +{"input":"{\"-1\":1.5,\"\":\"\"}","ok":true,"stringify":"{\"-1\":1.5,\"\":\"\"}"}, +{"input":"1null","ok":false}, +{"input":"[-9223372036-54775808,2.2250738585072011e-308,{}]","ok":false}, +{"input":"1e-400-4-4000","ok":false}, +{"input":"[{}]","ok":true,"stringify":"[{}]"}, +{"input":"{\"4294967295\":[[[\"\\ud83d\\ude00\"],{}],1e400,\"\"]}","ok":true,"stringify":"{\"4294967295\":[[[\"😀\"],{}],null,\"\"]}"}, +{"input":"t\"__proto__\":r\\xue","ok":false}, +{"input":"[true,[[,\"\\udc00\\ud800\"]]","ok":false}, +{"input":"[\u000b","ok":false}, +{"input":"\"é","ok":false}, +{"input":"100","ok":true,"stringify":"100"}, +{"input":"\"
\"\"[","ok":false}, +{"input":"{\"4294967295\":[","ok":false}, +{"input":"{\"__proto__\":-9223372036854775808}","ok":true,"stringify":"{\"__proto__\":-9223372036854776000}"}, +{"input":"\"\\","ok":false}, +{"input":"{\"constructor\":[\"\"]}","ok":true,"stringify":"{\"constructor\":[\"\"]}"}, +{"input":"{\"1\":[[[\"
\",1E+2,-1e400],[]]]}","ok":true,"stringify":"{\"1\":[[[\"
\",100,null],[]]]}"}, +{"input":"{\"__proto__\"\\ud800[],\"4294967294\":\"\\u00e9\"}","ok":false}, +{"input":"{}}{nul}","ok":false}, +{"input":"[[[[]],{\"0\":true},\u001f\"4294967295\":{\"10\":-1},\"4294967295\":null,\"1\":[\"\\/\"]}],{\"constructor\":{},\"01\":[],\"1\":{\"4294967294\":{\"10\":\"\"},\"01\":{\"e\\u0301\":1e400,","ok":false}, +{"input":"{\"a\":","ok":false}, +{"input":"{\"\\u0000\":123456789012345678901234567890,\"-1\":-1,\"\\u00e9\":\"\"","ok":false}, +{"input":"0e","ok":false}, +{"input":"{\"10\":[\"\\ud800\"],\"10\":{},\"1\":[\"\\/\"","ok":false}, +{"input":"2.22507385850720nul1e-30883088","ok":false}, +{"input":"e-999999999-1NaN","ok":false}, +{"input":"[{\"4294967294\":[{\"\\ud800\":1e400,\"a\":123456789012345678901234567890}],\"4294967295\":1234567890123456789012345678567890}]","ok":true,"stringify":"[{\"4294967294\":[{\"\\ud800\":null,\"a\":1.2345678901234568e+29}],\"4294967295\":1.2345678901234568e+33}]"}, +{"input":"1E+\\u 002","ok":false}, +{"input":"[[],[\"\\:00e9],-1e400]","ok":false}, +{"input":"[\"\n\"e-99","ok":false}, +{"input":"[-9223372036854775808,{\"4294967294\":{},\"constructor\":\"e\\u0301\"}]","ok":true,"stringify":"[-9223372036854776000,{\"4294967294\":{},\"constructor\":\"é\"}]"}, +{"input":"{\"\":\"\\ud800\",\"__proto__\":\"\\udc00\\ud800\"}","ok":true,"stringify":"{\"\":\"\\ud800\",\"__proto__\":\"\\udc00\\ud800\"}"}, +{"input":"{ ]","ok":false}, +{"input":"}","ok":false}, +{"input":"{\"constructor\":{\"a\":[1e-400,[1.5,\"\\u0000\"]],\"\":123456789012345678901234567890,\"0\":[\"e\\u0301\"]},\"\\u0000\":\"\"}","ok":true,"stringify":"{\"constructor\":{\"0\":[\"é\"],\"a\":[0,[1.5,\"\\u0000\"]],\"\":1.2345678901234568e+29},\"\\u0000\":\"\"}"}, +{"input":"2.2250738NaN585072011e-308","ok":false}, +{"input":"[\"\\/\",1,null]","ok":true,"stringify":"[\"/\",1,null]"}, +{"input":"{\"\":900719925474099]3}","ok":false}, +{"input":"\"é\"\f","ok":false}, +{"input":"2.2250738585072011e-308","ok":true,"stringify":"2.225073858507201e-308"}, +{"input":"[1e400,\"😀\"]","ok":true,"stringify":"[null,\"😀\"]"}, +{"input":"\"\\\\udc000E\"","ok":true,"stringify":"\"\\\\udc000E\""}, +{"input":"-9223372036854775808","ok":true,"stringify":"-9223372036854776000"}, +{"input":"{\"\":{\"4294967295\":[],\"\":{\"4294967294\":{\"-1\":\"\\ud83d\\ude00\"},\"e\\u0301\":\"\\ud800800\",\"42949294967294\":{\"0\":\"\"}}},\"\":false}","ok":true,"stringify":"{\"\":false}"}, +{"input":"0E","ok":false}, +{"input":"{\"\":[],\"constructor\":{\"\\ud800\":[],\"4294967294\":\"\\udc00\\ud800\",\"10\":[{\"1\":-0,\"4294967294\":-9223372036854775808},{},[]]},\"-1\":1E+2}","ok":true,"stringify":"{\"\":[],\"constructor\":{\"10\":[{\"1\":0,\"4294967294\":-9223372036854776000},{},[]],\"4294967294\":\"\\udc00\\ud800\",\"\\ud800\":[]},\"-1\":100}"}, +{"input":"{\"\":9007199254740993,\"\\ud8
0\":-9223372036854775808}","ok":false}, +{"input":"\"\\u0","ok":false}, +{"input":"{\"__proto__\":\"\\udc00\\ud800\",\"-1\":{},\"0\":{},\"0\":[-9223372036854775808]}","ok":true,"stringify":"{\"0\":[-9223372036854776000],\"__proto__\":\"\\udc00\\ud800\",\"-1\":{}}"}, +{"input":"90071[99254740","ok":false}, +{"input":"1.51.","ok":false}, +{"input":"{\"\\u00e9\":-9223372036854775808,\"a\":{\"\":123456789012345678901234567890},\"4294967294\":{\"01\":{}}}","ok":true,"stringify":"{\"4294967294\":{\"01\":{}},\"é\":-9223372036854776000,\"a\":{\"\":1.2345678901234568e+29}}"}, +{"input":"{\"\\udc00","ok":false}, +{"input":",\"\\ud800\\","ok":false}, +{"input":"Infinity{}","ok":false}, +{"input":"[]null","ok":false}, +{"input":"[{\"a\":{\"__proto__\":[]}},{\"01\":\"\\ud800\"}]","ok":true,"stringify":"[{\"a\":{\"__proto__\":[]}},{\"01\":\"\\ud800\"}]"}, +{"input":"\"\\u000\"","ok":false}, +{"input":"{\"01\":{\"\\u00e9\":{},\"10\":{\"10\":-1,\"constructor\":null},\"e\\u0301\":{}}}","ok":true,"stringify":"{\"01\":{\"10\":{\"10\":-1,\"constructor\":null},\"é\":{},\"é\":{}}}"}, +{"input":"{\"0\":{\"a\":\"\\u0000\",\"__proto__\":2.2250738585072011e-308,\"01\":[2.2250738585072011e-308]},\"a\":\"\\u00e9\"}","ok":true,"stringify":"{\"0\":{\"a\":\"\\u0000\",\"__proto__\":2.225073858507201e-308,\"01\":[2.225073858507201e-308]},\"a\":\"é\"}"}, +{"input":"07199254740993","ok":false}, +{"input":"{\"\\u0\":[{\"10\":\"\\/\"}],\"__proto__\":\"\"}","ok":false}, +{"input":"140E","ok":false}, +{"input":"{\"4294967295\":[{},1e400,[\"😀\"]],\"4294967294\":{\"\\u00e9\":{\"1\":1E+2,\"__proto__\":[\"
\",-0,\"\\u00e9\"]}},\"1\":{\"e\\u0301\":[\"😀\",[\"\\u0000\",\"😀\",[\"😀\",-1]],\"10\":[]}}","ok":false}, +{"input":"{\"429 496729-\":-0} ","ok":true,"stringify":"{\"429 496729-\":0}"}, +{"input":"{\f\"\\u00e9\":{\"-1\":1E+2,\"\\u00e9 ","ok":false}, +{"input":"1e00","ok":true,"stringify":"1"}, +{"input":"[{\"\\ud800\":[[null,\"\\/\"]],\"4294967294\":[{\"0\":1e-400},{\"4294967294\":\"\\ud800\\ud800\",\"10\":false,\"4294967294\":-922337203685477580","ok":false}, +{"input":"{{}","ok":false}, +{"input":"{\"4294967294\":1.5,\"\\u00e9\":[[1E+2,{\"4294967294\":false,\"-1\":2.2250}
38585072011e-308}],{}]}}]}","ok":false}, +{"input":"\"\\u\u001f800","ok":false}, +{"input":"[[{\"4294967295\":{\"a\":\"\\udc00\\ud800\",\"__proto__\":\"a\",\"4294967294\":1.}]]","ok":false}, +{"input":"\"\\ud80e9999999990","ok":false}, +{"input":"{\"constructor\":{\"1\":[[null,-9223372036854775808,\"\\udc00\"],1,[\"\\u0000\",\"
\",0]],\"1\":{}},\"constructor\":[]}","ok":true,"stringify":"{\"constructor\":[]}"}, +{"input":"1E+","ok":false}, +{"input":"-1\\ud800400400","ok":false}, +{"input":"[{\"construconstructor\":null,\"\\ud800\":\"\\u0000\",\"4294967295\":{\"\":\"😀\",\"01\":[\"e\\u0301\",\"\\ud800\",2.225073858507072011e-308],\"\\u00e9\":[1e400,-9223372036854775808,\"\"]}}]","ok":true,"stringify":"[{\"construconstructor\":null,\"\\ud800\":\"\\u0000\",\"4294967295\":{\"\":\"😀\",\"01\":[\"é\",\"\\ud800\",2.225073858507072e-308],\"é\":[null,-9223372036854776000,\"\"]}}]"}, +{"input":"{\"4294967294\":1E+","ok":false}, +{"input":"\"\\u00\\u00e9","ok":false}, +{"input":"\"😀\"\\u\\uDBFF\\uDFFF","ok":false}, +{"input":"[:::","ok":false}, +{"input":"{\"10\":\"a\"}","ok":true,"stringify":"{\"10\":\"a\"}"}, +{"input":"{\"\\u0000\":12345678901234567890\"1234567890,\"429496😀7295\"{}}","ok":false}, +{"input":"{\"\\u0000\":{\"e\\u0301\":[-1,\"\",1E+2],\"e\\u0301\":{},\"10\":{\"constructor\":[\"\",-1,null],\"1\":{\"4294967295\":0,\"e\\u0301\":\"😀\",\"\":\"a\"}}},\"-1\":{\"4294967294\":1.5,\"e\\u0301\":[{},\"😀\",\"\\u0000\"]}}","ok":true,"stringify":"{\"\\u0000\":{\"10\":{\"1\":{\"4294967295\":0,\"é\":\"😀\",\"\":\"a\"},\"constructor\":[\"\",-1,null]},\"é\":{}},\"-1\":{\"4294967294\":1.5,\"é\":[{},\"😀\",\"\\u0000\"]}}"}, +{"input":"}}","ok":false}, +{"input":"[{ ","ok":false}, +{"input":"{\"1\":true}","ok":true,"stringify":"{\"1\":true}"}, +{"input":"[{\"\\ud80{\"\\ud800\":[]}]","ok":false}, +{"input":"1e-40","ok":true,"stringify":"1e-40"}, +{"input":"{\"\\u0000\":[1E+2,{\"10\":[],\"10\":-1e400}],\"\\u00e9\":-1}","ok":true,"stringify":"{\"\\u0000\":[100,{\"10\":null}],\"é\":-1}"}, +{"input":"{\"0\":{\"10\":{\"__proto__\":{\"\":\"
\",\"a\":1},\"\\u0000\":9007199254740993,\"\":\"\\/\"},\"\\ud800\":{\"\\u0000\":1e-400,\"\":\"e\\u0301\",\"01\":{\"\":\"
\",\"4294967295\":1e400,\"1\":\"\\ud83d\\ude00\"}},\"1\":{\"__proto__\":[\"a\",\"😀\",\"\\\"],\"\\u00e9\":\"\\ud800\",\"e\\u0301\":{\"0\":\"\\ud83d\\ude00\",\"__proto__\":\"😀\"}}},\"1\":[{\"\\u00e9\":\"\\/\",\"4294967294\":\"\\ud83d\\ude00\"},{\"01\":{\"\":-0,\"0\":-1,\"0\":2.225\n738585072011e-308},\"\":-1,\"\\u00e9\":[\"\",1e-400]},[\"\\udc00\"]],\"\\u0000\":[]}","ok":false}, +{"input":"[],{\"42949nul67294\":{\"-1\":true,\"1\":[1.5]}},[\"\\udc00","ok":false}, +{"input":"{\"cén","ok":false}, +{"input":"fa:lses\"a\":1,\"a\":","ok":false}, +{"input":"{\"01\":[\"\\udc00\\uud800\"],\"\\u00e9\":-1e400,\"e\\u0301\u001f:[{\"a\":\"\\ud83d\\ude00\"},\"e\\u0301\",[[9007199254740993],[1e-400]]]}","ok":false}, +{"input":"{\"\\u00e9e-999999999:-922337","ok":false}, +{"input":"faléInfinitye","ok":false}, +{"input":"[{\"\\u0000\":{\"e\\u0301\":[-1]},\"0\":\"\\udc00\\ud800\",\"__proto__\":\"\\ud800\\ud800\"},[1E+2,\"\\ud800\\ud800\"],false]","ok":true,"stringify":"[{\"0\":\"\\udc00\\ud800\",\"\\u0000\":{\"é\":[-1]},\"__proto__\":\"\\ud800\\ud800\"},[100,\"\\ud800\\ud800\"],false]"}, +{"input":"[123","ok":false}, +{"input":"\"\\ ud800\\ud800nul\"","ok":false}, +{"input":"[[0[[0\u000b","ok":false}, +{"input":"\"\\\\u000","ok":false}, +{"input":"\"\\udc00\\ud800\"","ok":true,"stringify":"\"\\udc00\\ud800\""}, +{"input":"\"\\/\"\\/\u0000\"","ok":false}, +{"input":"{\"0NaN1\":[0,{}]}","ok":true,"stringify":"{\"0NaN1\":[0,{}]}"}, +{"input":"[{}][{","ok":false}, +{"input":"[\"e\\uu0301\",1.5,\"\\u00e0e9\"]","ok":false}, +{"input":"e","ok":false}, +{"input":"{\"e\\u0301\":{\"\\ud800\":{}},\"\\u0000\":[{\"\\u00e9\":\"\",\"01\":.a\",\"constructor\":{\"1\":\"\\/\",\"constructor\":\"\\u0000\"}},[9007199254700993,{\"a\":-0},[]]],\"4294967294\":\"\\udc00\\ud800\"}","ok":false}, +{"input":"{\"a\":[","ok":false}, +{"input":"😀","ok":false}, +{"input":"\"e\\\\udc000301\"","ok":true,"stringify":"\"e\\\\udc000301\""}, +{"input":"{\"\":{\"\":[{\"1\":\"\\udc00\\ud800\",\"-1\":\"\\udc00\"},{\"-1\":\"\\udc00\",\"01\":9007199254740993,\"e\\u0301\":123456789012345678901234567890},{}]},\"-1\":1e400}","ok":true,"stringify":"{\"\":{\"\":[{\"1\":\"\\udc00\\ud800\",\"-1\":\"\\udc00\"},{\"-1\":\"\\udc00\",\"01\":9007199254740992,\"é\":1.2345678901234568e+29},{}]},\"-1\":null}"}, +{"input":"[1.5,[{},1e400],[[\"\\u 83d\\ude00\"],2.2250738585072011e-308","ok":false}, +{"input":"{\"\\u0000\":{\"0\":{\"0\":1E+2,\"\\ud800\":[\"😀\",2.2250738585072011e-308,\"a\"],\"10\":{}},\"4294967294\":{\"\\u0000\":\"e\\u0301\",\"\":[9007199254740993,\"\\udc00\",\"\\/\"],\"constructor\":-1e400},\"1\":{}}}","ok":true,"stringify":"{\"\\u0000\":{\"0\":{\"0\":100,\"10\":{},\"\\ud800\":[\"😀\",2.225073858507201e-308,\"a\"]},\"1\":{},\"4294967294\":{\"\\u0000\":\"é\",\"\":[9007199254740992,\"\\udc00\",\"/\"],\"constructor\":null}}}"}, +{"input":"\"\"","ok":false}, +{"input":"\"\\u\\u0e","ok":false}, +{"input":"{\"0\":\"\"\\\"\\u00,\"10\":\"\\udc00\"+}","ok":false}, +{"input":"{\"\\u00e9\":{\"constructor\":\"😀\"},\"10\":\"\\udc00\"}","ok":true,"stringify":"{\"10\":\"\\udc00\",\"é\":{\"constructor\":\"😀\"}}"}, +{"input":"trueE2","ok":false}, +{"input":"[[\u0000[[true,123456:789012345678901234567890,\"\\/\"]],[{\"constructor\":1e400,\"\\u0000\":\"\\udc","ok":false}, +{"input":"[[null],[\"\\ud800\\ud800,[{\"\\u00e9\":2.2250738585072011e-308,\"4}94967294\":9007199254740993}],\"\\,\"\\u00e9\"]]","ok":false}, +{"input":"\"a\"\"a\"","ok":false}, +{"input":"[\\udc0","ok":false}, +{"input":"e-999999999-1","ok":false}, +{"input":"\"\u001f\"\"\u000b","ok":false}, +{"input":"{\"4294😀967294\":[[1","ok":false}, +{"input":"[{}","ok":false}, +{"input":"{}true","ok":false}, +{"input":"[[],\"\"],\"\"]","ok":false}, +{"input":"[\"e\\u0301\"NaN","ok":false}, +{"input":"{\"0\":[1e-400,-0,[2.2250738585072011e-308,-1]],\"01\":\"\"}","ok":true,"stringify":"{\"0\":[0,0,[2.225073858507201e-308,-1]],\"01\":\"\"}"}, +{"input":"[{}e}]","ok":false}, +{"input":"-1e4","ok":true,"stringify":"-10000"}, +{"input":"\"Infinityudc0000","ok":false}, +{"input":"\r[true],[],1..5]","ok":false}, +{"input":"[{\"a\":\"\\u","ok":false}, +{"input":"90071992E47","ok":true,"stringify":"9.0071992e+54"}, +{"input":"\\x[]","ok":false}, +{"input":"{\"\\u00e9\":{\"\":0,\"\":{\"a\":2.2250738585072011e-308,\"4294967295\":2.22507385850585072011-308}}}","ok":false}, +{"input":"{ 4294","ok":false}, +{"input":"{\"10\"\":\"a\"}","ok":false}, +{"input":"1\u0000e","ok":false}, +{"input":"\"4294967294\":\"\"}","ok":false}, +{"input":"{\"-1\":1e--","ok":false}, +{"input":"\"\\ud800\\udInity\"","ok":false}, +{"input":"{\"4294967294\":[]}00","ok":false}, +{"input":"{\"e\\u030\"e\\u0301\":1e-400,\"1\":[{\"\\u0000\":\"\\/\",\"01\":{\"e\\u03","ok":false}, +{"input":"\"\\ud83d\\ude00\"]","ok":false}, +{"input":"\"__proto__\":{}","ok":false}, +{"input":"\t.9.9","ok":false}, +{"input":"00{","ok":false}, +{"input":"{\"4294967294\":[1e-400,\"😀\",{\"0\":[-1e400,-1],\"01\":0,\"constructor\":9007199254740993}]}","ok":true,"stringify":"{\"4294967294\":[0,\"😀\",{\"0\":[null,-1],\"01\":0,\"constructor\":9007199254740992}]}"}, +{"input":"\"\\u000\n\"","ok":false}, +{"input":"1e4","ok":true,"stringify":"10000"}, +{"input":"[[","ok":false}, +{"input":"[]\\ud8d800","ok":false}, +{"input":"\"[","ok":false}, +{"input":"{\"0\":\"\\ud83d\\ude00\",\"0\":[{},[{\"\":\"\\/\",\"4294967295\":\"\\udd800\",\"__proto__\":\"\\u00e9\"},1e-400],[true,\"\\/\",{\"a\":-0,\"01\":\"\\udc00\"}]]}","ok":true,"stringify":"{\"0\":[{},[{\"\":\"/\",\"4294967295\":\"\\udd800\",\"__proto__\":\"é\"},0],[true,\"/\",{\"a\":0,\"01\":\"\\udc00\"}]]}"}, +{"input":"{\u0000null}\\ud800","ok":false}, +{"input":"[ e-40","ok":false}, +{"input":"[\"\\unu\u0000l800\\ud800\"},true]","ok":false}, +{"input":"{\"4294967295\":[false],\"-1\":{\"1\":[{\"4294967294\":2.2250738585072011e-308,\"constructor\":\"\\udc00\",\"44967\f94\":-1e400},{},\"\"],\"-1\":{\"\\ud800\":0}}}","ok":false}, +{"input":"{\"0\":[[[],\"\\udc00\"]],\"10\":{\"0\":null,\"01\":1,\"\":[0,123456789012345678901234567890]}}","ok":true,"stringify":"{\"0\":[[[],\"\\udc00\"]],\"10\":{\"0\":null,\"01\":1,\"\":[0,1.2345678901234568e+29]}}"}, +{"input":"1e0","ok":true,"stringify":"1"}, +{"input":"{\"10\":true\"-1\":{\"10\":9007199254740993,\"-1\":-0},\"\\u0000\":[[]],\"\\u0000\":[{\"constructor\":\"a\",\"\":\"
\",\"\\ud800\":\"\\/\"},{\"-1\":1E+2,\"-1\":\"a\",\"0\":\"\"},1e400]},\"a\":-0}","ok":false}, +{"input":"[2.2250738585072011e-308,{}]","ok":true,"stringify":"[2.225073858507201e-308,{}]"}, +{"input":"\"
\"","ok":true,"stringify":"\"
\""}, +{"input":"[1e-400,{}]","ok":true,"stringify":"[0,{}]"}, +{"input":"900799254740993","ok":true,"stringify":"900799254740993"}, +{"input":"{\"__proto__\":}","ok":false}, +{"input":"{\"\\ud800\":{\"01\":\"\\ud800\\ud800}}","ok":false}, +{"input":"{\"e\\u0301\":{\"__proto__\":\"\\u00e9\"},\"\\u0000\":[-9223372036854775808,1\u000bE+2,0],\"\":\"\\u00e9\"}","ok":false}, +{"input":"{\"\\u00e9\":[[{\"01\":-9223372036854775808,\"a\":1,\"\":-1}],0,{\"0\":{},\"4294967295\":\"
\"}],\"1\":1.5}","ok":true,"stringify":"{\"1\":1.5,\"é\":[[{\"01\":-9223372036854776000,\"a\":1,\"\":-1}],0,{\"0\":{},\"4294967295\":\"
\"}]}"}, +{"input":"{\"__proto__\":-1,\"ud800\":{},\"0\":[-0,\"\\ud800\",{\"a\":\"\\/\",\"__proto__\":\"\",\"constructor\":{\"a\":null}}]}","ok":true,"stringify":"{\"0\":[0,\"\\ud800\",{\"a\":\"/\",\"__proto__\":\"\",\"constructor\":{\"a\":null}}],\"__proto__\":-1,\"ud800\":{}}"}, +{"input":"trurue","ok":false}, +{"input":"\"\\ud0\\ud800\"","ok":false}, +{"input":"{\"\":[1,-1]}","ok":true,"stringify":"{\"\":[1,-1]}"}, +{"input":"\"2.2250738585072011e-308,\"\\/\",{}]","ok":false}, +{"input":"{\"10\":\"\\ud800\\ud800\",\"\\u0000\":[-1,{\"1\":[1e400],\"\\u00e9\":[true,1e400,\"\\u0301\"]}]}","ok":true,"stringify":"{\"10\":\"\\ud800\\ud800\",\"\\u0000\":[-1,{\"1\":[null],\"é\":[true,null,\"́\"]}]}"}, +{"input":"-0\r\\","ok":false}, +{"input":"[{},12345678901345678901234567890]","ok":true,"stringify":"[{},1.2345678901345678e+28]"}, +{"input":"-0\\x-0","ok":false}, +{"input":"-1[","ok":false}, +{"input":"\"e-99999999E\\ud-800\"","ok":false}, +{"input":"{\"-1\":-9223372036854775808,\"4294967294\":\"😀\"}","ok":true,"stringify":"{\"4294967294\":\"😀\",\"-1\":-9223372036854776000}"}, +{"input":"900719In\\udc00inity254740993","ok":false}, +{"input":"[][{","ok":false}, +{"input":"{\"\\ud800\":[1.5,[\"\\udc00\",{\"4294967295\":\"\",\"0\":\"\"},{\"0\":2.2250738585072011e-308,\"0\":\"\\ud800\\ud800\"}],[[1.5]]],\"\\u0000\":{\"1\":{\"4294967295\":\"\",\"__proto__\":{\"-1\":-1},\"1\":\"\\udc00\"},\"4294967295\":[{},\"\\udc00\",-1],\"4294967295\":{\"4294967294\":123456789012345678901234567890}}}","ok":true,"stringify":"{\"\\ud800\":[1.5,[\"\\udc00\",{\"0\":\"\",\"4294967295\":\"\"},{\"0\":\"\\ud800\\ud800\"}],[[1.5]]],\"\\u0000\":{\"1\":{\"1\":\"\\udc00\",\"4294967295\":\"\",\"__proto__\":{\"-1\":-1}},\"4294967295\":{\"4294967294\":1.2345678901234568e+29}}}"}, +{"input":"{\"\\u0","ok":false}, +{"input":"[-+,\\u00e9\"]","ok":false}, +{"input":"[[\"\",{\"__proto__\":[9007199254740993]},-1e400],{\"\":{\"01\":\"\\u0\",\"\":{\"4294294\":true,\"10\":-1e400}}}]","ok":false}, +{"input":"1800-400","ok":false}, +{"input":"[{\"__proto__\":true😀,\"é","ok":false}, +{"input":"{}nul","ok":false}, +{"input":"{\"__proto__\":false}","ok":true,"stringify":"{\"__proto__\":false}"}, +{"input":"\"a\"\"a\"\"a\":","ok":false}, +{"input":"[[e-999999999]","ok":false}, +{"input":"[{}[{é}]}[{}]","ok":false}, +{"input":"[{\"4294967294\":{\"-1\":0,\"__proto__\":[1E+2],\"__proto__\":0}},[{\"constructor\":null,\"__proto__\":{},\"0\":[\"\\u0000\",9007199254740993]},\"
\",[\"\",[\"\\ud83d\\ude00\",\\u007199254740993],[\"\",\"\\/\"]]],[-9223372036854775808]]","ok":false}, +{"input":"{\"1\"1\":,null}","ok":false}, +{"input":"\"\\u00e9\n","ok":false}, +{"input":"[{\"-1\":[-\"constructor\":0}]},[]]","ok":false}, +{"input":"{\"01\":{\"10\":2.2250738585072011[-308","ok":false}, +{"input":"\"\\ud800\\d8900\"","ok":false}, +{"input":"1.5.5","ok":false}, +{"input":"{\"constructor\":\"
\"}","ok":true,"stringify":"{\"constructor\":\"
\"}"}, +{"input":"[\"\\0000\"]","ok":false}, +{"input":"{\r\"4294967295\":\"
\"}","ok":true,"stringify":"{\"4294967295\":\"
\"}"}, +{"input":"{\"\\u0000\":2.225073858507205072011e-308,\"\":{},\"0\":[[[0123456789012345678901234567890]]]}","ok":false}, +{"input":"{\"__proto__\":[[true,1e-400,0],[[1.5,1.,9007199254740993],\"😀\",1],1E+2]}","ok":false}, +{"input":"90071992e5474099300","ok":true,"stringify":"null"}, +{"input":"[{\"4294967295\":0,\"4294967295\":[[\"\\udc00\",\"\\udc00\"],[2.2250738585072011e-308,\"\\ud800\\ud800\"]]},{\"01\":{\"0\":\"\\ud8\\udc0000\"},\"a\":\"\\udc00\",\"-1\":{\"01\":null,\"10\":\"\\udc00\\00\"}},[123456789012345678901234567890,1,\"\"]]","ok":false}, +{"input":"udc00\\ud800\"","ok":false}, +{"input":"{\"4294\u000b967294\":-1e400,\"\\u0[0e9\":{}}","ok":false}, +{"input":"\"\\u\u000b[d83d\\de00\"","ok":false}, +{"input":"falt","ok":false}, +{"input":"{\\u00e9\":\"\",\"1\":-","ok":false}, +{"input":"[-1,1\f]","ok":false}, +{"input":"[\"\\ud83d\\ude00\"]","ok":true,"stringify":"[\"😀\"]"}, +{"input":"n","ok":false}, +{"input":"\"e999999999ud800\\ud800\"","ok":true,"stringify":"\"e999999999ud800\\ud800\""}, +{"input":"{\"\\ud800\":{\"\\u00e9\":{\"\\u00e9\":[],\"constructor\":9007199254740993,\"\\u0000\":{\"1\":2.2250738585072011e-308,\"E4294967295\":9007199254740993,\"e\\u0301\":false}},","ok":false}, +{"input":"\"\\ud\\ud800\"","ok":false}, +{"input":"1é345678901,345678901234567890","ok":false}, +{"input":"{\"constructor\":[[\"\\u0000\"]],\"constructor\":\"\\/\",\"\\u00ee9\":[{\"__proto__\":900:199254740993,\"construcr\":1.5}]}","ok":false}, +{"input":"[{u000\\ud8000\":[]},[]]","ok":false}, +{"input":"[[]00,[{},[]]]","ok":false}, +{"input":"[{\"\\u0000\":","ok":false}, +{"input":"[1.5,{\"e\\u0301","ok":false}, +{"input":"[\"","ok":false}, +{"input":"\n\"e\\u3","ok":false}, +{"input":"[]\n","ok":true,"stringify":"[]"}, +{"input":"NaNul 9","ok":false}, +{"input":"{}\\ud","ok":false}, +{"input":"-922","ok":true,"stringify":"-922"}, +{"input":"\\u00null","ok":false}, +{"input":"\"\\\"a\":1,\"a\":d8\"","ok":false}, +{"input":"\"\\uud800\\ud800\"","ok":false}, +{"input":"e-999999999\"\\/9999\"\\/\"","ok":false}, +{"input":"-9223372036854\"__proto__\":775808508","ok":false}, +{"input":"[{\"e\\u0301\":1},[\"\\udc00\",1.5,\"e\\","ok":false}, +{"input":"{\"a\":false}","ok":true,"stringify":"{\"a\":false}"}, +{"input":"{\"{\"429672\\udc004\":-0}","ok":false}, +{"input":"\"\\udc0ud800ud800\"","ok":false}, +{"input":"\"\"\"","ok":false}, +{"input":"-1e40e-99","ok":false}, +{"input":"[00]","ok":false}, +{"input":"[[[[
","ok":false}, +{"input":"\t{} ","ok":true,"stringify":"{}"}, +{"input":"\"e\\u0+301\"\"","ok":false}, +{"input":"{\"42949
7294\":","ok":false}, +{"input":"n[]","ok":false}, +{"input":"{\"constructor\":[[],1e-400],\"\\u0000\":{\"10\":[\"\\ud800\\ud800\"],\"0\":-1,\"4294967294\":{\"constructor\":0,\"0\":-1}},\"-1\":[]}","ok":true,"stringify":"{\"constructor\":[[],0],\"\\u0000\":{\"0\":-1,\"10\":[\"\\ud800\\ud800\"],\"4294967294\":{\"0\":-1,\"constructor\":0}},\"-1\":[]}"}, +{"input":"fa\\uDBFF\\uDFFFse","ok":false}, +{"input":"[{\"429496\"729E4\":{\"constructor\":[\"\\udc00\\ud800\",\"
\"],\"constructor\":[\"\\udc00\\ud800\"],\"\\u00e9\":{}}\r,\"1\":0,\"1\":1e-400}]","ok":false}, +{"input":"[{\"1\":\":\"😀\",\"\\u00e9\":{},\"\\ud800\":\"e\\u0301\"},{}]","ok":false}, +{"input":"{\"e\\u0301\":{\"0::{\"\":\"
\"},\"-1\":{\"__proto__\":{}}}}","ok":false}, +{"input":"[\"\\udc00\\ud800\"]","ok":true,"stringify":"[\"\\udc00\\ud800\"]"}, +{"input":"{\"10\":[]}:[]}","ok":false}, +{"input":"\"\\udce-9999\t99990","ok":false}, +{"input":"[{\"429496724\":400}]","ok":true,"stringify":"[{\"429496724\":400}]"}, +{"input":"\"__proto__\":\u000b","ok":false}, +{"input":"[{\"\\ud800\":\"udc00\",\"-1\":[]},\"\\udc00","ok":false}, +{"input":"\r]\"","ok":false}, +{"input":"{\"constructor\":\"a\",\"-1\":-1e400,\"0\":{\"__proto_\\ud800{\"\":\"\",\"10\":-1e40}],\"constructor\":true}}","ok":false}, +{"input":"\frueuee","ok":false}, +{"input":"{\"-1\":-1e400}","ok":true,"stringify":"{\"-1\":null}"}, +{"input":"\\eud,001.5]","ok":false}, +{"input":"\"\"__proto__\":","ok":false}, +{"input":"\"\\udc0,\\udd800\"","ok":false}, +{"input":"\"\\ud\"__proto__\":c00\"","ok":false}, +{"input":"\"\\ud80\"","ok":false}, +{"input":"-1-1","ok":false}, +{"input":"[true[],\"\",[{\"01\":\"\\ue9\"}]]","ok":false}, +{"input":"{\"e\\u0301\":{\"10\":\"a\"},\"0\":\"
\",\"10\":[]}","ok":true,"stringify":"{\"0\":\"
\",\"10\":[],\"é\":{\"10\":\"a\"}}"}, +{"input":"null","ok":true,"stringify":"null"}, +{"input":"1\\u","ok":false}, +{"input":"[\u000b{","ok":false}, +{"input":"{\"1\":1.5,\"1\":1e400,\"4294967294\"
:1e-","ok":false}, +{"input":"{\"1\":\"😀\",\"e\\\u0000030\u00000301\":0","ok":false}, +{"input":"true[1E+2,{\"__proto__\":{\"01\":[],\"01\":-0},\"constructor\":\"
\"},[[123456789\\ud80012345678901234567890]]]","ok":false}, +{"input":"\"\\udc0
\"","ok":false}, +{"input":"{\"\":[-0,{\"\\u0000\":[]},[\"
\",{\"-1\":false,\"\":\"😀\"},{\"1\":\"\\u0000\",\"0\":1e400,\"a\":\"😀\"}]],\"\\ud800\":[","ok":false}, +{"input":"[[null,[[]]],{\"10\":[false,[]],\"01\":{\"\\ud
800\"9\"a\"},\"0\":tr,{}]","ok":false}, +{"input":"{\"ce","ok":false}, +{"input":"[[{},[[1e400]]],null,{\"4294967295\":\"\\udc00\\ud800\",\"a\":\"\\ud800\",\"0\":1e400}]","ok":true,"stringify":"[[{},[[null]]],null,{\"0\":null,\"4294967295\":\"\\udc00\\ud800\",\"a\":\"\\ud800\"}]"}, +{"input":"[\f","ok":false}, +{"input":"
0","ok":false}, +{"input":"e-999999999","ok":false}, +{"input":"[{\"\\u00e9\":-1},[{\"e\\u0301\":\"\\ud83d\\udde00\",\"0\":\"\"}]","ok":false}, +{"input":"[{\"\":[],\"10\":{\"10\":true,\"__proto__\":false}},[-1e400,{\"constructor\":[1e400,\"a\"],\"\":{\"4294967295\":-1},\"-1\":{}}],[[{\"constructor\":1e-400},{\"constructor\":1.5,\"e\\u0301\":\"\\ud83d\\ude00\"}],{\"__proto__\":{\"__proto__\":1E+2}},[[\"
\",1e400]]]]","ok":true,"stringify":"[{\"10\":{\"10\":true,\"__proto__\":false},\"\":[]},[null,{\"constructor\":[null,\"a\"],\"\":{\"4294967295\":-1},\"-1\":{}}],[[{\"constructor\":0},{\"constructor\":1.5,\"é\":\"😀\"}],{\"__proto__\":{\"__proto__\":100}},[[\"
\",null]]]]"}, +{"input":"\"😀true","ok":false}, +{"input":"[:]","ok":false}, +{"input":"9007154740993","ok":true,"stringify":"9007154740993"}, +{"input":"{\"01\":[-9223372036854775808,[],4294967295\":1E+2,\"a\":{\"e\\u0301\":1e-400},\"__proto__\":\"}],\"constructor\":{\"a\":0}}","ok":false}, +{"input":"{\"\\u0000\":[1E+2,{\"\\u00e9\":[null,1e400],\"1\":\"\\/\",\"0\":null},{\"\":[\"\\u0000\"]}],\"1\":{\"e\\u0301\":[\"\\ud800\\ud800\",{\"a\":123456789012345678901234567890,\"a\":1e-400,\"\":\"\\/\"}],\"1\":-1e400}}","ok":true,"stringify":"{\"1\":{\"1\":null,\"é\":[\"\\ud800\\ud800\",{\"a\":0,\"\":\"/\"}]},\"\\u0000\":[100,{\"0\":null,\"1\":\"/\",\"é\":[null,null]},{\"\":[\"\\u0000\"]}]}"}, +{"input":"\\x\"4294967294\":1E+2,\"10\":{},\"constructor\":[\"\"]}","ok":false}, +{"input":"[]nul]nu\\x","ok":false}, +{"input":"{\"10\":[{}],\"__proto__\":\"😀\"}","ok":true,"stringify":"{\"10\":[{}],\"__proto__\":\"😀\"}"}, +{"input":"-1e]0\u000b0","ok":false}, +{"input":"nula\"","ok":false}, +{"input":"{\"\\u00","ok":false}, +{"input":"[0","ok":false}, +{"input":"{\"a\":\u001f0,\"\\","ok":false}, +{"input":"{e","ok":false}, +{"input":"[\"a\",[[],{\"10\":{\"4294967295\":-1e400},\"constructor\":{\"-1\":\"\"\\udc00\\ud800\",\"0\",\"01\":\"\\ud800\\ud800\",\"\\ud800\":\"
\"},\"10\":\"\\ud800\"},{}],{\"0\":[[\"\\u0000\",\"\",true]]}]","ok":false}, +{"input":"{\"co","ok":false}, +{"input":"{\"c9nstructor\":[\"\\u0000\t]","ok":false}, +{"input":"{\"\\ud800\"\\x\"\\/\\\"}","ok":false}, +{"input":"{\"\\{\"\\u0000\":0,\"\":\"\\ud800\"}00","ok":false}, +{"input":"[1.5,{\"__proto__\":{\"1\":\"
\",\"
\",\"\\ud800\":[false,-0]},\"e\\u0301\":[]},1]","ok":false}, +{"input":"[{\"constructor\":{\"\":\"\\udc00\"},\"\\u00e9\":[{\"4294967295\":\"\\ud800\\ud800\",\"01\":-1,\"4294967294\":\"😀\"}],\"0\":{}}]","ok":true,"stringify":"[{\"0\":{},\"constructor\":{\"\":\"\\udc00\"},\"é\":[{\"4294967294\":\"😀\",\"4294967295\":\"\\ud800\\ud800\",\"01\":-1}]}]"}, +{"input":"{\"\\u00e9\":\"\\udc00\\ud800\"}","ok":true,"stringify":"{\"é\":\"\\udc00\\ud800\"}"}, +{"input":",5,","ok":false}, +{"input":"\"-\"","ok":true,"stringify":"\"-\""}, +{"input":"-922337036854775808","ok":true,"stringify":"-922337036854775800"}, +{"input":"\"__proto__\":}","ok":false}, +{"input":"-0-0nuull","ok":false}, +{"input":"{1\u001f\":\"\\/\"}","ok":false}, +{"input":"\"\\u\"","ok":false}, +{"input":"223372036854775808","ok":true,"stringify":"223372036854775800"}, +{"input":"\\udc00\\ud80]","ok":false}, +{"input":"111","ok":true,"stringify":"111"}, +{"input":"123456789015678901234567890","ok":true,"stringify":"1.234567890156789e+26"}, +{"input":"e\"a\"","ok":false}, +{"input":"]\"10\":[","ok":false}, +{"input":"1.500","ok":true,"stringify":"1.5"}, +{"input":"[{\"4294967294\":[[],1e-400,{\"10\":1,\"4294967294\":1e400,\"10\":1}],\"-1\":[],\"\\u00e9\":-0},[\"\\u0000\",\"\\u00e9\",{\"e\\u0301\":-0,\"01\":2.2250738585072011e-308}],123456789012345678901234567890]","ok":true,"stringify":"[{\"4294967294\":[[],0,{\"10\":1,\"4294967294\":null}],\"-1\":[],\"é\":0},[\"\\u0000\",\"é\",{\"é\":0,\"01\":2.225073858507201e-308}],1.2345678901234568e+29]"}, +{"input":"90071😀99254740","ok":false}, +{"input":"{é10\":\"\\ud83d\\ude00\",\"-1,\"0\":[{\"__pro_proto__\":true,\"10\":null}]}","ok":false}, +{"input":"-\u000b","ok":false}, +{"input":" 0]","ok":false}, +{"input":"{\"\":\\uDBFF\\uDFFF\\u0000\"}\"}","ok":false}, +{"input":"[\"\\/\",\"
","ok":false}, +{"input":"[123456789012367890123[567890]","ok":false}, +{"input":"[[],{\"e\\u0301\":e{\"","ok":false}, +{"input":"-14","ok":true,"stringify":"-14"}, +{"input":"[[[\"\\ud800\",[\"\\true0c00\"],{\"4294967294\":\"\\83d\\ude00\",\"\\u00e9\":-1,\"e\\u0301\":9007199254740993}]],\"\"]","ok":false}, +{"input":"{\"4294967294\":[],\"4294967294\":[[{\"-1\":-\n1e401e400,\"\\u0000\":\"\\ud800\\ud800\",\"-1\":false},\"\"],{\"0\":\"\\/\"},{}],\"\":{\"10\":[{},{\"\":\"\\u00e9\",\"01\":\"\\u0000\",\"a\":\"\\udc00\"},-9223372036854775808]}}","ok":false}, +{"input":"{\"\\u9\":[][]}","ok":false}, +{"input":"E","ok":false}, +{"input":"{\"a\":[\"\\ud83d","ok":false}, +{"input":"--}{}","ok":false}, +{"input":"{\"4294967294\":1.5,\"e\\u0301\":[{},f{},false,1E+2]}","ok":false}, +{"input":"{\"\\u0000\":1E+2}","ok":true,"stringify":"{\"\\u0000\":100}"}, +{"input":"[{\"__proto__\":1E+2,\"__proto__\":[[\"\\udc00\\ud80e\"]]},[]]","ok":true,"stringify":"[{\"__proto__\":[[\"\\udc00\\ud80e\"]]},[]]"}, +{"input":"\"e,\\u0301\"","ok":true,"stringify":"\"e,́\""}, +{"input":"[{[e\\u0301\\u0301\":\\[]}]","ok":false}, +{"input":"nul","ok":false}, +{"input":"{\"4294967295\":[],\"\":[\"\\udc00\\ud800\",[\"\",1e400,\"\\udc00\\ud800\"],[[1.5],[9007199254740993,-1,\"\\/\"],\"
\"]]}","ok":true,"stringify":"{\"4294967295\":[],\"\":[\"\\udc00\\ud800\",[\"\",null,\"\\udc00\\ud800\"],[[1.5],[9007199254740992,-1,\"/\"],\"
\"]]}"}, +{"input":"[\"\\u00e9\",[[{\"\":\"\\ud800\",\"01\":1},{},[-1e400,\"\\u0000\"]]],\"\\ud83d\\ude00\"]","ok":true,"stringify":"[\"é\",[[{\"\":\"\\ud800\",\"01\":1},{},[null,\"\\u0000\"]]],\"😀\"]"}, +{"input":"\"e\\u0301😀\"","ok":true,"stringify":"\"é😀\""}, +{"input":"{\"429496724\":{},\"e\\u0301\":\"\\u0000\",\"01\":true}","ok":true,"stringify":"{\"429496724\":{},\"é\":\"\\u0000\",\"01\":true}"}, +{"input":"[[[]],[\"\",1E+2,null]]","ok":true,"stringify":"[[[]],[\"\",100,null]]"}, +{"input":"{\"__pr{\"__proto__\":\"\",\"\\u00\\+d80000\":\"a\"}","ok":false}, +{"input":"]alse","ok":false}, +{"input":"{\"4294967295\":\"\\u00e9\",\"-1\":[{\"a\":1}]}","ok":true,"stringify":"{\"4294967295\":\"é\",\"-1\":[{\"a\":1}]}"}, +{"input":"[1234567890190123456nul8901234567890]","ok":false}, +{"input":"[{\"4294967295\":2.2250738nul85072011e-30 ,\"01\":false}]","ok":false}, +{"input":"[\"\\u0000\"00\",9007199254740993]","ok":false}, +{"input":"{\".4294967294\":\"\\ud800\"}","ok":true,"stringify":"{\".4294967294\":\"\\ud800\"}"}, +{"input":"[-9223372036854775808]","ok":true,"stringify":"[-9223372036854776000]"}, +{"input":"{\"a\":[false,[1E+2,\"e\\u0301\",{}],9007199254740993]}","ok":true,"stringify":"{\"a\":[false,[100,\"é\",{}],9007199254740992]}"}, +{"input":"[\"😀\",{\"e\\u0301\":\"\"},1e-400]]","ok":false}, +{"input":"1.e-\u000b9999999995","ok":false}, +{"input":"123456789012345678901234\"]a\":1,\"a\":67890","ok":false}, +{"input":"a\"","ok":false}, +{"input":"-1-","ok":false}, +{"input":"{\"01\":{\"\":{},\"01\":[{\"4294967295\":1},9007199254740993]},\"\\u0000\":[{\"constructor\":{\"0\":\"\",\"\\ud800\":9007199254740993},\"\\ud800\":[1e-400]}]}","ok":true,"stringify":"{\"01\":{\"\":{},\"01\":[{\"4294967295\":1},9007199254740992]},\"\\u0000\":[{\"constructor\":{\"0\":\"\",\"\\ud800\":9007199254740992},\"\\ud800\":[0]}]}"}, +{"input":"1.{5","ok":false}, +{"input":"[[1","ok":false}, +{"input":"2.22507385","ok":true,"stringify":"2.22507385"}, +{"input":"[{\"\":{}},{\"constructor\":{\"4294967295\":[\"e\\u0301\"],\"\\u0000\":{\"4294967294\":\"\\udc00\\ud800\"},\"10\":[\"😀\",\"e\\u0301\",0]},\"4294967294\":{},\"01\":{}},{\"e\\u0301\":\"\\ud800\"}]","ok":true,"stringify":"[{\"\":{}},{\"4294967294\":{},\"constructor\":{\"10\":[\"😀\",\"é\",0],\"4294967295\":[\"é\"],\"\\u0000\":{\"4294967294\":\"\\udc00\\ud800\"}},\"01\":{}},{\"é\":\"\\ud800\"}]"}, +{"input":"\\udc00{\"","ok":false}, +{"input":"\"\\8\\ud800\"","ok":false}, +{"input":"\"\\ud83d\\ude00\"","ok":true,"stringify":"\"😀\""}, +{"input":"{\"\\u0000\"","ok":false}, +{"input":"[[\"\\udc00\",12345678901234567890134567890,[-1\re400,{\"constructor\":true,\"a\":\"a\"}]]]","ok":false}, +{"input":"{\"1\":{\"4294967294\":{\"a\":[\"\\u0000\",\"😀\"],\"a\":[\"\\u00e9\",\"😀\"]},\"0\":1E+2},\"e\\u0301\":{\"4294967295\":{\"\\u0000\":{\"\\ud800\":\"\",\"constructor\":\"\\udc00\"},\"4294967295\":{},\"\\ud800\":-1}}}","ok":true,"stringify":"{\"1\":{\"0\":100,\"4294967294\":{\"a\":[\"é\",\"😀\"]}},\"é\":{\"4294967295\":{\"\\u0000\":{\"\\ud800\":\"\",\"constructor\":\"\\udc00\"},\"4294967295\":{},\"\\ud800\":-1}}}"}, +{"input":"\"\\uNaN800\"","ok":false}, +{"input":"{\"01\":1e-400,\"0\":{\"__proto__\":[{},{\"\\ud800\":\"\",\"1\":0,\"4294967295\":\"\"}],\"e\\u0301\":[],\"10\":{\"constructor\":\"\\ud83d\\ude00\",\"\\u00e9\":{\"1\":9007199254740993,\"-1\":9007199254740993},\"\\ud800\":{\"1\":\"
\",\"constructor\":1e-400}}}}","ok":true,"stringify":"{\"0\":{\"10\":{\"constructor\":\"😀\",\"é\":{\"1\":9007199254740992,\"-1\":9007199254740992},\"\\ud800\":{\"1\":\"
\",\"constructor\":0}},\"__proto__\":[{},{\"1\":0,\"\\ud800\":\"\",\"4294967295\":\"\"}],\"é\":[]},\"01\":0}"}, +{"input":"\"\\ud800\" ","ok":false}, +{"input":"\"e\\uunul301\"\"","ok":false}, +{"input":"{\"\\u00e9\":\"\\In","ok":false}, +{"input":"{\"9\":\"\"}","ok":true,"stringify":"{\"9\":\"\"}"}, +{"input":"{\"__proto__\":1E+2}","ok":true,"stringify":"{\"__proto__\":100}"}, +{"input":"1e1e400","ok":false}, +{"input":"[-1,{\"1\":[-0,[1e-400]],\"\":{\"\\u00e9\":\"\\u00e9\"},\"-1\":[]}]","ok":true,"stringify":"[-1,{\"1\":[0,[0]],\"\":{\"é\":\"é\"},\"-1\":[]}]"}, +{"input":"{\"e\\u0301\":[1.5,[\"\\udc00\\ud800\",[],{\"\":9007199254740993}],{\"1\":\"e\\u030\u001f1\"}],\"01\":{\"constructor\":{\"-1\":null,\"4294967294\":\"\\u0000\",\"1\":[123456789012345678901234567890,1e-400]},\"e\\u0301\":-9223372036854775808,\"a\":{\"-1\":\"\",\"e\\u0301\":[\"\\u00e9\",\"
\",\"\\udc00\\ud800\"]}},\"-1\":\"\\ud800\"}","ok":false}, +{"input":"{\"\\u0000\"\\{\"-1\":-0},\"\":90071992547440993,\"a\":{\"\\u00e9\":1.5}}","ok":false}, +{"input":"{\"e\\u0301\":{\"-1\":{},\"0\":{\"\\ud800\":[\"\",false,-9223372036854775808]}},\"4294967295\":{\"__proto__\":\"e\\u0301\"},\"\\u0000\":[false,[\"
\",{}]]}","ok":true,"stringify":"{\"é\":{\"0\":{\"\\ud800\":[\"\",false,-9223372036854776000]},\"-1\":{}},\"4294967295\":{\"__proto__\":\"é\"},\"\\u0000\":[false,[\"
\",{}]]}"}, +{"input":"{\"01\":[{\"10\":\"a\"\"a\",\"01\":-0}]\u0000","ok":false}, +{"input":"[\\x]","ok":false}, +{"input":"\"\\udd8","ok":false}, +{"input":"\"\\ud800800ud80","ok":false}, +{"input":"-1e40{","ok":false}, +{"input":"-0:E","ok":false}, +{"input":"[{},\\u[-9272036854775808]]","ok":false}, +{"input":"[nu-l{}é]","ok":false}, +{"input":"[[}","ok":false}, +{"input":"[][]","ok":false}, +{"input":"nullnulullNaN","ok":false}, +{"input":"{\"01\":[-1,[[],{\"a\":1E+2,\"4294967294\"__proto__\"::\"😀\"}]]}","ok":false}, +{"input":"{\"4294967295\":{\"4294967295\":1e400},\"e\\u0301\":\":\"\\","ok":false}, +{"input":"{\"\\ud800\":[-1],\"4294967295\":{\"\\u0000\":[1.5,[\"😀\",1e400]]},\"4294967295\":[{},[{\"\\u00e9\":\"\",\"01\":\"\",\"\\ud800\":\"\\u0000\"}],[null,{\"a\":\"\\ud800\\ud800\",\"0\":\"\\udc00\"}]]}","ok":true,"stringify":"{\"\\ud800\":[-1],\"4294967295\":[{},[{\"é\":\"\",\"01\":\"\",\"\\ud800\":\"\\u0000\"}],[null,{\"0\":\"\\udc00\",\"a\":\"\\ud800\\ud800\"}]]}"}, +{"input":"e999999999[]","ok":false}, +{"input":"\\ud800nul\f","ok":false}, +{"input":"{\"constructor\":[null],\"0\":{}}","ok":true,"stringify":"{\"0\":{},\"constructor\":[null]}"}, +{"input":"[\"\\udc00\",[\"\\ud800\\0ud800\",{}]]","ok":false}, +{"input":"{\"\\ud800\":{\"10\":[{\"0\":\"\\u0000\",\"e\\u0301\":\"\",\"10\":\"\\u0000\"},-9223372036854775808,1],\"0\":1,\"e\\u0301\":-1e400}}","ok":true,"stringify":"{\"\\ud800\":{\"0\":1,\"10\":[{\"0\":\"\\u0000\",\"10\":\"\\u0000\",\"é\":\"\"},-9223372036854776000,1],\"é\":null}}"}, +{"input":"1\"a\":1,\"a\":","ok":false}, +{"input":"[true,{}]","ok":true,"stringify":"[true,{}]"}, +{"input":"{\"-11","ok":false}, +{"input":"fae","ok":false}, +{"input":"{Infiny}","ok":false}, +{"input":"[[{},\fe-400],[[[false],[9007199250993,1e400,null]]]]","ok":false}, +{"input":"{\"-1\":0,\"10\":[\"\\udc00\"],\"-1\":9007199254740993}","ok":true,"stringify":"{\"10\":[\"\\udc00\"],\"-1\":9007199254740992}"}, +{"input":"{\"\":[[{\"\":1E+2,\"\\u00e9\":\"\\ud83d\\ude00\",\"4294967295\":\"\\ud800\"}],{\"\":null},\"\\udc00\"]}","ok":true,"stringify":"{\"\":[[{\"\":100,\"é\":\"😀\",\"4294967295\":\"\\ud800\"}],{\"\":null},\"\\udc00\"]}"}, +{"input":"\"\u001f\\u0301\"","ok":false}, +{"input":"[]\\u0NaN0","ok":false}, +{"input":"[{\"\\ud800\":\"
\",\"__proto__\":{\"10\":[\"\\/\",2.2250738585072011e-308,-1],\"10\":{}}}]","ok":true,"stringify":"[{\"\\ud800\":\"
\",\"__proto__\":{\"10\":{}}}]"}, +{"input":"{\"10\":{}}","ok":true,"stringify":"{\"10\":{}}"}, +{"input":"{\"1\":\"😀\",\"4294967294\":\"\",\"constructor\":{\"-1\":[{\"e\\u0301\":2.2250738585072011e-308,\"-1\":9007199254740993,\"1\":0},9007199254740993,{}],\"a\":{\"10\":123456789012345678901234567890,\"\\u00e9\":{\"consttor\":1,\"a\":1}}}}","ok":true,"stringify":"{\"1\":\"😀\",\"4294967294\":\"\",\"constructor\":{\"-1\":[{\"1\":0,\"é\":2.225073858507201e-308,\"-1\":9007199254740992},9007199254740992,{}],\"a\":{\"10\":1.2345678901234568e+29,\"é\":{\"consttor\":1,\"a\":1}}}}"}, +{"input":"{\"\\ud800\":{\"01\":9007199\\u254740993},\"\\u0000\":fal00","ok":false}, +{"input":"[[\"
\"]]9","ok":false}, +{"input":"\"e-999999999999\\ud83d\\ude00\"","ok":true,"stringify":"\"e-999999999999😀\""}, +{"input":"{\"0\":-0,\"\":{},\"1\"\":\"\\udc00\"}","ok":false}, +{"input":"{\"\":0,\"\":{\"4294967294\":\"\",\"4294967295\":9007199254740993},\"42967295\":{\"4294Infinity967294\":false,\"__proto__\":\"😀\",\"\\u00e9\":[[\"\\ud83d\\ude00\",\"\\u00e9\"]]}}","ok":true,"stringify":"{\"42967295\":{\"4294Infinity967294\":false,\"__proto__\":\"😀\",\"é\":[[\"😀\",\"é\"]]},\"\":{\"4294967294\":\"\",\"4294967295\":9007199254740992}}"}, +{"input":"[]😀","ok":false}, +{"input":"{\"10\":{\"1\"\"1\"","ok":false}, +{"input":"5]","ok":false}, +{"input":"[{\"\\u00{\"\\u0000\":[[\"\\ud800\\ud800\",false],{\"4294967295\":\"\\udc00\\ud800\",\"constructor\":\"\\udc00\\ud800\"},[-0,\"\\u0000\",\"\\u0000\"]],\"a\":1e400,\"\\u00e9\":true},[{},\"a\"],{\"0\":\"\\ud800\",\"\\u0000\":{\"constructor\":[2.2250738585072011072011e-308,-9223372036854775808],\"0\":{\"__proto__\":-1e400,\"constructor\":\"\\udc00\\ud800\",\"\\u0000\":true}}}]","ok":false}, +{"input":"{\"0\":{}}","ok":true,"stringify":"{\"0\":{}}"}, +{"input":"[1E+2,[\"\\ud800\"]]","ok":true,"stringify":"[100,[\"\\ud800\"]]"}, +{"input":"{\"e\\u0301\":[{\"\\ué0000\":[],\"\\ud800\":1,\"1\":\"\\udc00\\ud\r800\"},\"\\u00e9\",{\"constructor\":2.225073858507","ok":false}, +{"input":"[{\"a\":{\"\\e-999999999d800\":{\"\\ud800\":1.5,\"e\\u0301","ok":false}, +{"input":"{\"4967294\":[[],\"\\ud800\"],\"\":0}","ok":true,"stringify":"{\"4967294\":[[],\"\\ud800\"],\"\":0}"}, +{"input":"[[1.5,90071250993]]","ok":true,"stringify":"[[1.5,90071250993]]"}, +{"input":"[[{\"4294967294\":{\"-1\":1e-400}},{\"0\":\"\\ud800\\ud800\",\"\"\":\"e\\u0301\",\"10\":\"\\/\"},[]],\"\\u00e9\"]","ok":false}, +{"input":"\"\\.udc0\\ud800ud8000\\ud800\"","ok":false}, +{"input":"{\"\\u00e9\"00e9\":{\"\":\"\",\"e\\u0301\":false},\"a\":1,\"___proto__\":[[]]}","ok":false}, +{"input":"[[{},{\"10\":\"\"\\u00e9\":[\"\\ud83d\\ude00","ok":false}, +{"input":"{\t}","ok":true,"stringify":"{}"}, +{"input":"[{\"\\u00e9\":{\"__proto__\":[],\"constructor\":[]},\"\\u00e9\":{\"\\e9\":{\"\\u0000\":\"\\ud800\\ud800\"},\"__proto__\":{\"\":1.5,\"-1\":\"\\u00e9\",\"0\":\"\\ud83d\\ude00\"}},\"\\u00e9\":[[false]]},{},-1]","ok":false}, +{"input":"2.2250738585072011e-3088","ok":true,"stringify":"0"}, +{"input":"[[[],","ok":false}, +{"input":"\"\\ /\\u00","ok":false}, +{"input":"[]]Infnullnity1E+2,1]","ok":false}, +{"input":"{\"a\":\"\\udc00\",\"01\":{\"\":true,\"4294967295\":\"\",\"4294967295\":{\"\\u00e9\":{\"\\ud800\":1e-400,\"\\ud800\":true,\"__proto__\":\"\\udc00\"}}}}","ok":true,"stringify":"{\"a\":\"\\udc00\",\"01\":{\"\":true,\"4294967295\":{\"é\":{\"\\ud800\":true,\"__proto__\":\"\\udc00\"}}}}"}, +{"input":"{\"e\\u0301\":[{\"01\":{\"\":null},\"1\":1e400},[],{\"e\\u0true301\":[\"\\udc00\\ud\r800\",1,\"😀\"]}],\"\":[[],{\"0\":{\"__proto__\":1E+2},\"10\":1e-400},\"\\ud800\"]}","ok":false}, +{"input":"[{\"-1\":{\"10\":[],\"constructor\":\"\\ud800\\ud800\",\"4294967294\":[\"
\",\"\\/\"]}},{\"4\"__proto__\":2949694\":{\"\\u0000\":{\"a\":1e400},\"e\\u0301\":{\"a\":-1e400},\"1\":[-1,\"\\ud83d\\ude00\"]},\"\\ud800\":{}}\\ud800]","ok":false}, +{"input":"{\"4294967294\":\"\\udc00\",\"\\u0000\":[],\"01\":[-9223372036854775808,\"\\udc00\"]}","ok":true,"stringify":"{\"4294967294\":\"\\udc00\",\"\\u0000\":[],\"01\":[-9223372036854776000,\"\\udc00\"]}"}, +{"input":"

\"\\u0000\"","ok":false}, +{"input":"[[[{\"e\\u0301\":\"
\",\"01\":1E+2}],{\"1\":[1e-400,-1e400,1e400],\"4294967294\":1E+2,\"1\":{\"-1\":\"\\ud800\",\"10\":\"😀\",\"\":false}}],{},{\"constructor\":900719925740993}","ok":false}, +{"input":"[{\"constructor\":[{\"1\":\"\\udc00\\ud800\",\"4294967294\":123456789012345678901234567890},[1E+2,1,\"\\ud83d\\ude00\"]],\"__proto__\":\"😀\"},{\"1\":2.2250738585072011e-308},[123456789012345678901234567890,[],{\"a\":1,\"\\u00e9\":\"\\udc00\\ud800\",\"42994967294\":\"\\u0000\"}]","ok":false}, +{"input":"\f\".\"","ok":false}, +{"input":"\"\\u0000\"000\"","ok":false}, +{"input":"{}-","ok":false}, +{"input":"{\"a\":\"\\u0000\",\"\\u0000[:-1}","ok":false}, +{"input":"{\"\\u00e9\":[{},[{\"__proto__\":\"
\"},{\"\"},{\"a\"\",\"1\":-0,\"10\":\"\\u00e9\"}],-1]}","ok":false}, +{"input":"\"\"a\":1,\"a\":
u0000\"\\u","ok":false}, +{"input":"{\"\\u0000\":{},\"constructor\":[],\"\\u00e9\":{\"4294967295\u000b:1E+2,\"\\u00e9\":{\"4294967295\":\"\",\"\\u0000\":{é\"a\":123456789012345678901234567890,\"const\"constructor\":\"\\u00e9\"}}}}","ok":false}, +{"input":"\"\\u00\\ue.[9\"","ok":false}, +{"input":"12345678901234.678901","ok":true,"stringify":"12345678901234.68"}, +{"input":"{\"4294967295\":\u001f\"0\":\"\\u00e9\",\"0\":{\"a\":[\"a\"],\"\":{\"10\":\"\\udc00\\0\\ud800\",\"01\":0,\"4294967295\":-0},\"-1\":\"\"},\"\":1.5},\"1\":\"\\ud800\",\"a\":\"\\ud800\\ud800\"}","ok":false}, +{"input":"{\"\\u0000\":{\"a\":{\"__proto__\":[1e-400],\"0],\"\\u0000\":[\"\\u0000\",-1e400],\"constructor\":\"\\udc00\\ud800\"},\"","ok":false}, +{"input":"fal","ok":false}, +{"input":"{\"\":\"\\u0000\",\"0\":{\"e\\u0301\":{},\"-1\":[1.5,{\"\\ud800\":\"\\ud800\\ud800\",\"1\":\"\\ud800\",\"-1\":\"\\udc00\"},nu\\u00ll]}}","ok":false}, +{"input":"15","ok":true,"stringify":"15"}, +{"input":"123456789012345678901234567]é90","ok":false}, +{"input":"\"e\\u0\"__proto__\":{01\"","ok":false}, +{"input":"\"
\"\"
\"","ok":false}, +{"input":"[[nul,[[],[[-0,2.2250738585072011e-308,-9223372036854775808]]]]","ok":false}, +{"input":"[true,{\"4294967294\":\"\\ud83d\\ude00\",\"4294967294é\":[],\"e\\u0301\":[{\"\\u00e9\":1e-400,\"-1\":\"e\\u0301\"},\"e\\u0301\",true]}]","ok":true,"stringify":"[true,{\"4294967294\":\"😀\",\"4294967294é\":[],\"é\":[{\"é\":0,\"-1\":\"é\"},\"é\",true]}]"}, +{"input":"{\"e\\u0301\":1e-40E}","ok":false}, +{"input":"250738585072011e-308","ok":true,"stringify":"2.50738585072011e-294"}, +{"input":"\"\\u0\\u9\"","ok":false}, +{"input":"].5","ok":false}, +{"input":"[\"\"","ok":false}, +{"input":"[null,2.2250738585072011e-308,[{\"-1\":{\"4294967294\":null,\"01\":\"\",\"e\\u0301\":\"\\/\"},\"constructor\":[\"e\\u0301\",-9223372036854775808,1]},false\\ud800]]","ok":false}, +{"input":"\"","ok":false}, +{"input":"}+","ok":false}, +{"input":"[\"\",\"
\",[{\"\":{}},1e-e00]]","ok":false}, +{"input":"\"\\ud800\\ud\\u00\"","ok":false}, +{"input":"tru","ok":false}, +{"input":"[{\"a\":{\"4294967295\":12345678901234\t56701234567890,\"e\\u0301\":\"\",\"constructor\":{}}}]","ok":false}, +{"input":"[[ ,{[],{}]","ok":false}, +{"input":"{\"4294967295\":\"\\ud83d\\ude00\"}","ok":true,"stringify":"{\"4294967295\":\"😀\"}"}, +{"input":"{\"10\":[\"\\u00e9\",2.2250738585072011e-308],\"01\":0,\"0\":[\"\\ud800\\ud800\",{\"-1\":{\"01\":\"\\ud800\",\"\\u0000\":\"\\/\",\"10\":1.5},\"1\":\"\\ud800\\ud800\"},{\"a\":-1e400,\"01\":{\"\\ud800\":\"\"},\"\\u00e9\":[-1]}]}","ok":true,"stringify":"{\"0\":[\"\\ud800\\ud800\",{\"1\":\"\\ud800\\ud800\",\"-1\":{\"10\":1.5,\"01\":\"\\ud800\",\"\\u0000\":\"/\"}},{\"a\":null,\"01\":{\"\\ud800\":\"\"},\"é\":[-1]}],\"10\":[\"é\",2.225073858507201e-308],\"01\":0}"}, +{"input":"\"a\"nulE","ok":false}, +{"input":"{\"4294,67\u001f294\":","ok":false}, +{"input":" {","ok":false}, +{"input":"{\"0\":[{\"a\":\"\\u00e9\",\"a\":{\"10\":1.5,\"1\":\"\\u00e9\"}}],\"-1\":[],\"4294967295\":{\"4294967295\":{\"1\":\"\\udc00\",\"\\u0000\":[],\"e\\u0301\":0{\"\\ud800\\ud800\":\"\"}},\"a\":[]}}","ok":false}, +{"input":"nul[]+","ok":false}, +{"input":"[[true,{\"429\n967295\":1e-400}],{","ok":false}, +{"input":",-0,[{\"10\":{\"a\":90071","ok":false}, +{"input":"Infini\\uDBFF\\uDFFFty\t[1]","ok":false}, +{"input":"-\\ud800e400","ok":false}, +{"input":"[11e400,[],0]","ok":true,"stringify":"[null,[],0]"}, +{"input":"{\"1\":{},\"a\":{\"01\":\"\\ud800\\ud800\",\"a\":{\\u,\"\\u000\":\"\\udc00\\ud800\"}}","ok":false}, +{"input":"\"\\u00e9\"u00e9\"","ok":false}, +{"input":"[\"\",[{}],{}]","ok":true,"stringify":"[\"\",[{}],{}]"}, +{"input":"{\"4294967295\":{\"e\\u0301\":[2.2250738585072011e-308,2.2250738585072011e-308],\"4294967295\":{}},\"-1\":[[],\"\"]}","ok":true,"stringify":"{\"4294967295\":{\"é\":[2.225073858507201e-308,2.225073858507201e-308],\"4294967295\":{}},\"-1\":[[],\"\"]}"}, +{"input":"{\"a\":[1e400,[[],{\"0\":2.2250738585072011e-308\"a\":1,\"a\":\"constructor\":2.2250738585072011e-308308}],\"\"],\"4294967295\":[{\"e\\u0301\":{},\"e\\u0301\":{\"4294967294\":\"\\u0000\",\"e\\u0301\":-1,\"__proto__\":0}}\\u,\"a\":[{\"constructor\":\"\\u0000\",\"0\":{\"a\":-1,\"constructor\":-1},\"10\":[]},{},{\"01\":\"\\u00e9\",\"\":123456789012345678901234567890,\"\\u00e9\":-9223372036854775808}]}","ok":false}, +{"input":"e-9999999-0","ok":false}, +{"input":"{\"\\ud80-0\":[],\"0\":[],\"01\":[]}","ok":false}, +{"input":"{\"\\ud800\":{\"constructor\":[\"\\ud800\",\"😀\"]},\"-1\":{\"constructor\":true,rue,\"\\u","ok":false}, +{"input":"[[[\"\\udc00\"],1,0]]","ok":true,"stringify":"[[[\"\\udc00\"],1,0]]"}, +{"input":"[[-1e400,{},\"a\"],{\"\\u00e9\":\"\\ud800\"},{\"4294967295\":{\"4294967294\":[\"\",\"😀\",true],\"10\":\"\",\"1\":[0,2.225073","ok":false}, +{"input":"\"\\/\"e","ok":false}, +{"input":"[[[[-1e400],1e-400,2.2250738585072011e-308]]]","ok":true,"stringify":"[[[[null],0,2.225073858507201e-308]]]"}, +{"input":"\"\"a\":1\"a\":\"","ok":false}, +{"input":"\"\\ud\u000b80:","ok":false}, +{"input":"{\"a\":[[],{\"4294967294\":2.2250738585072011e-308,\"\\u00e9\":{\"\\ud800\":\"\\ud800\\ud800\",\"10\":1234567890123456","ok":false}, +{"input":"\"\\é00","ok":false}, +{"input":"{\"10\":[\"\\/\",{},{\"10\":[1e-400,\"\",\"\\udc00\\ud800\"],\"a\":1.5}],\"a\":1e400,\"0\":{\\u\"4294967295\":{\"10\":{\"4294967294\":1234567890123456789012345678890}}}}","ok":false}, +{"input":"{\"0\":{\"0\":{\"constructor\":{}}},\"4294967295\":{\"\\u00e9\":[],\"10\"\\ud800{},\"0\":-0},\"e\\u0301\":[[\"😀\"],[]]}","ok":false}, +{"input":"{\"\\u0000\":\"\\/\",\"1\":[{},[[],\"a\"],\"\\}","ok":false}, +{"input":"}\t","ok":false}, +{"input":"[{\"constructor\":[1E+2,false],\"01\" :{\"4294967294\":[],\"\\u0000\":{}}},\"\\udc00\",[[\"😀\"],[\"\\ud\"]]]","ok":false}, +{"input":"{\"\r_proto__\":[]😀,\"4294967295\":\"e\\u0301\"}","ok":false}, +{"input":"-922337203684775808","ok":true,"stringify":"-922337203684775800"}, +{"input":"{}+","ok":false}, +{"input":"{\"4294967294\":[[{},[],{\"10\":0}],1e-400,[{\"1\":\"a\",\"e\\u0301\":\"e\\u0301\"},[]]]}","ok":true,"stringify":"{\"4294967294\":[[{},[],{\"10\":0}],0,[{\"1\":\"a\",\"é\":\"é\"},[]]]}"}, +{"input":"2.2258585072011e-308","ok":true,"stringify":"2.2258585072011e-308"}, +{"input":"{\"e\\éu0{\"e\\u0301\":{\"e\\u0301\":[[]],\"\\u00e9\":[null,{\"1\":-1},{\"4294967294\":\"\\udc00\",\"4294967295\":\"\\u0000\",\"0\":9007199254740993}]}}","ok":false}, +{"input":"\"😀\"😀\"","ok":false}, +{"input":"truetru","ok":false}, +{"input":"[[[],[{\"01\":\"\\u0000\",\"0\":1e400},0,{\"429496729294\":1E+2,\"44294967295\":-1e400}],\"\"],-9223372036854775808,\"\\ud83d\\ude00\"]","ok":true,"stringify":"[[[],[{\"0\":null,\"01\":\"\\u0000\"},0,{\"429496729294\":100,\"44294967295\":null}],\"\"],-9223372036854776000,\"😀\"]"}, +{"input":"{\"\\ud800\":{\"a\":{},\"0\":\"\\ud800\\uud800\",\"4294967294\":\"\"},\"0\":{\"1\":\"\\ud800\\ud800\",\"constructor\":{\"e\\u0301\":{","ok":false}, +{"input":"[{\"01\":[],\"01\":[],\"a\"\\x:{\"\\u00e9\":{\"-1\":\"
\",\"4294967294\":\"\\u0000\"},\"__proto__\":[]}},[\"\\ud800\\ud800\",-1],[[]]]","ok":false}, +{"input":"{\"\\u00e9\":\"\"}","ok":true,"stringify":"{\"é\":\"\"}"}, +{"input":"\"\\ud800\\u\"__proto___\":d8","ok":false}, +{"input":"{\"\":{\"\":1e-400,\"e\\u0301\":[[\"\\u00e9\",1e400],-1e400]},\"4294967295\":[],\"\\ud800\":\"\\/\"}","ok":true,"stringify":"{\"\":{\"\":0,\"é\":[[\"é\",null],null]},\"4294967295\":[],\"\\ud800\":\"/\"}"}, +{"input":"{\"4294967294\":{},\"e\\u0301\":{\"\\ud.00\":\"\",\"\\ud800\":[]},null\"1\u001f0\":1.5}","ok":false}, +{"input":"ettrue]","ok":false}, +{"input":"{\"constructor\":1}","ok":true,"stringify":"{\"constructor\":1}"}, +{"input":"{\"a\":\"e\\u0301\"\u000b,\"4294967295\":[],\"e","ok":false}, +{"input":"enull-999999999","ok":false}, +{"input":"[1e400,{","ok":false}, +{"input":"[[],{\"e\\u0301\":{\"-1\":\"e\\u0301\",\"4294967294\":\"\\udc00\"},\"1\":\"\"},\"\\ud800\"]","ok":true,"stringify":"[[],{\"1\":\"\",\"é\":{\"4294967294\":\"\\udc00\",\"-1\":\"é\"}},\"\\ud800\"]"}, +{"input":"{\"10\":[\"a\",{},1e-400],\"42949294\":[{\"consructor\":[1e400]},{}]}","ok":true,"stringify":"{\"10\":[\"a\",{},0],\"42949294\":[{\"consructor\":[null]},{}]}"}, +{"input":"2.22507385850\"a\":1,\"a\":72011e-308","ok":false}, +{"input":"{\"a\":-1e400,,\"1\":\"\\/\"}","ok":false}, +{"input":"\"\\ud800\\ud80-0\u0000\"","ok":false}, +{"input":"[{\"a\":{\"4294967295\":{},\"4294967294\":{\"01\":90071992547Infinity40993,\"\\ud😀800\":0,\"__proto__\":\"e\\u0301\"}}}]","ok":false}, +{"input":"]\u000b{","ok":false}, +{"input":"[[],\"\\ud83d\\ude00\",[{\"constructor\":[],\"10\":[\"\\ud800\\ud800\",\"😀\",123456789012345678901234567890],\"\":{\"01\":\"\\u00e9\"}}]]","ok":true,"stringify":"[[],\"😀\",[{\"10\":[\"\\ud800\\ud800\",\"😀\",1.2345678901234568e+29],\"constructor\":[],\"\":{\"01\":\"é\"}}]]"}, +{"input":"{\"-1\":[{},[E+2,{\"-1\":\"a\",\"1\":true,\"__proto__\":\"\"}],\"__proto__\":{\"\\u00e9\":{}}}","ok":false}, +{"input":"[[[{\"\\ud800\":100}],\"\\ud83d\\ude00\",[\"a\"]],[\"\",\"😀\"],[\"\",1E+2,\"e\\u0301\"]]","ok":true,"stringify":"[[[{\"\\ud800\":100}],\"😀\",[\"a\"]],[\"\",\"😀\"],[\"\",100,\"é\"]]"}, +{"input":"\"anull","ok":false}, +{"input":"-1e40true[0","ok":false}, +{"input":"[[[123456789012345678901234567890,{\"a\":1.5.,\"e\\u0301\":\"\\u00e9\"},{\"4294967","ok":false}, +{"input":"{\\u01\":null}","ok":false}, +{"input":"\"\\/\"nul/\"","ok":false}, +{"input":"[\\u{},\"\\/\"","ok":false}, +{"input":"\"\\\"a\":1,\"a\":\"","ok":false}, +{"input":"Eal","ok":false}, +{"input":"[]\\dc00","ok":false}, +{"input":"{\"a\":{\"__protéo__\":[]}}","ok":true,"stringify":"{\"a\":{\"__protéo__\":[]}}"}, +{"input":"{\"-1\u001f\":{\"constructor\":[{,{\",{\"10\":\"\\udc00\",\"-1\":\"\\ud800\",\"\":\"😀\"}]}}","ok":false}, +{"input":"als","ok":false}, +{"input":"[{\"\\u0000\":123456789012345678901234567890,\"e\\u0301\":2.2250738585072011e-308}]","ok":true,"stringify":"[{\"\\u0000\":1.2345678901234568e+29,\"é\":2.225073858507201e-308}]"}, +{"input":"123456789012345678901","ok":true,"stringify":"123456789012345680000"}, +{"input":"[1e-400\u0000","ok":false}, +{"input":"[\\u00","ok":false}, +{"input":"{\"\\u0000\":[{\"a\":\"😀\"},[1e-400,[true,\"\\u0000\"]]],\"1\":[01\":{\"\\NaNu0000\":1e400,\"\\uDBFF\\uDFFF\":-1},\"\":[]},[[1E+2,\"\\udc00\",1.5],[]],{\"constructor\":false,\"4294967295\":true}]}","ok":false}, +{"input":"[]\u000b","ok":false}, +{"input":"[{\"\\u0000\":{},\"4294967[95\":[{}]},-1]","ok":true,"stringify":"[{\"\\u0000\":{},\"4294967[95\":[{}]},-1]"}, +{"input":"{\"-1\":\"\\u0000\"}","ok":true,"stringify":"{\"-1\":\"\\u0000\"}"}, +{"input":"-92233720363685477Infi.ity808","ok":false}, +{"input":"[{\"4294967295\":-400]},[[{\"\\u00e9\":-0,\"a\":2.2250738585072011e-308},[-1e400],0],[1E+2,[123456789nul12345678901234567890,\"e\\u0301\",1e400]],{\"\\u00e9\":[\"\\udc00\\ud800\",1e400],\"\\u0000\":-9223372036854775808,\"constructor\":{\"","ok":false}, +{"input":"[{\"01\":[{\"\\u0000\":1.5},\"\\u0000\"],\"e\\u0301\":\"\"},[1E+2,{\"\\u0000\":0,\"\\ud800\":[],\"-1\":[\"\",\"\\udc00\",\"a\"]}],123456789012345678901234567-890]0]","ok":false}, +{"input":"[{\"a\":{}}]","ok":true,"stringify":"[{\"a\":{}}]"}, +{"input":"{\"\\ud80]\":9007199254740993}","ok":false}, +{"input":"[{\"4294967294\":{\"4294967295\":{},\"__proto__\":[1,1,\"😀\"]}}]","ok":true,"stringify":"[{\"4294967294\":{\"4294967295\":{},\"__proto__\":[1,1,\"😀\"]}}]"}, +{"input":"\"\\udc00\"dc00\"","ok":false}, +{"input":"1E+-2
","ok":false}, +{"input":"[{},123456789012345678901234567890]","ok":true,"stringify":"[{},1.2345678901234568e+29]"}, +{"input":"[{\"01\":-1e400,\"10\":{\"10\":[],\"constructor\":-1,\"constructor\":1}},1e-400,\"\\ud800\\ud800\"]","ok":true,"stringify":"[{\"10\":{\"10\":[],\"constructor\":1},\"01\":null},0,\"\\ud800\\ud800\"]"}, +{"input":"{\"\\u00e9\":true,\"4267295\":[\"\\udc00\\ud800\",{\"\\u00e9\":false,\"01\":\"\\ud83d\\ude00\",\"a\":{\"\":\"\\udc00\",\"__proto__\":\"\\udc00\\ud800\"}},-0],\"\\u0000\":null}","ok":true,"stringify":"{\"4267295\":[\"\\udc00\\ud800\",{\"é\":false,\"01\":\"😀\",\"a\":{\"\":\"\\udc00\",\"__proto__\":\"\\udc00\\ud800\"}},0],\"é\":true,\"\\u0000\":null}"}, +{"input":"[\"\\u00e9\\u00,-972036854775808]","ok":false}, +{"input":"{}","ok":false}, +{"input":"{\"01\":-0,\r-1\":123456789012345678901234567890,\"e\\u0301\":\"\\udc00\"}","ok":false}, +{"input":"{\"__proto__\":[[],[[\"\\ud83d\\ude00\"]]],\"4294967294\":[[[\"\\udc00\\ud800\"],[]]]}","ok":true,"stringify":"{\"4294967294\":[[[\"\\udc00\\ud800\"],[]]],\"__proto__\":[[],[[\"😀\"]]]}"}, +{"input":"1.51.5nul","ok":false}, +{"input":"{\"\\u0000\":{\"\\u00e9\":[[\"\\ud800\\ud800\",\"
\"]],1\":{},\"42e-9999999994967294\":{\"1\":{\"\\u0000\":-9223372036854775808}}}}","ok":false}, +{"input":"{}e{}e9999999","ok":false}, +{"input":"\"\\ud80ud800\\u00\\ud800\"","ok":false}, +{"input":"[900719925474{993,1E+2,\"\\u0000\"]","ok":false}, +{"input":"{\"\\u\\u0000\"","ok":false}, +{"input":"__proto__\":0","ok":false}, +{"input":"1E+2+2","ok":false}, +{"input":"{\"constructor\":[1234567012345678901234567890,[{\"\\ud800\":9007199254740993,\"__proto__\":1.5,\"0\":\"e999999999u00e9\"}]],\"01\":[\"\\/\",\"\\udc00\",[{\"0\":-0,\"1000\":2.2250738585072011e-308,\"\\u00e9\":\"😀\"}]],\"0\":[true,1.5]}","ok":true,"stringify":"{\"0\":[true,1.5],\"constructor\":[1.2345670123456789e+27,[{\"0\":\"e999999999u00e9\",\"\\ud800\":9007199254740992,\"__proto__\":1.5}]],\"01\":[\"/\",\"\\udc00\",[{\"0\":0,\"1000\":2.225073858507201e-308,\"é\":\"😀\"}]]}"}, +{"input":"{\"\\ud800\":\"\\/\",\"a\":\"😀\"}","ok":true,"stringify":"{\"\\ud800\":\"/\",\"a\":\"😀\"}"}, +{"input":"{_p","ok":false}, +{"input":"9
\"","ok":false}, +{"input":"[\"\\ud800\\ud800\"]","ok":true,"stringify":"[\"\\ud800\\ud800\"]"}, +{"input":"{\"-1\":{\"10\":{\"\\u00e9\":{\"\":true,\"4294967295\":-1},\"__proto__\":{\"10\":-0,\"e\\u030 \":1e400},\"e\\u0301\":{\"constructor\":\"\\ud800\",\"\":\"\\ud800\"}},\"a\":{\"__proto__\":{\"\":2.2250738585072011e-308,\"4294967294\":-9223372036854775808,\"-1\":2.2250738585072011e-308},\"1\":{}},\"__proto__\":{}},\"4294967294\":[]}","ok":false}, +{"input":"\r","ok":false}, +{"input":"\"\\ud83d\\u3d\\ude00\"\\ude00\"","ok":false}, +{"input":"{\"__proto__\":\"a\":1,\"a\":\u000b\\u0000\"","ok":false}, +{"input":"\"\\udc00","ok":false}, +{"input":"[fals\\ue]","ok":false}, +{"input":"[1e400]","ok":true,"stringify":"[null]"}, +{"input":"[{\"__proto__\":-1,\"01\":2.2250738585072011e-308,\"10\":1},1.5]","ok":true,"stringify":"[{\"10\":1,\"__proto__\":-1,\"01\":2.225073858507201e-308},1.5]"}, +{"input":"\"a\":1,\"a\":[true]","ok":false}, +{"input":"[[-1,\"\\udc00\"]]","ok":true,"stringify":"[[-1,\"\\udc00\"]]"}, +{"input":"[{\"\\u0000\":{\"constructor\":1.5,\"1\":1E+2,\"0\":\"\\udc00\"},\"\":1.5,\"e\\u0301\":-1},[],[{\"\\u00e9\":\"\\udc00\\ud800\",\"10\":[1,\"😀\"]},{\"__proto__\":[","ok":false}, +{"input":"[1.5,false]NaN","ok":false}, +{"input":"{\"\":{}}[","ok":false}, +{"input":"e{Infinity}","ok":false}, +{"input":"EE","ok":false}, +{"input":"\u001f1","ok":false}, +{"input":"1}","ok":false}, +{"input":"[[],[{\"__proto__\":12345678901234567 901234567890,\"-1\":\"\\udc00\\ud800\"},[],-0]]","ok":false}, +{"input":"e999999999","ok":false}, +{"input":"[{\"\\u00e0e9\":1e40\"","ok":false}, +{"input":"{\"\\u0000\":1e400,\"__proto__\":{\"\":[{\"\\ud800\":2.2250738585072011e-308,\"__proto__\":1},_\":1},{\"01\":\"\",\"e\\u0301\":\"\\ud800\"},[\"\\u0000\",\"\\u00e9\",1e-400]],\"e\\u0301\":[\"a\",{\"\\ud800\":\"\\udc00\",\"\\ud800\":\"a\"},{\"constructor\":1E+2,\"\\ud800\":1e400}]}}","ok":false}, +{"input":"[0009,false]","ok":false}, +{"input":"9}","ok":false}, +{"input":"{\"0\":[],\"\\ud800","ok":false}, +{"input":"-1e4001e400","ok":false}, +{"input":"{\"4294967294\":[4\":[{\"01\":\"e\\u0301\",\"constructor\":[\"\\/\"],\"a\":{}},[\"\\udc00\",[e\\u0301\",[]]]}","ok":false}, +{"input":"{\"__proto__\":9007199254740993,\"44967294\":1e-400,\"e\\u0301\":1}","ok":true,"stringify":"{\"44967294\":0,\"__proto__\":9007199254740992,\"é\":1}"}, +{"input":"\"\\ud800\\u800\"","ok":false}, +{"input":"{\f4294967294\":1e-40E}","ok":false}, +{"input":"{\"-1\":\"\\ud83d\\ude00\",\"0\":{\"1\":\"\",\"a\":[{\"e\\u0301\":😀\"},true]},\"4294967295\":\"\\udc00\\ud800\"}","ok":false}, +{"input":"[NaN]","ok":false}, +{"input":"\"00","ok":false}, +{"input":"2.2250738\u000058😀0720e-308","ok":false}, +{"input":"[\"\\u00e9\",{\"\\u0000\":[{\"10\":1e-400,\"0\":\"\\/\",\"a\":\"\\udc00\\ud800\"},123456789012345678901234567890,[null]]},0]","ok":true,"stringify":"[\"é\",{\"\\u0000\":[{\"0\":\"/\",\"10\":0,\"a\":\"\\udc00\\ud800\"},1.2345678901234568e+29,[null]]},0]"}, +{"input":"{\"0\":2.2250738585072011e-308,\"-1\":[2.2250738585072011e-308,[[-1,-0,\"😀\"],\"\\udc00\\ud800\"],{}],\"4294907294\":[-1e400,{}]}","ok":true,"stringify":"{\"0\":2.225073858507201e-308,\"4294907294\":[null,{}],\"-1\":[2.225073858507201e-308,[[-1,0,\"😀\"],\"\\udc00\\ud800\"],{}]}"}, +{"input":"{\"0\":[],\"e\\u0301\":9007199254740993,\"0\":{\"0\":\"e\\u0301\",\"\\u00e9\":[[9007199254740993,\"\\udc00\\ud800\",-9223372036854","ok":false}, +{"input":"\"\\ud800\\ud\u000080","ok":false}, +{"input":"NaNé{\"\\u00e9\":","ok":false}, +{"input":"{\"\\u0000\":[[[],{\"10\":\"\\ud83d\\ude00\",\"a\":123456789012345678901234567890,\"\":\"e\\u0301\"},\"\\udc00\"],{\"\\u00e9\":{\"4294967294\":\"e\\u0301\",\"\\\\ud800\":0},\"e\\u0301\":false,\"\":[]},[]]}","ok":true,"stringify":"{\"\\u0000\":[[[],{\"10\":\"😀\",\"a\":1.2345678901234568e+29,\"\":\"é\"},\"\\udc00\"],{\"é\":{\"4294967294\":\"é\",\"\\\\ud800\":0},\"é\":false,\"\":[]},[]]}"}, +{"input":"{\"\\u00e9\":{},\"01\":\"\\ud83d\\ude00\"}","ok":true,"stringify":"{\"é\":{},\"01\":\"😀\"}"}, +{"input":"900719925:4740993","ok":false}, +{"input":"\"e\\u0null301\"","ok":false}, +{"input":"{\"\\\"\\u0000\":\"\\ud8\u000b00\"}\u000b00\"}","ok":false}, +{"input":"{\"4294967294\":[{\"__proto__\":[0,123456789012345678901234567890]},\"\\u00e9\"],\"e\\u0301\":[{\"__proto__\":\"\",\"e\\u0301\":[]},\"\\ud83d\\ude00\",[{\"4294967295\":\"\\ud800\\ud800\"},\"\\/\",{\"\":\"\",\"4294967294\":\"\\/\"}]]}","ok":true,"stringify":"{\"4294967294\":[{\"__proto__\":[0,1.2345678901234568e+29]},\"é\"],\"é\":[{\"__proto__\":\"\",\"é\":[]},\"😀\",[{\"4294967295\":\"\\ud800\\ud800\"},\"/\",{\"4294967294\":\"/\",\"\":\"\"}]]}"}, +{"input":"truel","ok":false}, +{"input":"[\"9e\\u0301\",\"\\ud800\"]","ok":true,"stringify":"[\"9é\",\"\\ud800\"]"}, +{"input":"{\"\\ud800\":{},\"e\\u0301\":1,\"\\ud800\"}:{0\":{\"\\ud800\":[],\"__proto__\":[],\"4294967294\":0}}","ok":false}, +{"input":"[{\"0\":{},\"1\":{\"4294967295\":{\"constructor\":\"e\\u0301\",\"constructor\":1},\"4294967294\":1},\"\":\"\\udc00\"},\"\\u0000\",{\"1\":9007199254740993,\"\":1e400,\"\\ud800\":[]}]","ok":true,"stringify":"[{\"0\":{},\"1\":{\"4294967294\":1,\"4294967295\":{\"constructor\":1}},\"\":\"\\udc00\"},\"\\u0000\",{\"1\":9007199254740992,\"\":null,\"\\ud800\":[]}]"}, +{"input":"[{\u001f},[\"","ok":false}, +{"input":"{\"\":{\"10\":{\"42944967294\"","ok":false}, +{"input":"{\"\\u00e9\":[],\"0\":{\"01\":\"\\u0000\"1\":[[],0","ok":false}, +{"input":"]1+","ok":false}, +{"input":"\"\\u00\"\u000b","ok":false}, +{"input":"\\u00\"\\/\"0","ok":false}, +{"input":"{\"429e-9999999994967295\":{}}","ok":true,"stringify":"{\"429e-9999999994967295\":{}}"}, +{"input":"{\"co\\u00nstructor\":[{1e-400","ok":false}, +{"input":"{\"e\\u0301\":\"\\udc00\",\"e\\u0301\":[{\"1\":{\"0\":-1e400},\"\\ud800\":\"e\\nulu0301\",\"-1\":\"\\ud800\\ud800\"}]}","ok":true,"stringify":"{\"é\":[{\"1\":{\"0\":null},\"\\ud800\":\"e\\nulu0301\",\"-1\":\"\\ud800\\ud800\"}]}"}, +{"input":"-1e400400","ok":true,"stringify":"null"}, +{"input":"{\"constructor\":[],\"a\":[[],false,[[123456789012345678901201234567890,0]]]}]]}","ok":false}, +{"input":"[[1e-400,[{\"10\":\"\\ud83d\\ude00\"},2.2250738585072011e-308,\"\\ud83d\\ude00\"]],{}]","ok":true,"stringify":"[[0,[{\"10\":\"😀\"},2.225073858507201e-308,\"😀\"]],{}]"}, +{"input":"[\":{}}]","ok":false}, +{"input":"\"-922337203036854775808,[],{\"\\u00e9\":-1,\"__proto__\":1E+2,\"10\":-1e400}]","ok":false}, +{"input":"2.225073858538585072011e-308","ok":true,"stringify":"2.225073858538585e-308"}, +{"input":"{\"\":[1e400,\"e\\u0301\",{\"4294967295\":1.5,\"4294967295\":{\"\\u00e9\":0,\"__proto__\":-1e400}}]}","ok":true,"stringify":"{\"\":[null,\"é\",{\"4294967295\":{\"é\":0,\"__proto__\":null}}]}"}, +{"input":"[[[-1],{}]]","ok":false}, +{"input":"[{\"\":{\"4294967295\":1e400,\"constructor\":{}},\"4294967295\":{},\"\\ud800\":[[\"\\ud","ok":false}, +{"input":"[[],123456789012345678901234","ok":false}, +{"input":"\\u\\xDBFF\\uDFFF\\{}","ok":false}, +{"input":"[\"
\",{\"constructor\":\"\\/\",\"\\u0000\"\\u{\"0\":","ok":false}, +{"input":"\"\\ud800ud800\"","ok":true,"stringify":"\"\\ud800ud800\""}, +{"input":"truee","ok":false}, +{"input":"{\"\\u00e9\":{\"-1\":null,\"constructor\":0,\"\":2.2250738585072011e-308}","ok":false}, +{"input":"[{\"01\":1,\"10\":9007199254740993},{\"constructor\":123456789012345678901234567890,\"\\ud800\":{\"0\":{\"constructor\":\"
\"},\"constructor\":false}},{}]","ok":true,"stringify":"[{\"10\":9007199254740992,\"01\":1},{\"constructor\":1.2345678901234568e+29,\"\\ud800\":{\"0\":{\"constructor\":\"
\"},\"constructor\":false}},{}]"}, +{"input":"{\"4294967295\":{\"\\ud800\":[{\"\\ud800\":\"😀\",\"\\ud800\":-1e400,\"\\ud800\":true}]}}","ok":true,"stringify":"{\"4294967295\":{\"\\ud800\":[{\"\\ud800\":true}]}}"}, +{"input":"\n{},","ok":false}, +{"input":"{\"\\u0000\":[{}],\"\\u0000\":\"\"","ok":false}, +{"input":"\"\\udc00\\u800\"","ok":false}, +{"input":"{+","ok":false}, +{"input":"[[{\"e\\u0301\":\"\\udc00\\ud800\",\"01\":{\"\\u00e9\":-1,\"e\\u0301\":\"\\udc00\\ud800\"}}]]","ok":true,"stringify":"[[{\"é\":\"\\udc00\\ud800\",\"01\":{\"é\":-1,\"é\":\"\\udc00\\ud800\"}}]]"}, +{"input":"\"\\u000e9\"","ok":true,"stringify":"\"\\u000e9\""}, +{"input":"{\"0\":{\"a\":1,\"-1\":-0},\"\\ud800\":[{\"a\":[],\"__proto__\":\"\"\"10\":[\"\\udc00\",\"a\",\"e\\u0301\"]},{\"\\u00e9\":{\"4294967295\":-0,\"__proto__\":1.5},\"\\u0000\":1.5,\"1\":{\"e\\u0301\":1e400,\"1\":9007199254740993}}]}","ok":false}, +{"input":"-1e400Infinity","ok":false}, +{"input":"{\"0\":[[],-92233720368547736854775775808,\"
\"0]}","ok":false}, +{"input":"-1\":\"\\/\"","ok":false}, +{"input":"😀\"","ok":false}, +{"input":"[{\"\\u00e9\":[0,\"
\",{\"constructor\":123456789012345678901234567890,\"\":1e-400}],\"\\u00e9\"u00e9\":\"\\udc00\\ud800\"},1e400,[]]","ok":false}, +{"input":"-0 ","ok":false}, +{"input":"[{\"1\":{\"10\":[1.5,false],\"\\ud800\":[\"\",-1e400,-1],\"\\u0000\":1e-400},\"1\":{\"10\":{\"\\u00e9\":1E+2},\"4294967294\":{\"01\":1E+2}},\"e\\u0301\":1E+2},[\"\\udc00\\ud800\",[],{}]]","ok":true,"stringify":"[{\"1\":{\"10\":{\"é\":100},\"4294967294\":{\"01\":100}},\"é\":100},[\"\\udc00\\ud800\",[],{}]]"}, +{"input":"+]","ok":false}, +{"input":"[1223456789012345678901234567890","ok":false}, +{"input":"{\"\":{\"a\":\"
\"},\"__proto__\":[\"e\\u0301\"],","ok":false}, +{"input":"[{\"\\u00e9\":{\"a\":\"\",\"\\u00e9\":2.2250738585072011e-308,\"4294967295\":\"\\udc00\\ud800\"},\"-1\":[]}]","ok":true,"stringify":"[{\"é\":{\"a\":\"\",\"é\":2.225073858507201e-308,\"4294967295\":\"\\udc00\\ud800\"},\"-1\":[]}]"}, +{"input":"{\"1\":\"
\",\"con\"c\"constructor\":{\"\":-1e400}}😀","ok":false}, +{"input":"é[{}]","ok":false}, +{"input":"{\"42\\\u0000","ok":false}, +{"input":"{\"e\\u0301\":{\"\\\":{\"\\u0000\":\"\"}}","ok":false}, +{"input":"[{\"\\u0000\":{\"constructor\":\"\\u0000\",\"0{}},\"__proto__\":{\"e\\u0301\":{},\"\":[\"😀\"]},\"1\":{\"__proto__\":\"e\\u0301\",\"\\u00e9\":\"\\u0000\",\"1\":{}}},\"\\ud800\"]","ok":false}, +{"input":"{\"1\":[1.5],\"__proto__\":{\"\\u00e9\":[[true]]},\"e\\u0301\":1E+2}","ok":true,"stringify":"{\"1\":[1.5],\"__proto__\":{\"é\":[[true]]},\"é\":100}"}, +{"input":"[{},\"\\/\",[[],[],{\"4294967295\":[0],\"e\\u0301\":\"\\ud800\\ud800\"}]]","ok":true,"stringify":"[{},\"/\",[[],[],{\"4294967295\":[0],\"é\":\"\\ud800\\ud800\"}]]"}, +{"input":"[[[],{\"\\u00e9\":\"\\ud800\\ud800\",\"10\":{\"constructor\":1e40,\"__proto__\":\"\\ud800\\ud800\",\"\":\"\\u00e9\"},\"\\u00e9\"","ok":false}, +{"input":"1Infinit","ok":false}, +{"input":"2.2250738585072011e-3null0","ok":false}, +{"input":"{\"\\ud800\":[\"\\ud83d\\ude00\"]}00\"]}","ok":false}, +{"input":"\"\\udc00\"}\u000b","ok":false}, +{"input":"[\"\\u0000\",{\"__proto__\":[[-1,\"\\ud800\"]]}]","ok":true,"stringify":"[\"\\u0000\",{\"__proto__\":[[-1,\"\\ud800\"]]}]"}, +{"input":"\\uDBFF\\uDFF\\uDFFF1e4 0","ok":false}, +{"input":" \"\"\"","ok":false}, +{"input":"2\\u00","ok":false}, +{"input":"e\\0301\"","ok":false}, +{"input":"{\"-1\":{\"\\ud800\":[-0],\"01\":\"\"}}","ok":true,"stringify":"{\"-1\":{\"\\ud800\":[0],\"01\":\"\"}}"}, +{"input":"\"\\ud80+","ok":false}, +{"input":"{\"\\u00e9\":{\"-1\":{\"-1\":-1,\"0\":false},\"4294967294\":[[\"e\\u0301\",1e400,1e-400]]},\"4294967294\":{\"constructor\":[{},{\"-1\":\"e\\u0301\",\"\":\"
\"},[1.5]]},\"\\u0000\":[\"\\udc00\",[]]}","ok":true,"stringify":"{\"4294967294\":{\"constructor\":[{},{\"-1\":\"é\",\"\":\"
\"},[1.5]]},\"é\":{\"4294967294\":[[\"é\",null,0]],\"-1\":{\"0\":false,\"-1\":-1}},\"\\u0000\":[\"\\udc00\",[]]}"}, +{"input":"{\"42949672nu94\":[]}","ok":true,"stringify":"{\"42949672nu94\":[]}"}, +{"input":"\"\\ud83d\\ude","ok":false}, +{"input":"[][][][]","ok":false}, +{"input":"1\\uDBFF\\uDFFF400","ok":false}, +{"input":"\"\\\\x00e9\"","ok":true,"stringify":"\"\\\\x00e9\""}, +{"input":"{\"__proto__\":\"\",\"4294967295\":[]}","ok":true,"stringify":"{\"__proto__\":\"\",\"4294967295\":[]}"}, +{"input":":","ok":false}, +{"input":"{\"10\":[{\"constructor\":[9007\"__proto__\":199254740993,123456789012345678901Infinity34567890,\"
\"]}]}","ok":false}, +{"input":"{\"\\ud800\":\"\",\"e\\u0301\":{\"e\\u0301\":1.5,\"\\u0000\":[\u001f{\"__proto__\":\"e\\u0\",\"__proto__\":\"\\/\",\"10\":1.5},123456789012345678901234567890,{\"01\":\"\\u00\\ud8009\"}]}}","ok":false}, +{"input":"tr e","ok":false}, +{"input":"{\"1\":[{\"4294967294\":{\"10\":\"😀\",\"4294967294\":true,\"e\\u0301\":-9223372036854775808},\"constructor\":{\"\\ud800\":2.2250738585072011011e-308,\"a\":E\"
\",\"0\":123459012345678901234567890}}],\"\\u00e9\":\"\\u00e9\"}","ok":false}, +{"input":"[{\"e\\u0301\":[[-1],{\"a\":\"\\udc00\",\"0\":\"😀\"},-1],\"\\u00","ok":false}, +{"input":"{\"\\ud800\":[[],-0,[{\u001f\\u00e9\":12345678901234\\uDBFF\\uDFFF5678901234567890},[1e-400]]],\"\\ud800\":[],\"4294967294\":{\"\\u0000\":{\"\\ud800\":{\"-1\":\"
\"},\"\\u0000\":\"😀\"},\"0\":[]}}","ok":false}, +{"input":"{\"0\":{\"-1\":\"\\u0000\",\"__p__proto__\":[{\"4294967295\":1,\"\\ud800\":\"\\udc00\",\"-1\":\"\\ud800\\ud800\"},[0,false,1e400]]},\"\\u0000\":\"a\",\"__proto__\":[{\"10\":\"\",\"01\":{}}]}","ok":true,"stringify":"{\"0\":{\"-1\":\"\\u0000\",\"__p__proto__\":[{\"4294967295\":1,\"\\ud800\":\"\\udc00\",\"-1\":\"\\ud800\\ud800\"},[0,false,null]]},\"\\u0000\":\"a\",\"__proto__\":[{\"10\":\"\",\"01\":{}}]}"}, +{"input":"[NaN","ok":false}, +{"input":"{\"\":[{\"1\":\"\",\"constructor\":\"\\u\\0000\"}],\"42949672950:{}}","ok":false}, +{"input":"2.2250738585072011e-","ok":false}, +{"input":"[[{\"a\":{\"1\":\"a\",\"01\":1e400,\"constructor\":1},\"__proto__\":\"\",\"\\ud800\":[1E+2]},{\"constructor\":1E+2,\"10\":\"\\u00e9\",\"4294967295\":false}],{},{\"__proto__\"+[null,1E+2,[]]}]","ok":false}, +{"input":"[9007199254740993]]]","ok":false}, +{"input":"{\"-1\":\"a\",\"\\ud800\":-9223372036854775808,\"1\":false}","ok":true,"stringify":"{\"1\":false,\"-1\":\"a\",\"\\ud800\":-9223372036854776000}"}, +{"input":"[99","ok":false}, +{"input":"{\"4294967295\":\"\\ud83d\\ude00\",\"e\\u0301\":[-1,[-1],{\"4294967295\":0,\"01\":-1,\"\\u00e9\":[true,-1]}e\\u0301\":[]}","ok":false}, +{"input":"[{\"1\":{}}E0]","ok":false}, +{"input":"[[{\"10\":[\"\\/\",\"
\"],\"\\ud8001\":{\"01\":-92\\x3","ok":false}, +{"input":"\"a\":1\"a\":0","ok":false}, +{"input":"😀{\"constructor\":[{\"0\":\"a\",\"4294967294\":1E+2,\"e\\301\":1e-400},0,\"\\udc00\\ud800\"]},false,-9223\f372036854775808]","ok":false}, +{"input":"\"\\ud80","ok":false}, +{"input":"{\"4294967295\":[}],\"__proto__\":null,\"0\":-0}","ok":false}, +{"input":"[1.5,{\"4294967294\":-9223372036854775808},[{\"\":\"\\ud800\\ud800\"},[false]]]","ok":true,"stringify":"[1.5,{\"4294967294\":-9223372036854776000},[{\"\":\"\\ud800\\ud800\"},[false]]]"}, +{"input":"{\"0\":1e-\"__proto__\":00,\"-1\":{},\"\":null}","ok":false}, +{"input":"[[Na","ok":false}, +{"input":"{}.","ok":false}, +{"input":"[\"\\ud83d\\ude00\",{\"\\u00e9\":[2.2+250738585072011e-308],\"-1\":{}}]","ok":false}, +{"input":"NaN\"\"\\/\"","ok":false}, +{"input":"1234567890123456781256781234567890","ok":true,"stringify":"1.2345678901234568e+33"}, +{"input":",{\"-1\"-[]}","ok":false}, +{"input":"\"\\ud83d\\ud\\ude0","ok":false}, +{"input":"[[\"\\u00\n00\"","ok":false}, +{"input":"\"\\\\u00\\uDBFF\\uDFFFe-\"","ok":true,"stringify":"\"\\\\u00􏿿e-\""}, +{"input":"\"\"NaN","ok":false}, +{"input":"1234.56789012345678901234567","ok":true,"stringify":"1234.567890123457"}, +{"input":"nulle+999999999","ok":false}, +{"input":"{\"1\":[[{\"1\":\"\\ud83d\\ude00\"}],{\"0\":1e-400},true],\"-1\":\"\\ud800\\ud800\"}","ok":true,"stringify":"{\"1\":[[{\"1\":\"😀\"}],{\"0\":0},true],\"-1\":\"\\ud800\\ud800\"}"}, +{"input":"\"e\"","ok":true,"stringify":"\"e\""}, +{"input":"-\"__proto__\":","ok":false}, +{"input":"[{\"-1\":[1E+2]},[{\"\\ud80","ok":false}, +{"input":"-e-9999999991e400400","ok":false}, +{"input":"fal\"__proto__\":","ok":false}, +{"input":"{\"\":-0,\"constructor\":,\"constructor\":[]}","ok":false}, +{"input":"[{\"4294967294\":1e400,\"e\\u0301\":{}},0,false]","ok":true,"stringify":"[{\"4294967294\":null,\"é\":{}},0,false]"}, +{"input":"[{\"4294967294\":\"\\ud800\"},{},\\ud800\\ud800\"]","ok":false}, +{"input":"[\"e\\u0301\",[{\"4294967295\":\"\\ud83d\\ude00\",\"constructor\":\"a\",\"4294967294\":{\"\":1e400,\"\\u00e9\":9","ok":false}, +{"input":"{\"a\":\"e\\u0301\"}","ok":true,"stringify":"{\"a\":\"é\"}"}, +{"input":"{\"\\u:0e9\":[[],\"\\ud800\",[]]Infinity}","ok":false}, +{"input":"{\"__proto__
:\"\\/\t\"}","ok":false}, +{"input":"-\\e40","ok":false}, +{"input":"{\"429967294\":\"\\/\",\"10\":9007199254740993}E","ok":false}, +{"input":"[{\"4294967295\":fatruese,\"-1\":[],\"01true:{\"4294967294\":123456789012345678901234567890}}]","ok":false}, +{"input":"{\"10\":[]}","ok":true,"stringify":"{\"10\":[]}"}, +{"input":"[{\"\\u0000\":[[9007199254740993],-1e400,[1e-400,\"\\udc00\\ud800\",\"\\u0000\"]],\"01\":-9223372036854775808},[\"/\",{},{\"1\":123456789012345678901234567890,\"\\u00e9\":\"\\/\"}],1]","ok":true,"stringify":"[{\"\\u0000\":[[9007199254740992],null,[0,\"\\udc00\\ud800\",\"\\u0000\"]],\"01\":-9223372036854776000},[\"/\",{},{\"1\":1.2345678901234568e+29,\"é\":\"/\"}],1]"}, +{"input":"{\\uDBFF\\DFFFé","ok":false}, +{"input":"{\"\":{\"\\u00e9\":{}}}","ok":true,"stringify":"{\"\":{\"é\":{}}}"}, +{"input":"-920023372036854 4775808","ok":false}, +{"input":"[null,{\"e\\u0301\":\"e\\u0301\",\"42949672e-9999999994\":{\"0\":\"\\u0000\",\"01\":{\"01\":\"😀\"}}}]","ok":true,"stringify":"[null,{\"é\":\"é\",\"42949672e-9999999994\":{\"0\":\"\\u0000\",\"01\":{\"01\":\"😀\"}}}]"}, +{"input":"{\"0\":0\"e\":1,\"a\":","ok":false}, +{"input":"{\"constructor\":{\"4294967295\":[[],0,{\"\\u0000\":\"😀\",\"a\":null}],\"4294\\ud80067295\":{\"{\"\\u0000\":\"a\",\"429496\\x7294\":{\"__proto__\":-0,\"__proto__\":1E+2,\"-1\":\"\"}}}}","ok":false}, +{"input":"[{\"constructor\":[]},[],{\"1\":-0}]]","ok":false}, +{"input":"2.225025073858507385850720","ok":true,"stringify":"2.2250250738585073"}, +{"input":"\"+\\u\"\"","ok":false}, +{"input":"\"\"E","ok":false}, +{"input":"\"t\\rue","ok":false}, +{"input":"

","ok":false}, +{"input":"{\"\\ud800\":{},\"-01\":{\"4294967295\":-1}}:-1}}","ok":false}, +{"input":"{\"e\\u0301\":0,\":0,\"\":1E+2,\"e\\u0301\":true}","ok":false}, +{"input":"[]]","ok":false}, +{"input":"[{\"429496é295\":-0},\"\\u,\"\\udc00\",\"\"]","ok":false}, +{"input":"1E+2\"a\"\":1,\"","ok":false}, +{"input":"\\ud800\\ud8","ok":false}, +{"input":"{\"\"42949672NaN94\":-1e400,\"a\":[[],[","ok":false}, +{"input":"[\"
\",\"\\\"\\u0000[]]","ok":false}, +{"input":"{\"constructor\":\"\\u00e9\",\"e\\u0301\":\"a\"}","ok":true,"stringify":"{\"constructor\":\"é\",\"é\":\"a\"}"}, +{"input":"n\t","ok":false}, +{"input":"lll","ok":false}, +{"input":"{\"{\"-1\":{\"10\":\"","ok":false}, +{"input":"{\"10\":null}","ok":true,"stringify":"{\"10\":null}"}, +{"input":"{\"10\":1.5,\"__proto__\":{\"0\":2.2250738585072011072011e-308}}","ok":true,"stringify":"{\"10\":1.5,\"__proto__\":{\"0\":2.225073858507201e-308}}"}, +{"input":"\"Eud83d\\ude00\"","ok":true,"stringify":"\"Eud83d\\ude00\""}, +{"input":"\"\\udc\"__proto_\":00\\u","ok":false}, +{"input":"{\"10\":0,\"\\u0000\":-1,\"0\":[123456789012345678901234567890]}0","ok":false}, +{"input":"{\"4294967294\":{\"e\\u0301\":[{},\"\\udc00\\ud800\",\"e\\u0301\"],\"10\":123456789012345678901234567890,\"0\":[{\"1\"{\"
\"},-1e400,[\"\\u\\ud83d\\ude00\"]]},\"-1\":-1e400}","ok":false}, +{"input":"{\\u0301\":\"\\udc00\"}","ok":false}, +{"input":"fafalse","ok":false}, +{"input":"\"
\"

\"","ok":false}, +{"input":"[{\"\\ud800\":1e-400,\"\\u00e9\":\"\\udc00\"}]","ok":true,"stringify":"[{\"\\ud800\":0,\"é\":\"\\udc00\"}]"}, +{"input":"[{\"1\"\\u:[9007199254740993,-1,{\"-1\":1.5,\"\":0}]},9007199254740993]","ok":false}, +{"input":" nulll","ok":false}, +{"input":"{\"constructor\":{\"01\":2.2250738585072011e-308},\"__proto__\":[{}],\"\\ud80\"\\ud800\\ud800\"}","ok":false}, +{"input":"[][","ok":false}, +{"input":"0\"__proto__\":","ok":false}, +{"input":"[\"\\\\uDBFF\\uDFFF\"","ok":false}, +{"input":"{\"4294967295\":{\\\\u00u-1\":\"
\",\"\\u00e9\":1,\"10\":\"\\udc0c00\\ud800\"}}","ok":false}, +{"input":"[[],[-9223372036854775808,\"\\/\"Infinity],{\"01\":[[a\"]],\"0\":-1e400,\"10\":\"\\udc00800\"}]","ok":false}, +{"input":"{\"\\u0000\":[{\"01\":[true,\"a\"],\"\\u0000\":{\"1\":\"\\ud83d\\ude0é0\"}}]}","ok":false}, +{"input":"nul\\u001E+2","ok":false}, +{"input":"{\"[-1","ok":false}, +{"input":"{\"10\":2.2250738585072011e-308}","ok":true,"stringify":"{\"10\":2.225073858507201e-308}"}, +{"input":"{\"\":{\"
c[onstructor\":-1,\"\\u00e9\":{\"-1\":[-1e400],\"4294967295\":\"\\u0000\",\"-1\":2+.2250738585072011e-308},\"\\u00e9\":1e400}}","ok":false}, +{"input":"9007199252\\x4740993 ","ok":false}, +{"input":"[{\"\\u00e9\":\"\\udc00\"},[[]],{\"0\":\"\\ud83d\\ude00\",\"1\":[[\"\\/\",1E+2E],\"\\ud8","ok":false}, +{"input":"[90071992547409930null😀\"]","ok":false}, +{"input":"{\"1\":[\"😀\"],\"e\\u}0301\":1,\"1\":\"\\u00e9\"}","ok":false}, +{"input":"2.225073858507201.e-300","ok":false}, +{"input":"{\"-1null\":0\"-1\":0","ok":false}, +{"input":"{\"e\\u0301\":[-0],","ok":false}, +{"input":"\"\\u00\"","ok":false}, +{"input":"{\"e\\u0301\":[{\"0\":[],\"10\":{\"10\":\"😀\",\"__proto__\":\"\\/","ok":false}, +{"input":"-92233720368547758\"8","ok":false}, +{"input":"][]null","ok":false}, +{"input":"\"😀\"a\":1,\"a\":","ok":false}, +{"input":"[\"\\u0000\",{\"4294967295\":[],\"4294967294\"\r:[[1234567890123459678901234567890]]}]","ok":true,"stringify":"[\"\\u0000\",{\"4294967294\":[[1.2345678901234597e+30]],\"4294967295\":[]}]"}, +{"input":"{\\ud800}","ok":false}, +{"input":"{9}\f","ok":false}, +{"input":"\"\\ud83d\\ude0e9999999990\"","ok":true,"stringify":"\"😎9999999990\""}, +{"input":"[[{\"\\u00e9\":\"\"E\"4967294\":\"
\"}]]","ok":false}, +{"input":"[[0,\"\\udc00\",{\"4294967294\":false,\"constru😀ctor\":{\"0\":2.2250738585072011e-308,\"consuctor\":\"\\u00e9\"},\"e\\u0301\":[\"a\",9223372036854775808]}],\"\\ud800\"]","ok":true,"stringify":"[[0,\"\\udc00\",{\"4294967294\":false,\"constru😀ctor\":{\"0\":2.225073858507201e-308,\"consuctor\":\"é\"},\"é\":[\"a\",9223372036854776000]}],\"\\ud800\"]"}, +{"input":"{\"constructor\":{:\"\\udc\"},\"-1\":[]}","ok":false}, +{"input":":\"\\u0\\u0000\"","ok":false}, +{"input":"{\"10\":-9223372036854775808,\"constructor\":\"a\"}","ok":true,"stringify":"{\"10\":-9223372036854776000,\"constructor\":\"a\"}"}, +{"input":"{\"1\":[[[]nul,[\"\\","ok":false}, +{"input":"[\"\\u00\"_pr","ok":false}, +{"input":"{\"\\u0000\":\"
\",\"-1\":1,\"1\":{}}","ok":true,"stringify":"{\"1\":{},\"\\u0000\":\"
\",\"-1\":1}"}, +{"input":"{\"__proto__protto__\":{}}","ok":true,"stringify":"{\"__proto__protto__\":{}}"}, +{"input":"[é","ok":false}, +{"input":"{\"a\":1","ok":false}, +{"input":"[\"\\u00e9\",\"1","ok":false}, +{"input":"[{\"-1\":[\"\\ud800\",-","ok":false}, +{"input":"{\"\\ud800\"","ok":false}, +{"input":"[{\"constructor\":[]}]","ok":true,"stringify":"[{\"constructor\":[]}]"}, +{"input":"{\"\":\"\\ud800\",\"10\":{\"10\f\":[{},-1e00],\"1\"[:\"😀\"},\"e\\u0301\":{}}","ok":false}, +{"input":"2.225073\u000b585072011e-308","ok":false}, +{"input":"[{\"\\u00e9\":\"a\"}]","ok":true,"stringify":"[{\"é\":\"a\"}]"}, +{"input":"{\"__protoo__","ok":false}, +{"input":"[[{\"a\":{\"01\":9007199254740993,\"a\":\"\\u0000\",\"4294967295\":\"\\udc00\"},\"10\":\"😀\"},\"\\ud800\\ud800\",[{\"1\":1,\"\\u00e9\":null},\"\\ud800\"]],{\"constructor\":\"a\"}]","ok":true,"stringify":"[[{\"10\":\"😀\",\"a\":{\"01\":9007199254740992,\"a\":\"\\u0000\",\"4294967295\":\"\\udc00\"}},\"\\ud800\\ud800\",[{\"1\":1,\"é\":null},\"\\ud800\"]],{\"constructor\":\"a\"}]"}, +{"input":"{\"\\u00e9\":\"\\u00e9\",\"4294967294\":1e400}","ok":true,"stringify":"{\"4294967294\":null,\"é\":\"é\"}"}, +{"input":"{\"-1\":}","ok":false}, +{"input":"{\"4","ok":false}, +{"input":"01E+2,1e0,\"\\udc00\"]","ok":false}, +{"input":"+[e-400,\"\\/\"]","ok":false}, +{"input":"\"\\u00e00ee00e","ok":false}, +{"input":"{\"\\ud800\":\"\\ud800","ok":false}, +{"input":"NaN[","ok":false}, +{"input":"\"😀+\"","ok":true,"stringify":"\"😀+\""}, +{"input":"[[2.2250738585072011e-308,[\"\",\"
\"],{\"-1\":[1234563456789012345678901234567890]}],1E+2,1e400]","ok":true,"stringify":"[[2.225073858507201e-308,[\"\",\"
\"],{\"-1\":[1.2345634567890124e+33]}],100,null]"}, +{"input":"[{\"-1\":[],\"\\ud800\":\"\"},{\"10\":-1e400,\"10\":[\"\",{},[null]],\"4294967294\":[\"\\udc00\",{\"4294967295\":-1},{\"constructor\":250738585072011e-308}]},[[[\"\\u00e9\",1E+2,\"\\/\"],2.2250738585072011e-308,[]],[[1.5,\"\\udc00\"],{\"e\\u0301\":\"\\udc0.\\ud800\"},-1e400],-0]]","ok":false}, +{"input":"[\"e\\u0\u001f301301\"","ok":false}, +{"input":"{\"4294967295\":\"e\\u0301\",\"__proto__\":{\"1\":[],\"__proto__\":{\"4294967294\":[\"
\"],\"01\":{\"consnulltructor\":\"\\udc00\"},\"4294967295\":\"\\ud800\\ud800\"},\"-1\":\"e\\u0301\"}}","ok":true,"stringify":"{\"4294967295\":\"é\",\"__proto__\":{\"1\":[],\"__proto__\":{\"4294967294\":[\"
\"],\"01\":{\"consnulltructor\":\"\\udc00\"},\"4294967295\":\"\\ud800\\ud800\"},\"-1\":\"é\"}}"}, +{"input":":\"\\ud800","ok":false}, +{"input":"[{\"\\u00e9\":[true,[1e-\"__proto__\":00,\"\\ud800\\ud800\"],123456789012345678901234567890],\"-1\":[true,[ru},[\"\\u0000\",-0]]}]","ok":false}, +{"input":"{\"4294967294\":1,\"1\":{\"e\\u0301\":\"\\/\"},\"a\":{\"4294967294\":{\"0\":[.],\"-1\":{}},\"0\":\"
\",\"e\\u0301\":-9223372036854775808}}","ok":false}, +{"input":"-\"\\u00e9\":\"\"9\":\"\"}","ok":false}, +{"input":"[[[],{\"consconsnultructor\":[1,1.5,\"\\ud8ud800\\ud800\"],\"-1\":{}},\"
\"],9007199254740993]","ok":false}, +{"input":"E]","ok":false}, +{"input":"\f","ok":false}, +{"input":"[[],[\"\\u0000\"],[{},{\"constructor\":[\"\\/\",\"\\ud800\",-1],\"\\ud800\":\"a\",\"__proto__\":oto__\":\"
\"},[123456789012345678901234567890,[\"e\\u0301\"]]]]","ok":false}, +{"input":"\"a\"\u001f","ok":false}, +{"input":"{\"😀10\":[\"\"],\"\\u0000\":\"
\"}\"
\"}","ok":false}, +{"input":"1234567890","ok":true,"stringify":"1234567890"}, +{"input":"{\"a\":[]}:[:[","ok":false}, +{"input":"1e-400","ok":false}, +{"input":"{\"01\":{},\"4294967295\":{\"01\":9007199254740993},\"construct:true}","ok":false}, +{"input":"\t1\u000b","ok":false}, +{"input":"{\"-1\":1}","ok":true,"stringify":"{\"-1\":1}"}, +{"input":"[\\udc","ok":false}, +{"input":"{\"\\u0000e\"__proto__\":\":{\"e\\u0301\":\"\"}}","ok":false}, +{"input":"[{\"10\":-1e400,\"__proto__\":\"\",\"\\u0000\":[]}]","ok":true,"stringify":"[{\"10\":null,\"__proto__\":\"\",\"\\u0000\":[]}]"}, +{"input":"\"\\ud80d80\\udc00null","ok":false}, +{"input":"[{\"\\u00e9\":\"\\/\"},-9223372036854775808,\"\\/\"]","ok":true,"stringify":"[{\"é\":\"/\"},-9223372036854776000,\"/\"]"}, +{"input":"\"\\u0\"\\u0\"","ok":false}, +{"input":"[{},{\"1\":{\"\\00:1,\"-1\":{\"1\":1E+2}},\"constru:tor\":\"\"},[]]","ok":false}, +{"input":"{\"a\":[{},[[true,-9223372036854775808,\"\\ud800\"],\"\\ud83d\\u83d\\ude00\"],false],\"\":{\"\\ud800\":2.2250738585072011e","ok":false}, +{"input":"{\"\\u00e9\":{\"10\":2.220738585072011e-308,\"01\":\"a\"}}","ok":true,"stringify":"{\"é\":{\"10\":2.220738585072011e-308,\"01\":\"a\"}}"}, +{"input":"{\"4294967294\"udc00\\ud800\"}","ok":false}, +{"input":"true
\"","ok":false}, +{"input":"\"\\udc000\"d\"","ok":false}, +{"input":"12345678901567890\r123é567890","ok":false}, +{"input":"{\"e+u030\\udc001\"[{\"1\":true,\"\\u0000\":\"\\ud800\"}]}","ok":false}, +{"input":"[[[]]]","ok":true,"stringify":"[[[]]]"}, +{"input":"\"\\\"\\/\"","ok":true,"stringify":"\"\\\"/\""}, +{"input":"{\"\":[{\"0\":{},\"429496\"4294967295\":\"
\"},[{\"1\":\"\\ud800\\ud800\", a\":\"\\ud800\\ud800\"},\"\\u\tc00\"]]}","ok":false}, +{"input":"1.51NaN.5","ok":false}, +{"input":"trueu\te","ok":false}, +{"input":"[{\"\\u0000\":\"e\\u0301\",\"e\\u0301\":{},\"\":}}\":{}},\"\\udc00\\ud800\",1e-400]","ok":false}, +{"input":"-11","ok":true,"stringify":"-11"}, +{"input":"-0\"__.roto__proto__\":","ok":false}, +{"input":"[\"\",\"
\"]\t","ok":true,"stringify":"[\"\",\"
\"]"}, +{"input":"{\"\\u00e9","ok":false}, +{"input":"[\"😀\",[[\"\\u00e9\",[]],[true],\"0000\"]]","ok":true,"stringify":"[\"😀\",[[\"é\",[]],[true],\"0000\"]]"}, +{"input":"{\"1\":[],\"\\u00e9\":[{},{\"a\":a\":{\"__proto__\":1e-400,\"constructor\":\"\\u0000\"}}},-1]}","ok":false}, +{"input":"\"]\\ud800\"","ok":true,"stringify":"\"]\\ud800\""}, +{"input":"[[true,[\"\\u000e9\",2.2250738585072011e[-308,1e400],[1.5]]]","ok":false}, +{"input":"[\"\\udc00\\ud800\",\"\",{\"\\u00e9\":\"\\ud83d\\ude00\",\"0\":[[\"\",\"\\/\"],\\udc00[\"\\/\",2.2250738585072011e-308,-1]],\"4294967294\":\"
\"}\t","ok":false}, +{"input":"{\"4294967294\":\"
\"}","ok":true,"stringify":"{\"4294967294\":\"
\"}"}, +{"input":"{\"e\\u0301\":[[{\"0\":false,\"4294967294\":\"\\ud800\"},{\"-1\":1,\"e\\u0301\":-0,\"coruct\for\":\"\\udc00\"}]],\"-1\":[{\"10\":[\"
\"]}],\"10\":{}}","ok":false}, +{"input":"1e\u000b-40","ok":false}, +{"input":"{\"e\\u0301\":[{\" \":1E+2,\"constructor","ok":false}, +{"input":"n\\udc00ulNaN","ok":false}, +{"input":"\"\\u009\"\t","ok":false}, +{"input":"\"\\ud83d\\ude00😀\"","ok":true,"stringify":"\"😀😀\""}, +{"input":"12345679012345678901233456NaN890","ok":false}, +{"input":"falsese\f","ok":false}, +{"input":"[1E+1E+2","ok":false}, +{"input":"{\"0\":[{\"4294967295\":[false],\"\\u00e9\":{},\"\":{\"a\":-1e400}},-1,{\"0\":\"\\/\",\"0\":[1e-400,1e400,-1e400]}],\"constructor\":[{\"\":{},\"\\u00e9\":{\"-1\":\"\\ud800\"},\"a\":\"e\\u0301\"},{\"1\":[false,\"\\ud800\\ud800\",1E+2],\"constructor\":\"\\ud800\\ud800\",\"\\u00e9\":{\"10\":\"a\",\"1\":\"\"}}],\"a\":\"\\ud83d\\ude00\"}","ok":true,"stringify":"{\"0\":[{\"4294967295\":[false],\"é\":{},\"\":{\"a\":null}},-1,{\"0\":[0,null,null]}],\"constructor\":[{\"\":{},\"é\":{\"-1\":\"\\ud800\"},\"a\":\"é\"},{\"1\":[false,\"\\ud800\\ud800\",100],\"constructor\":\"\\ud800\\ud800\",\"é\":{\"1\":\"\",\"10\":\"a\"}}],\"a\":\"😀\"}"}, +{"input":"\"\\ud8\\d\\ude00\"]\\u","ok":false}, +{"input":"\"\rud83d\\ude00-\"","ok":false}, +{"input":"\u000btru","ok":false}, +{"input":"{\"a\":{\"\\u0000\":[\"e\\u0301\",[123456789012345678901234567890]]},\"01\":{\"__proto__\":{\"\\u0000\":1e400},\"10\":-1,\"a\":[[true,123456789012345678901234567890,-1]]},\"\\u00e9\":{\"1\":-1,\"4294967295\":\"\\udc00\\ud800\"}}","ok":true,"stringify":"{\"a\":{\"\\u0000\":[\"é\",[1.2345678901234568e+29]]},\"01\":{\"10\":-1,\"__proto__\":{\"\\u0000\":null},\"a\":[[true,1.2345678901234568e+29,-1]]},\"é\":{\"1\":-1,\"4294967295\":\"\\udc00\\ud800\"}}"}, +{"input":"{\"a\":{\"__proto__\":1.5,\"01\":{\"1\":[],\"01\":{},\"a\":[\"\\ud83d\\ude00e\"]}},\"\\u00e9\":-922337203636854775808}","ok":true,"stringify":"{\"a\":{\"__proto__\":1.5,\"01\":{\"1\":[],\"01\":{},\"a\":[\"😀e\"]}},\"é\":-922337203636854700000}"}, +{"input":"{\"\\ud800\":,\"1\":2.2250738585072011e-308,\"\":{}}}","ok":false}, +{"input":"{\"10\":{\"4{\"4294967295\":null}}","ok":false}, +{"input":"1E+2","ok":false}, +{"input":"[[[1e400,[]],{\"\\u0000\":{\"1\":-0,\"1\":\"
\"}}],[[[],[2.225073858\\uDBFF\\uDFFF5072","ok":false}, +{"input":"nu","ok":false}, +{"input":"[[[{\"e\\u0301\":\"😀\",\"4294967294\":9007199254740993,\"4294967295\":\"\\ud800\\ud800\"},\"\\ud83d\\ude00\"],\"e\\u0301\"],[{\"constructor\":{\"e\\u0301\":1e-400},\"10\":[]},\"\\\\ud800d800\"]]","ok":true,"stringify":"[[[{\"4294967294\":9007199254740992,\"é\":\"😀\",\"4294967295\":\"\\ud800\\ud800\"},\"😀\"],\"é\"],[{\"10\":[],\"constructor\":{\"é\":0}},\"\\\\ud800d800\"]]"}, +{"input":"[-92233\\uDBFF\\uDFFF2036854775808","ok":false}, +{"input":"{\\u","ok":false}, +{"input":"[][][]","ok":false}, +{"input":"[\"\",{\"01\":1E+2,\"constructor\":{\"\\u00e9\":-1,\"\":\"a\"},\"10\":[\"😀\",{\"1\":2.2250738585072011e-308}]},{\"\":-9223372036854775808,\"4294967294\":-1,-1,\"\\ud800\":1.5}]","ok":false}, +{"input":"\u0000}}","ok":false}, +{"input":"[{\"01\":[2.225073","ok":false}, +{"input":"{\"1\":[[\"\\u00e9\",{\"4294967295\":1E+2},\"😀\"],{\"__proto__\":{}},\"\"],\"4294967294\":null}","ok":true,"stringify":"{\"1\":[[\"é\",{\"4294967295\":100},\"😀\"],{\"__proto__\":{}},\"\"],\"4294967294\":null}"}, +{"input":"{\"4294967295\":{\"0\":{\"e\\u0301\":true,\"e\\u0301\":\"\\ud83d\\ude00\"},\"424967295\":{\"\\u0000\":-9223372036854775808,\"a\":[0,\"\\u0[0,\"\\u00e9e400],\"__proto__\":\"e\\u0301\"},\"10\":0}}","ok":false}, +{"input":"+","ok":false}, +{"input":"[1E+2,{\"0\":[123456789012345678901234","ok":false}, +{"input":"[[{\"1\":{},\"01\":{\\ud800\"\\ud800\":1E+2,\"10\":\"\\udc00\"","ok":false}, +{"input":"[{\"01\":{\"constructor\":{\"429497295\":0},\"0\":1E+2,\"-1\":\"a\"},\"1\":{\"__proto__\":[]}},null,-1]","ok":true,"stringify":"[{\"1\":{\"__proto__\":[]},\"01\":{\"0\":100,\"constructor\":{\"429497295\":0},\"-1\":\"a\"}},null,-1]"}, +{"input":"\\u00\"\\ud83d\\ude00","ok":false}, +{"input":"{\"10\":{\"\\ud800\":{}},\"\":{}}","ok":true,"stringify":"{\"10\":{\"\\ud800\":{}},\"\":{}}"}, +{"input":"\"e\\u0","ok":false}, +{"input":"\"a\":1,\"a\\u\":}u00e9\"","ok":false}, +{"input":"\"\\udc00\\u0000\"","ok":true,"stringify":"\"\\udc00\\u0000\""}, +{"input":"\"e","ok":false}, +{"input":"[1]","ok":true,"stringify":"[1]"}, +{"input":"{\"42\u001f94967295\":1.5,\"constructor\":{},\",\"01\":{\"\":\"\",\"\"","ok":false}, +{"input":"11","ok":true,"stringify":"11"}, +{"input":"{\"\":{\"a\":2.2250738585072011e-308,\"\":-0,\"constructor\":{\"\":{\"10\":\"\",\"-1\":false},\"4294967295\":[\"e\\u0301\"]}},\"a\":1E+2,\"__proto__\":\"\\ud83d\\ude00\"}","ok":true,"stringify":"{\"\":{\"a\":2.225073858507201e-308,\"\":0,\"constructor\":{\"\":{\"10\":\"\",\"-1\":false},\"4294967295\":[\"é\"]}},\"a\":100,\"__proto__\":\"😀\"}"}, +{"input":"{\"10\":{\"10\":null,\"0true\":2.\":2.22507385250738585072011e-308},\"0\":\"\\ud83d\\ude00\"}","ok":false}, +{"input":"{\"\":[],\"1[],\"1\":[{\"__proto__\":{\"__proto__\":\"\\ud800\\ud800\",\"1\":1E+2,\"4294967295\":\"\\u00e9\"},\"01\":{\"10\":1,\"4294967294\":\"\\u00e9\"}}],\"4294967294\":{\"4294967294\":{\"constructor\":\"a\",\"-1\":\"\\ud800\\ud800\"},\"\\ud800\":\"a\",\"a\":\"\\udc00\\ud800\"}}","ok":false}, +{"input":"\"\\NaNud800\\00\"","ok":false}, +{"input":"{\"0\":[],\"01\":[{\"e\\u0301\":[-0,\"\\/\"],\"4294967295\":\"\\ud83d\\ude00\"},{\"4294967295\":\"a\"},true],\"1\":{}}","ok":true,"stringify":"{\"0\":[],\"1\":{},\"01\":[{\"é\":[0,\"/\"],\"4294967295\":\"😀\"},{\"4294967295\":\"a\"},true]}"}, +{"input":"[-0,,{\\x]","ok":false}, +{"input":"Infinity","ok":false}, +{"input":"{\"4294967295\":{\"e\\u0301\":[],\"a\":1😀.5,\"__proto__\":1e-400}}","ok":false}, +{"input":"e}","ok":false}, +{"input":"[\"\\u0000\",[{\"\":[],\"1\":[]},[{\"\\u00e9\":\"\\ud800\",\"e\\u0301\":true,\"\\ud800\":9007199254740993},1E+2,\"\"]]]","ok":true,"stringify":"[\"\\u0000\",[{\"1\":[],\"\":[]},[{\"é\":\"\\ud800\",\"é\":true,\"\\ud800\":9007199254740992},100,\"\"]]]"}, +{"input":"-\\uDB\\uDBFF\\\u001fDFFFe400","ok":false}, +{"input":"[null,[{},{},\"😀\"],{\"10\":[[\"e\\u0301\",\"\",1.5],-9223372036854775808],\"4294967295\":[{\"\\u00e9\":\"😀\"}],\"0\":{}\\u]","ok":false}, +{"input":"
[\"e\\u0301\"é","ok":false}, +{"input":"[]ull","ok":false}, +{"input":"{\"\\u0000\":\"
\",\"\\u0000\":{\"0\":\"a\"},\"\\u00e9\":[[]]}","ok":true,"stringify":"{\"\\u0000\":{\"0\":\"a\"},\"é\":[[]]}"}, +{"input":"[2.2:507385850","ok":false}, +{"input":"[[10E+","ok":false}, +{"input":"{\"a\":[[2.2250738585072011e-308,[],null],{\"__proto__\":[1E+2],\"\\u00e9\":{\"\\u0000\":-9223372036854775808,\"\\u00e9\":123456789012345678901234567890}}],\"10\":1e-400}","ok":true,"stringify":"{\"10\":0,\"a\":[[2.225073858507201e-308,[],null],{\"__proto__\":[100],\"é\":{\"\\u0000\":-9223372036854776000,\"é\":1.2345678901234568e+29}}]}"}, +{"input":"{\"__proto__\":\"\\udc0 \",\"a\":{\"","ok":false}, +{"input":"{\"1\"","ok":false}, +{"input":"[[[],2.2250738585072011e-308]]","ok":true,"stringify":"[[[],2.225073858507201e-308]]"}, +{"input":"{}{}\u000b","ok":false}, +{"input":"{\"\\{0NaN0e9{\":\"😀\",\"-1\":\"\\udc00\"}","ok":false}, +{"input":"{\"-1\":[{\"10\":{\"\\ud800e\\u0301\":\"\\udc00\",\"\":\"\\ud800\\ud800\",\"__proto__\":\"\\/\"},\"4294967295\":[-1,123456789012345678901234567890],\"constructor\":{\"\\u00e9\":1e-400,\"\":1,\"e\\u0301\":-0}","ok":false}, +{"input":"{\"10\":{\"a\":[\"\"],\"10\":0,\"__proto__\":\"\\udc00\"}}","ok":true,"stringify":"{\"10\":{\"10\":0,\"a\":[\"\"],\"__proto__\":\"\\udc00\"}}"}, +{"input":"{\":1,\"01\":\"\\ud83d\\ude00\",\"constructor\":-1e400}","ok":false}, +{"input":"
\"","ok":false}, +{"input":"[{\"\\u0000\":[null]},{\"constructor\":{\"0\":[\"
\",0],\"1\":[\"e\\u0301\",\"\"]}}]","ok":true,"stringify":"[{\"\\u0000\":[null]},{\"constructor\":{\"0\":[\"
\",0],\"1\":[\"é\",\"\"]}}]"}, +{"input":"{\"\\ud800\":\"\\u0000\",\"__proto__\":1e400}","ok":true,"stringify":"{\"\\ud800\":\"\\u0000\",\"__proto__\":null}"}, +{"input":"{\"\\u0000\":\"a\",\"4294967295\":{\"01\\udc00:{}}}","ok":false}, +{"input":"falsese","ok":false}, +{"input":"[1e-400,{\"1\":{\"a\":\"\\udc00\\ud800\",\"10\":{\"constructor\":\"
\",\"__proto__\":\"\\udc00\\ud800\"}\"e\\u0301\":{\"0\":\"\\ud800\\ud800\"}}]","ok":false}, +{"input":"{\"__prot","ok":false}, +{"input":"\"e\\u0101","ok":false}, +{"input":"[{\"constructor\":true,\"4294967295\":{\"\":[\"😀\",","ok":false}, +{"input":"\"\\ud8000\\ud800\"","ok":true,"stringify":"\"\\ud8000\\ud800\""}, +{"input":"[[{\"0\":{\"\\u0000\":\"\\ud800\\ud800\"},\"a\":[\"\\udc00\\ud800\",\"a\"]}],\"\\ud800\\ud 800\"]","ok":false}, +{"input":"[1e-400]00]]","ok":false}, +{"input":"[\"\\u00e9\",\"\\udc00\\ud800\"]n","ok":false}, +{"input":"{\"1\":{]0\":[],\"\":[90071992547409}}","ok":false}, +{"input":"{\"\\ud800\":[[E{\"e\\u0 301\":1E+2,\"4294967295\":-0}],{},\"\\udc00\\ud800\"],\"constructor\":{\"__proto__\": {\"\\u0000\":[]}},\"\":{\"4294967294\":\"e\\u0301\",\"__proto__\":{}}}","ok":false}, +{"input":"\"\\u00","ok":false}, +{"input":"{\"1\":[2.2250738585072011e-308]}","ok":true,"stringify":"{\"1\":[2.225073858507201e-308]}"}, +{"input":"{\"01\":{\":{\"__proto__\":},\"\\ud800\":\"\\ud800\",\"4294967294\":[[\"\\ud800\"]]}","ok":false}, +{"input":"{\"constructor\":{\"\\ud800\":\"\\ud83d\\ude00\",\"\",\"\":[{\"10:-0}]},\"\\ud800\":{Infinity\\u0000\":[]},\"\\u0000\":[\"\",{},\"\"]}","ok":false}, +{"input":"t
u
","ok":false}, +{"input":"{\"\\u0000\":\"\\/\",\"4294967295\":\"e\\u0301\",\"constructor\":{\"4294967294\":7294\":{}}}","ok":false}, +{"input":"[{\"-1\":null,\"10\":{\"-1\":[\"\\udc00\\ud800\",\"\\ud800\\ud800\"],\"-1\":-0,\"e\\u0301\":{\"a\":false,\"01\":1E+2}},\"constructor\":[{\"e\\u0301\":-9223372036854775808},\"\\ud83d\\ude00\"]},[2.2250738585072011e-3e-308,1234567890é12345678901234567890]]","ok":false}, +{"input":"[[[],{\"\\u00e9\":{\"a\":-0,\"10\":\"\",\"10\":\"a\"},\"4294967295\":[],\"0\":[\"a\",1e-400]},1E+2],[]]","ok":true,"stringify":"[[[],{\"0\":[\"a\",0],\"é\":{\"10\":\"a\",\"a\":0},\"4294967295\":[]},100],[]]"}, +{"input":"[\"😀\",{\"\":{\"\":{\"-1\":{\"0\":null,\"e\\u0301\":\"a\"}},\"0\":[\"\\u00e9\"],\"__proto__\"","ok":false}, +{"input":"[{\"1\":[{\"\\u0000\":123456789012345678901234567890}],\"4294967294\":[{\"e\\u0301\":1e400,\"constructor\":\"\\ud800\\ud800\"},{\"1\":\"\\ud800\\ud800\",\"4294967294\":\"\\ud800\",\"constructor\":-1},[\"\\u00e9\"]],\"\":{\"4294967295\":\"😀\",\"-1\":\"a\",\"-1\":\"\\ud83d\\00\"}},[[\"😀\",[],1e400],{\"-1\":{\"\\ud800\":-1e400,\"4294967294\":\"a\",\"constructor\":1e-400}}]]","ok":false}, +{"input":"[{\"constructor\"","ok":false}, +{"input":"\u000b","ok":false}, +{"input":"{\"4294967294\":\"\\ud800\",\"42949672954967295\":[1,-1e400],\"\\u00e9\":{\"\\u0000\":123456789012345678901234567890,\"\\ud800\":\"\\ud800\",\"\\u0000\":]{\"0\":{\"1\":\"a\",\"construc}tor\":-1},\"1\":{\"1\":\"e\\u0301\"}}}}","ok":false}, +{"input":"{\"00e9","ok":false}, +{"input":"t","ok":false}, +{"input":"{\"\\u0000\":[{\"e\\u0301\":[\"\",\"\\ud\",\"\\ud83d\\ude0\"],\"constructor\":1.5}]}","ok":false}, +{"input":"40","ok":true,"stringify":"40"}, +{"input":"[{\"4294967294\":\"a\"},[[[],\"\\u0000\",\"\\udc00\"]]]","ok":true,"stringify":"[{\"4294967294\":\"a\"},[[[],\"\\u0000\",\"\\udc00\"]]]"}, +{"input":"{\"\\ud800\":[{},{\"\\u00e9\":{\"0\":false,false,\"e\\u0301\":-9223372036854775808},\"\\ud800\":\"\\udc00\\ud800\",\"a\":[]},\"\\udc00\\ud8000\\ud800\"],\"c\fonstructor\":[{\"4294967294\":{}},[\"e\\u0301\",1],\"\\udc00\\ud800\"]}","ok":false}, +{"input":"[\"\\\\udc00\"]","ok":true,"stringify":"[\"\\\\udc00\"]"}, +{"input":"{\"01\":[[[0]],\"\\udc00dc00\"],\"1\":\"\"}","ok":true,"stringify":"{\"1\":\"\",\"01\":[[[0]],\"\\udc00dc00\"]}"}, +{"input":"{\"e\\u030true1\":\"\\ud800\\[dé00\"}","ok":false}, +{"input":"{\"0E00\"0\"00:1E+2}","ok":false}, +{"input":"{\"10\":1e-400,\"0\":[1e-400,\t{},\",\"4294967295\":\"\"}","ok":false}, +{"input":"{ ","ok":false}, +{"input":"{9","ok":false}, +{"input":"[[-9223372036854775808,123456789012345678901234567\"a\":1,\"a\":890,[2.2250738585072011e-308,{},[]]],\"\\udc00\\ud800\",{\"constructor\":[{\"1\":0},1],\"e\\u0301\":\"a\"}]","ok":false}, +{"input":"nullrue","ok":false}, +{"input":"[\"\\ud800\\ud800\",\"\\udc00\",{\"0\":true}]","ok":true,"stringify":"[\"\\ud800\\ud800\",\"\\udc00\",{\"0\":true}]"}, +{"input":"[\\\\\"","ok":false}, +{"input":"\"0\"","ok":true,"stringify":"\"0\""}, +{"input":"{\"__proto__\":{\"e\\u0301\":[],\"4294967295\":\"😀\"},\"e\\u0301\":-9223372036854775808,\"__proto__\":1e400}","ok":true,"stringify":"{\"__proto__\":null,\"é\":-9223372036854776000}"}, +{"input":"[1.50,[\"a\"[[]]]","ok":false}, +{"input":"\u000bnull","ok":false}, +{"input":"[null,[[{\"-1\":\"\\/\",\"0\":2.2250738585072011e-308,\"4294967295\":-922003372036854775808},{\"4294967295\":\"e\\u0301\",\"4294967295\":null}],9007199254740993,[-1e400]],1e400]","ok":true,"stringify":"[null,[[{\"0\":2.225073858507201e-308,\"-1\":\"/\",\"4294967295\":-922003372036854800000},{\"4294967295\":null}],9007199254740992,[null]],null]"}, +{"input":"[{\"\\ud800\":\"\"}]","ok":true,"stringify":"[{\"\\ud800\":\"\"}]"}, +{"input":"\t1NaN\t400","ok":false}, +{"input":"\"\\u\\ud800u\\u","ok":false}, +{"input":"\"Infinityudc00","ok":false}, +{"input":"{\"a\":{\"a\":[{},{\"01\":1},{\"\\u00e9\":\"
\",\"\\u00e9\":\"\\ud83d\\ude00\"}],\"a\":[{\"constructor\":\"\\udc00\",\"1\":1e-400,\"a\":\"\\ud800\\ud800\"}],\"__proto__\":{}},\"\":[[{\"\\u0000\":\"
\",\"a\":1.5},{\"\":\"\\ud800\"}],{\"4294967294\":[true],\"4294967294\":{\"__proto__\":\"\"},\"4294967295\":-9223372036854775808},[\"
\",{\"\\u00e9\":\"\\u0000\"},1E+2]]}","ok":true,"stringify":"{\"a\":{\"a\":[{\"1\":0,\"constructor\":\"\\udc00\",\"a\":\"\\ud800\\ud800\"}],\"__proto__\":{}},\"\":[[{\"\\u0000\":\"
\",\"a\":1.5},{\"\":\"\\ud800\"}],{\"4294967294\":{\"__proto__\":\"\"},\"4294967295\":-9223372036854776000},[\"
\",{\"é\":\"\\u0000\"},100]]}"}, +{"input":"[-9292","ok":false}, +{"input":"\"\\/","ok":false}, +{"input":"[[{\"constructor\":0},1ee400,:[]]]","ok":false}, +{"input":"{\"\\ud800\":{}}","ok":true,"stringify":"{\"\\ud800\":{}}"}, +{"input":"[{\"0\":{}}[],{\"__proto__\":-1e400,\"\\ud800\":1e400}]","ok":false}, +{"input":"[N\"__proto__\":a]","ok":false}, +{"input":"{\"\\u00e9\":\"\\udc00\",\"__proto__\":{\"e\\u0301\":[],\"\\ud800\":[{},9007199254740993,123456789012345678901234567890],\"10\":[\"e\\u0301\",{\"e\\u0301\":1e400,\"__proto__\":null,\"constructor\":\"\"},-0]}}","ok":true,"stringify":"{\"é\":\"\\udc00\",\"__proto__\":{\"10\":[\"é\",{\"é\":null,\"__proto__\":null,\"constructor\":\"\"},0],\"é\":[],\"\\ud800\":[{},9007199254740992,1.2345678901234568e+29]}}"}, +{"input":"[{\"4294967295\":\"a\",\"\\u00e","ok":false}, +{"input":"[{\"\\u0000\":{\"__proto__\":1e400,\"__proto__\":{},\"0\":1.5},\"10\":null,l,\"42949672\":\"😀\"}
]","ok":false}, +{"input":"{\"\\u00e9\":\"\\ud800\"}","ok":true,"stringify":"{\"é\":\"\\ud800\"}"}, +{"input":"-e9999999991","ok":false}, +{"input":"[[[-1],ud800\\ud800\",[{}]],[],{\"10\":0,\"1\":{\"10\":null,\"__proto__\":[9007199254740993,0],\"01\":{\"1\":1.5,\"constructor\":null,\"constructor\":\"ctor\":\"\"}}}]","ok":false}, +{"input":"\\x","ok":false}, +{"input":"1234567\n4567\n89012345678901234567890","ok":false}, +{"input":"-]","ok":false}, +{"input":"{\"\\ud800\":[]}","ok":true,"stringify":"{\"\\ud800\":[]}"}, +{"input":"{\"a\":{}}","ok":true,"stringify":"{\"a\":{}}"}, +{"input":"[]\\uDBFF\\uDFFF","ok":false}, +{"input":"[{}].","ok":false}, +{"input":"[null,{:,se],false]","ok":false}, +{"input":"[{}],{}]","ok":false}, +{"input":"[\"\\udc00\\ud800\",{\"constructor\"::\"e\\u0301\",\"a\":false}\f","ok":false}, +{"input":"{\"constructor\":{},\"\":[1e400,[\"\",9007199254740993,{\"__proto__\":9007199254740993,\"10\":false,\"\\ud800\":-1}]],\"__proto__\":\"😀\"}","ok":true,"stringify":"{\"constructor\":{},\"\":[null,[\"\",9007199254740992,{\"10\":false,\"__proto__\":9007199254740992,\"\\ud800\":-1}]],\"__proto__\":\"😀\"}"}, +{"input":"{\"4294967295\":\"😀\",\"__proto__\":{\"4294967294\":[{\"01\":\"\\u0000\",\"constructor\":2.2250738585072011e-308\\uDBFF\\uDFFF,\"e\\u0301\":\"\\ud83d\\ude00\"},{\"10\"},{\"10\":1,\"1\":0,:\"1\":\"a\"},\"\"]},\"\\ud800\":\"a\"}","ok":false}, +{"input":"{\"__proto__\":[{\"1\":{\"4294967294\":-1e400},\"a\":\"\\udc00\\ud800\"},{\"0\":\"\\ud800\",\"4294964967295\":\"\\ud800\"},1],\"__proto__\":-9223372036854775808,\"\\u00e9\":{\"\\u0000\":{\"10\":{},\"01\":[]},\"__proto__\":90071992547[40993,\"1\":[]}}","ok":false}, +{"input":"{\"0\":false,\"e\\u0301\":[\"\\u\u001f00e9\"],\"10\":-1}","ok":false}, +{"input":"[[[{}]],[\"a\"],[\"\\ud800\"]]","ok":true,"stringify":"[[[{}]],[\"a\"],[\"\\ud800\"]]"}, +{"input":"{\"\\u0000\":[-92233720368E4775808,1e400],\"\\ud800\":{\"1\":{\"__proto__\":{\"10\":\"\\u0000\"}},\"\\u0000\":[]}}","ok":true,"stringify":"{\"\\u0000\":[null,null],\"\\ud800\":{\"1\":{\"__proto__\":{\"10\":\"\\u0000\"}},\"\\u0000\":[]}}"}, +{"input":"[]\t\t","ok":true,"stringify":"[]"}, +{"input":"NaN\r\\ud800\"","ok":false}, +{"input":"1e-0","ok":true,"stringify":"1"}, +{"input":"{\"1\":{\"constructor\":{}},\"\\{}},\"\\ud800\"\\udc00\"]}","ok":false}, +{"input":"[{\"a\":{\"a\":{\"\\u00e9\":-9223372036854775808},\"\\u0000\":-0,\"e\\u0301\":-1e400},\"0\":2.2250738585072011e-308},[],[[[\"\",\"\\u00e9\",\"
\"],\"\\/\"]]]","ok":true,"stringify":"[{\"0\":2.225073858507201e-308,\"a\":{\"a\":{\"é\":-9223372036854776000},\"\\u0000\":0,\"é\":null}},[],[[[\"\",\"é\",\"
\"],\"/\"]]]"}, +{"input":"}\\/","ok":false}, +{"input":"e99999999[","ok":false}, +{"input":"[{},9007199254740993]","ok":true,"stringify":"[{},9007199254740992]"}, +{"input":"2.225073825","ok":true,"stringify":"2.225073825"}, +{"input":"-\n{}","ok":false}, +{"input":"[{\"ud800\":[[,[\"😀\"],[]],\"0\":\"\\ud83d\\ude00\",\"10\":{}},{},{}]","ok":false}, +{"input":"[[],[{},[{\"\\u0000\":\"\\ud800\\ud800\",\"\\u0000\":\"\\u0000\",\"1\":\"\\ud800\"},\"\\u0\"__proto__\":000,\"\"]\"e\\u0301\"]]","ok":false}, +{"input":"[]\t[]\t]\t","ok":false}, +{"input":"123678901234567","ok":true,"stringify":"123678901234567"}, +{"input":"[-","ok":false}, +{"input":"[[90071992[474\n993,[{\"1\":\"\\u00e9\"\\uDBFF\\uDFFF]]]","ok":false}, +{"input":"[{\"e\\u0301\":-1,\"4294967294\":\"\\eud83d\\ude00\",\"constructor\":[[],{\"a\":1},{\"\\ud800\":-1e400,\"\":\"
\",\"\\u00e9\":1}]},1.5,[\"\\u00e9\",[{\"constructor\":true},\"01\":null,\"constructor\":\"\\ud83d\\ude00\"},[9007199254740993,123456789012345678901234567890,1.5],[9007199254740993,-1e400,false]],[\"\\udc00\\ud800\"]]]","ok":false}, +{"input":"[\"😀\",{\"__proto__\":[],\"a\":{\"e\\u0301\":[\"\",-9223372036854775808,\"\\u0000\"],\"\\u00e9\":{\"-1\":\"\",\"\":1E+2,\"\\u0000\":1.5}}}]","ok":true,"stringify":"[\"😀\",{\"__proto__\":[],\"a\":{\"é\":[\"\",-9223372036854776000,\"\\u0000\"],\"é\":{\"-1\":\"\",\"\":100,\"\\u0000\":1.5}}}]"} +]} diff --git a/packages/zero-conformance/vectors/harden-queries.json b/packages/zero-conformance/vectors/harden-queries.json new file mode 100644 index 0000000000..21712840c7 --- /dev/null +++ b/packages/zero-conformance/vectors/harden-queries.json @@ -0,0 +1,43 @@ +{"formatVersion":1,"kind":"client-scenarios","description":"Hardening client scenarios: re-entrant listeners, odd TTLs, query change throttling, pokes that leave views unchanged. See src/client/harness.ts.","shared":{"startTime":1700000000000},"cases":[ +{"name":"listener: removing a later listener during a notification skips it","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"unlisten","listener":"L2"}]},{"op":"listen","view":"v","listener":"L2"},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[]}}}]}, +{"name":"listener: removing an earlier listener during a notification","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1"},{"op":"listen","view":"v","listener":"L2","actions":[{"op":"unlisten","listener":"L1"}]},{"op":"listen","view":"v","listener":"L3"},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]},"L3":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]},"L3":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]},"L3":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L3":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]},"L3":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]},"L3":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L3":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}}]}, +{"name":"listener: adding a listener during a notification","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"listen","view":"v","listener":"L2"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"},{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}}]}, +{"name":"listener: a listener that destroys its own view","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"destroy","view":"v"}]},{"op":"listen","view":"v","listener":"L2"},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}}]}, +{"name":"listener: a mutation from a listener, with a later listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"mutate","mutation":"m2","mutator":"issue.create","args":{"id":"i2"}}]},{"op":"listen","view":"v","listener":"L2"},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[],"resultType":"complete"}]}}},{"sent":[["push",{"timestamp":1700000000010,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1"}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000000010,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":2,"clientID":"$clientID","name":"issue.create","args":[{"id":"i2"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"},{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"},{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}}]}, +{"name":"listener: a mutation from the listener of one view, seen by another","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"materialize","view":"w","query":"issues.all"},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"mutate","mutation":"m2","mutator":"issue.create","args":{"id":"i2"}}]},{"op":"listen","view":"w","listener":"L2"},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[],"resultType":"complete"}]}}},{"sent":[["push",{"timestamp":1700000000010,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1"}]}],"pushVersion":1,"requestID":"$requestID"}],["push",{"timestamp":1700000000010,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":2,"clientID":"$clientID","name":"issue.create","args":[{"id":"i2"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2},"w":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"},{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"},{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"},"m2":{"client":{"type":"success"},"server":"pending"}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}}]}, +{"name":"listener: a poke while a listener mutates","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i9"}}]},{"op":"listen","view":"v","listener":"L2"},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"sent":[["push",{"timestamp":1700000000010,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i9"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i9","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"},{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i9","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"},{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i9","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i9","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}}]}, +{"name":"listener: materializing the same query from a listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"materialize","view":"w","query":"issues.all"},{"op":"listen","view":"w","listener":"L2"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},{"id":"i2","title":"I2","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}}]}, +{"name":"listener: materializing another query from a listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"materialize","view":"w","query":"issues.byID","args":"i1"},{"op":"listen","view":"w","listener":"L2"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[{"op":"put","hash":"083p68r0nv1nqz"}],"rowsPatch":[{"op":"del","tableName":"issues","id":{"id":"i1"}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1},"w":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"unknown","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"unknown"}]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"083p68r0nv1nqz","name":"issues.byID","args":["i1"],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1},"w":{"data":null,"resultType":"complete","notifications":2}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]},"L2":{"calls":[{"data":null,"resultType":"unknown"},{"data":null,"resultType":"complete"}]}}}]}, +{"name":"listener: destroying another view of the same commit","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"a","query":"issues.all"},{"op":"materialize","view":"b","query":"issues.open"},{"op":"advance","ms":10},{"op":"listen","view":"a","listener":"La","actions":[{"op":"destroy","view":"b"}]},{"op":"listen","view":"b","listener":"Lb"},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"},{"op":"put","hash":"1i6n3ig0hcl1v4"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000},{"op":"put","hash":"1i6n3ig0hcl1v4","name":"issues.open","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[{"data":[],"resultType":"unknown"}]}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[{"data":[],"resultType":"unknown"}]}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[]}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[]}}},{"state":{"name":"connected"},"views":{"a":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}},"listeners":{"La":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown"},{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"Lb":{"calls":[]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1i6n3ig0hcl1v4"}]}]],"state":{"name":"connected"},"views":{"a":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[]}}}]}, +{"name":"listener: destroying an earlier view of the same commit","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"a","query":"issues.all"},{"op":"materialize","view":"b","query":"issues.open"},{"op":"advance","ms":10},{"op":"listen","view":"a","listener":"La"},{"op":"listen","view":"b","listener":"Lb","actions":[{"op":"destroy","view":"a"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"},{"op":"put","hash":"1i6n3ig0hcl1v4"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000},{"op":"put","hash":"1i6n3ig0hcl1v4","name":"issues.open","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[{"data":[],"resultType":"unknown"}]}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[{"data":[],"resultType":"unknown"}]}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[]}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[]}}},{"state":{"name":"connected"},"views":{"b":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":2}},"listeners":{"La":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown"}]},"Lb":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"unknown"},{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete"}]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"views":{"b":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":0}},"listeners":{"La":{"calls":[]},"Lb":{"calls":[]}}}]}, +{"name":"listener: updateTTL and run from a listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"updateTTL","view":"v","ttl":"10m"},{"op":"run","run":"r","query":"issues.all"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":600000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}},"listeners":{"L1":{"calls":[]}}}]}, +{"name":"listener: preload and cleanup from a listener","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"preload","preload":"p","query":"users.all"},{"op":"cleanup","preload":"p"},{"op":"cleanup","preload":"p"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"preloads":{"p":{"cached":"pending","complete":"pending"}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":0},{"op":"del","hash":"1864qec0oaacid"}]}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}},"listeners":{"L1":{"calls":[]}}}]}, +{"name":"view: a poke that rewrites a row with the same values","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"view: a poke that removes a row and puts it back","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"del","tableName":"issues","id":{"id":"i1"}},{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","gotQueriesPatch":[],"rowsPatch":[{"op":"del","tableName":"issues","id":{"id":"i1"}},{"op":"put","tableName":"issues","value":{"id":"i1","title":"changed","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"changed","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}}}]}, +{"name":"view: a poke that changes rows the view does not show","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.byID","args":"i1"},{"op":"materialize","view":"w","query":"issues.open"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"083p68r0nv1nqz"},{"op":"put","hash":"1i6n3ig0hcl1v4"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i2","title":"I2","description":"","closed":true,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"users","value":{"id":"u9","name":"x"}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"083p68r0nv1nqz","name":"issues.byID","args":["i1"],"ttl":300000},{"op":"put","hash":"1i6n3ig0hcl1v4","name":"issues.open","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":2},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":2}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"comments":[]}],"resultType":"complete","notifications":0}}}]}, +{"name":"view: the poke that confirms an optimistic write with the same row","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1","title":"I1"}},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","lastMutationIDChanges":{"$clientID":1},"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"sent":[["push",{"timestamp":1700000000010,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1","title":"I1"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}}}}]}, +{"name":"lifecycle: materialize and destroy before the store is loaded","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"destroy","view":"v"},{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"}},{"state":{"name":"connecting"}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000},{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"}}]}, +{"name":"lifecycle: a listener added before the store is loaded destroys the view on hydration","steps":[{"op":"materialize","view":"v","query":"issues.all"},{"op":"materialize","view":"w","query":"issues.all"},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"destroy","view":"w"}]},{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"unknown"}]}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"listeners":{"L1":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":2}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown"},{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]}}}]}, +{"name":"lifecycle: a listener destroys the other view of its query","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"materialize","view":"w","query":"issues.all"},{"op":"listen","view":"v","listener":"L1","actions":[{"op":"destroy","view":"w"}]},{"op":"listen","view":"w","listener":"L2"},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10},{"op":"destroy","view":"v"},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[{"data":[],"resultType":"complete"}]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"listeners":{"L1":{"calls":[{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete"}]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"state":{"name":"connected"},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"listeners":{"L1":{"calls":[]},"L2":{"calls":[]}}}]}, +{"name":"lifecycle: one() over a row deleted and put back in one poke","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.byID","args":"i1"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"083p68r0nv1nqz"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"del","tableName":"issues","id":{"id":"i1"}},{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":"u1","createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":"u2","createdAt":1}},{"op":"del","tableName":"issues","id":{"id":"i1"}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"083p68r0nv1nqz","name":"issues.byID","args":["i1"],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":2}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1},"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":"u1","createdAt":1},"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":"u1","createdAt":1},"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":{"id":"i1","title":"I1","description":"","closed":false,"ownerId":"u1","createdAt":1},"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":null,"resultType":"complete","notifications":1}}}]}, +{"name":"lifecycle: materialize, run, and preload after close","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"close"},{"op":"materialize","view":"w","query":"issues.all"},{"op":"run","run":"r","query":"issues.all"},{"op":"destroy","view":"w"},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}}},{"closed":[0],"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":1}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"cached","notifications":0}},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},{"state":{"name":"closed","reason":"Zero was explicitly closed by calling zero.close()"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}}]}, +{"name":"lifecycle: a preload and a view of one query with different TTLs","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"preload","preload":"p","query":"issues.all","ttl":"1m"},{"op":"advance","ms":10},{"op":"materialize","view":"v","query":"issues.all","ttl":"30s"},{"op":"advance","ms":10},{"op":"cleanup","preload":"p"},{"op":"advance","ms":10},{"op":"materialize","view":"w","query":"issues.all","ttl":"2m"},{"op":"destroy","view":"v"},{"op":"destroy","view":"w"},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":60000}]}]],"state":{"name":"connected"},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"w":{"data":[],"resultType":"unknown","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":120000},{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"preloads":{"p":{"cached":"pending","complete":"pending"}}}]}, +{"name":"lifecycle: a transformError after the query is complete","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"receive","message":["transformError",[{"error":"app","id":"1y7zr0i1xir4ek","name":"issues.all","message":"late"}]]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"run","run":"r","query":"issues.all","type":"complete"}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}}]}, +{"name":"poke: materialize between pokeStart and pokeEnd","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"materialize","view":"w","query":"issues.all"},{"op":"destroy","view":"v"},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"w":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connected"},"views":{"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0}}}]}, +{"name":"poke: a got query deleted while views are complete and cached","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"materialize","view":"w","query":"users.all"},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1864qec0oaacid"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"socketClose","code":1006},{"op":"materialize","view":"x","query":"users.all"},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[{"op":"del","hash":"1864qec0oaacid"}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":1}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie","01"],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000},{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":300000}],"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0},"x":{"data":[],"resultType":"cached","notifications":1}}},{"state":{"name":"connecting"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0},"x":{"data":[],"resultType":"cached","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0},"x":{"data":[],"resultType":"cached","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0},"x":{"data":[],"resultType":"cached","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0},"x":{"data":[],"resultType":"cached","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0},"x":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"complete","notifications":0},"x":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"run: complete pending over a dropped connection","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"run","run":"r","query":"issues.all","type":"complete"},{"op":"run","run":"c","query":"issues.all","type":"cached"},{"op":"advance","ms":10},{"op":"socketClose","code":1006},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"runs":{"r":"pending"}},{"state":{"name":"connected"},"runs":{"r":"pending","c":"pending"}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"runs":{"r":"pending","c":"pending"}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"runs":{"r":"pending","c":"pending"}},{"state":{"name":"connecting"},"runs":{"r":"pending","c":"pending"}},{"state":{"name":"connected"},"runs":{"r":"pending","c":"pending"}},{"state":{"name":"connected"},"runs":{"r":"pending","c":"pending"}},{"state":{"name":"connected"},"runs":{"r":"pending","c":"pending"}},{"state":{"name":"connected"},"runs":{"r":"pending","c":"pending"}},{"state":{"name":"connected"},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]},"c":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"runs":{"r":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]},"c":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}]}}}]}, +{"name":"throttle: the batch timer fires while disconnected","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"a","query":"issues.all"},{"op":"socketClose","code":1006},{"op":"advance","ms":10},{"op":"materialize","view":"b","query":"users.all"},{"op":"advance","ms":10},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":1}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connecting"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connecting"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connecting"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"lifecycle: a removal deferred by a pending mutation over a reconnect","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"destroy","view":"v"},{"op":"advance","ms":10},{"op":"socketClose","code":1006},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","lastMutationIDChanges":{"$clientID":1}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"sent":[["push",{"timestamp":1700000000010,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie","00"],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}],"activeClients":["$clientID"]}]}}],"closed":[0],"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connecting"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"sent":[["push",{"timestamp":1700000005020,"clientGroupID":"$clientGroupID","mutations":[{"type":"custom","timestamp":1700000000010,"id":1,"clientID":"$clientID","name":"issue.create","args":[{"id":"i1"}]}],"pushVersion":1,"requestID":"$requestID"}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":"pending"}}},{"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"mutations":{"m1":{"client":{"type":"success"},"server":{"type":"success"}}}}]}, +{"name":"error: a query that fails to hydrate throws from materialize and run","steps":[{"op":"preload","preload":"p","query":"issues.withFirstComment"},{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"materialize","view":"w","query":"issues.withFirstComment"},{"op":"run","run":"r","query":"issues.withFirstComment"},{"op":"run","run":"c","query":"issues.withFirstComment","type":"complete"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[{"op":"put","hash":"1w6spz31r53810","name":"issues.withFirstComment","args":[],"ttl":0}],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connecting"},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1w6spz31r53810","name":"issues.withFirstComment","args":[],"ttl":0},{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"threw":"Partitioned take does not allow unpartitioned fetches","state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"runs":{"r":{"rejected":{}}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"runs":{"r":{"rejected":{}},"c":{"rejected":{}}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1w6spz31r53810","name":"issues.withFirstComment","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"runs":{"r":{"rejected":{}},"c":{"rejected":{}}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"runs":{"r":{"rejected":{}},"c":{"rejected":{}}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0}},"runs":{"r":{"rejected":{}},"c":{"rejected":{}}},"preloads":{"p":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1}},"runs":{"r":{"rejected":{}},"c":{"rejected":{}}},"preloads":{"p":{"cached":"pending","complete":"pending"}}}]}, +{"name":"error: a poke whose push throws","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"materialize","view":"u","query":"users.all"},{"op":"materialize","view":"w","query":"issues.withAuthoredComments"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"users","value":{"id":"u1","name":"A","metadata":null}},{"op":"put","tableName":"comments","value":{"id":"c1","authorId":"u1","issue_id":"i1","text":"","createdAt":1}},{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"advance","ms":10},{"op":"materialize","view":"x","query":"users.all"},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i2"}},{"op":"advance","ms":5000},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":300000},{"op":"put","hash":"0nihugc1pbu0fd","name":"issues.withAuthoredComments","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"closed":[0],"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"threw":"Partitioned take does not allow unpartitioned fetches","state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}}}}},{"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}}}}},{"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}}}}},{"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}}}}}]}, +{"name":"error: a mutation whose push throws","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"materialize","view":"u","query":"users.all"},{"op":"materialize","view":"w","query":"issues.withAuthoredComments"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"users","value":{"id":"u1","name":"A","metadata":null}},{"op":"put","tableName":"comments","value":{"id":"c1","authorId":"u1","issue_id":"i1","text":"","createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"mutate","mutation":"m1","mutator":"issue.create","args":{"id":"i1"}},{"op":"advance","ms":10},{"op":"mutate","mutation":"m2","mutator":"issue.create","args":{"id":"i2"}},{"op":"materialize","view":"x","query":"users.all"},{"op":"run","run":"r","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","lastMutationIDChanges":{"$clientID":2}}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":300000},{"op":"put","hash":"0nihugc1pbu0fd","name":"issues.withAuthoredComments","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":1},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}}},{"threw":"Partitioned take does not allow unpartitioned fetches","state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}},"runs":{"r":{"rejected":{}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}},"runs":{"r":{"rejected":{}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}},"runs":{"r":{"rejected":{}}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":"pending"},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}},"runs":{"r":{"rejected":{}}}},{"closed":[0],"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}},"runs":{"r":{"rejected":{}}}},{"state":{"name":"error","reason":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"},"views":{"v":{"data":[{"id":"i1","title":"","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"u":{"data":[{"id":"u1","name":"A","metadata":null}],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}},"mutations":{"m1":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"zero","message":"Poke processing error: Error: Partitioned take does not allow unpartitioned fetches"}}},"m2":{"client":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}},"server":{"type":"error","error":{"type":"app","message":"Partitioned take does not allow unpartitioned fetches"}}}},"runs":{"r":{"rejected":{}}}}]}, +{"name":"error: a poke with two rows for a one relationship","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p0","baseCookie":null}]},{"op":"receive","message":["pokePart",{"pokeID":"p0","gotQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek"}],"rowsPatch":[]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p0","cookie":"00"}]},{"op":"materialize","view":"w","query":"issues.withOneComment"},{"op":"advance","ms":10},{"op":"receive","message":["pokeStart",{"pokeID":"p1","baseCookie":"00"}]},{"op":"receive","message":["pokePart",{"pokeID":"p1","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"issues","value":{"id":"i1","title":"I1","description":"","closed":false,"owner_id":null,"createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p1","cookie":"01"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p2","baseCookie":"01"}]},{"op":"receive","message":["pokePart",{"pokeID":"p2","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"comments","value":{"id":"c1","authorId":"u1","issue_id":"i1","text":"","createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p2","cookie":"02"}]},{"op":"receive","message":["pokeStart",{"pokeID":"p3","baseCookie":"02"}]},{"op":"receive","message":["pokePart",{"pokeID":"p3","gotQueriesPatch":[],"rowsPatch":[{"op":"put","tableName":"comments","value":{"id":"c2","authorId":"u1","issue_id":"i1","text":"","createdAt":1}}]}]},{"op":"receive","message":["pokeEnd",{"pokeID":"p3","cookie":"03"}]},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"10652d800k9wug","name":"issues.withOneComment","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"complete","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":1},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"oneComment":{"id":"c1","authorId":"u1","issueId":"i1","text":"","createdAt":1}}],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"oneComment":{"id":"c1","authorId":"u1","issueId":"i1","text":"","createdAt":1}}],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"oneComment":{"id":"c1","authorId":"u1","issueId":"i1","text":"","createdAt":1}}],"resultType":"unknown","notifications":0}}},{"closed":[0],"state":{"name":"error","reason":"Poke processing error: Error: Singular relationship 'oneComment' should not have multiple rows. You may need to declare this relationship with the `many` helper instead of the `one` helper in your schema."},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"oneComment":{"id":"c1","authorId":"u1","issueId":"i1","text":"","createdAt":1}}],"resultType":"unknown","notifications":0}}},{"state":{"name":"error","reason":"Poke processing error: Error: Singular relationship 'oneComment' should not have multiple rows. You may need to declare this relationship with the `many` helper instead of the `one` helper in your schema."},"views":{"v":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1}],"resultType":"complete","notifications":0},"w":{"data":[{"id":"i1","title":"I1","description":"","closed":false,"ownerId":null,"createdAt":1,"oneComment":{"id":"c1","authorId":"u1","issueId":"i1","text":"","createdAt":1}}],"resultType":"unknown","notifications":0}}}]}, +{"name":"ttl: strings JS parses oddly","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v0","query":"issues.byOwner","args":"t0","ttl":"5x"},{"op":"materialize","view":"v1","query":"issues.byOwner","args":"t1","ttl":""},{"op":"materialize","view":"v2","query":"issues.byOwner","args":"t2","ttl":"s"},{"op":"materialize","view":"v3","query":"issues.byOwner","args":"t3","ttl":"Infinitys"},{"op":"materialize","view":"v4","query":"issues.byOwner","args":"t4","ttl":"0x10s"},{"op":"materialize","view":"v5","query":"issues.byOwner","args":"t5","ttl":"1e1m"},{"op":"materialize","view":"v6","query":"issues.byOwner","args":"t6","ttl":"NaNs"},{"op":"materialize","view":"v7","query":"issues.byOwner","args":"t7","ttl":"-5m"},{"op":"materialize","view":"v8","query":"issues.byOwner","args":"t8","ttl":"1_0s"},{"op":"materialize","view":"v9","query":"issues.byOwner","args":"t9","ttl":" 3s"},{"op":"materialize","view":"v10","query":"issues.byOwner","args":"t10","ttl":"3s "},{"op":"materialize","view":"v11","query":"issues.byOwner","args":"t11","ttl":"+2m"},{"op":"materialize","view":"v12","query":"issues.byOwner","args":"t12","ttl":".5m"},{"op":"materialize","view":"v13","query":"issues.byOwner","args":"t13","ttl":"5.m"},{"op":"materialize","view":"v14","query":"issues.byOwner","args":"t14","ttl":"0b11s"},{"op":"materialize","view":"v15","query":"issues.byOwner","args":"t15","ttl":"1e400s"},{"op":"materialize","view":"v16","query":"issues.byOwner","args":"t16","ttl":"-0s"},{"op":"materialize","view":"v17","query":"issues.byOwner","args":"t17","ttl":"5M"},{"op":"materialize","view":"v18","query":"issues.byOwner","args":"t18","ttl":"1h"},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":0},"v13":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":0},"v13":{"data":[],"resultType":"unknown","notifications":0},"v14":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":0},"v13":{"data":[],"resultType":"unknown","notifications":0},"v14":{"data":[],"resultType":"unknown","notifications":0},"v15":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":0},"v13":{"data":[],"resultType":"unknown","notifications":0},"v14":{"data":[],"resultType":"unknown","notifications":0},"v15":{"data":[],"resultType":"unknown","notifications":0},"v16":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":0},"v13":{"data":[],"resultType":"unknown","notifications":0},"v14":{"data":[],"resultType":"unknown","notifications":0},"v15":{"data":[],"resultType":"unknown","notifications":0},"v16":{"data":[],"resultType":"unknown","notifications":0},"v17":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":0},"v13":{"data":[],"resultType":"unknown","notifications":0},"v14":{"data":[],"resultType":"unknown","notifications":0},"v15":{"data":[],"resultType":"unknown","notifications":0},"v16":{"data":[],"resultType":"unknown","notifications":0},"v17":{"data":[],"resultType":"unknown","notifications":0},"v18":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1u36en60jbbqem","name":"issues.byOwner","args":["t0"],"ttl":null},{"op":"put","hash":"1xm770o0u1j6or","name":"issues.byOwner","args":["t1"],"ttl":null},{"op":"put","hash":"006mdhe18baixp","name":"issues.byOwner","args":["t2"],"ttl":0},{"op":"put","hash":"0ctwpkd0u9brq0","name":"issues.byOwner","args":["t3"],"ttl":600000},{"op":"put","hash":"0fnwj8k0s7f5wx","name":"issues.byOwner","args":["t4"],"ttl":16000},{"op":"put","hash":"1v4slmv0ir82g2","name":"issues.byOwner","args":["t5"],"ttl":600000},{"op":"put","hash":"1gusz0m0zal9na","name":"issues.byOwner","args":["t6"],"ttl":null},{"op":"put","hash":"199lelc1a8f5m4","name":"issues.byOwner","args":["t7"],"ttl":-300000},{"op":"put","hash":"0owz4i811p5vr2","name":"issues.byOwner","args":["t8"],"ttl":null},{"op":"put","hash":"1bg38eo1ivp0i7","name":"issues.byOwner","args":["t9"],"ttl":3000},{"op":"put","hash":"03rccww0uch6gn","name":"issues.byOwner","args":["t10"],"ttl":null},{"op":"put","hash":"0nualdn10cef54","name":"issues.byOwner","args":["t11"],"ttl":120000},{"op":"put","hash":"0vzbbgl0k7pjli","name":"issues.byOwner","args":["t12"],"ttl":30000},{"op":"put","hash":"0r7ghj00x2kbek","name":"issues.byOwner","args":["t13"],"ttl":300000},{"op":"put","hash":"0900bdm03gonx4","name":"issues.byOwner","args":["t14"],"ttl":3000},{"op":"put","hash":"0ybztjz0r6r2py","name":"issues.byOwner","args":["t15"],"ttl":600000},{"op":"put","hash":"1qnvyay034oucy","name":"issues.byOwner","args":["t16"],"ttl":0},{"op":"put","hash":"00gizc310edai5","name":"issues.byOwner","args":["t17"],"ttl":null},{"op":"put","hash":"0boahcy0x7z0m3","name":"issues.byOwner","args":["t18"],"ttl":600000}]}]],"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0},"v10":{"data":[],"resultType":"unknown","notifications":0},"v11":{"data":[],"resultType":"unknown","notifications":0},"v12":{"data":[],"resultType":"unknown","notifications":0},"v13":{"data":[],"resultType":"unknown","notifications":0},"v14":{"data":[],"resultType":"unknown","notifications":0},"v15":{"data":[],"resultType":"unknown","notifications":0},"v16":{"data":[],"resultType":"unknown","notifications":0},"v17":{"data":[],"resultType":"unknown","notifications":0},"v18":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"ttl: numbers at the edges","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v0","query":"issues.byOwner","args":"n0","ttl":0},{"op":"materialize","view":"v1","query":"issues.byOwner","args":"n1","ttl":-1},{"op":"materialize","view":"v2","query":"issues.byOwner","args":"n2","ttl":-0.5},{"op":"materialize","view":"v3","query":"issues.byOwner","args":"n3","ttl":0.001},{"op":"materialize","view":"v4","query":"issues.byOwner","args":"n4","ttl":1e-7},{"op":"materialize","view":"v5","query":"issues.byOwner","args":"n5","ttl":599999.5},{"op":"materialize","view":"v6","query":"issues.byOwner","args":"n6","ttl":600000},{"op":"materialize","view":"v7","query":"issues.byOwner","args":"n7","ttl":600000.5},{"op":"materialize","view":"v8","query":"issues.byOwner","args":"n8","ttl":1e+21},{"op":"materialize","view":"v9","query":"issues.byOwner","args":"n9","ttl":9007199254740992},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"0ejlozd0uymbya","name":"issues.byOwner","args":["n0"],"ttl":0},{"op":"put","hash":"03o80ey1uvqo5r","name":"issues.byOwner","args":["n1"],"ttl":600000},{"op":"put","hash":"09asvzb1yol585","name":"issues.byOwner","args":["n2"],"ttl":600000},{"op":"put","hash":"1ujkwf804fmk19","name":"issues.byOwner","args":["n3"],"ttl":0.001},{"op":"put","hash":"1o8n5081vu5xo4","name":"issues.byOwner","args":["n4"],"ttl":1e-7},{"op":"put","hash":"11h5e4i0fz8yxi","name":"issues.byOwner","args":["n5"],"ttl":599999.5},{"op":"put","hash":"12i95e917o0w8e","name":"issues.byOwner","args":["n6"],"ttl":600000},{"op":"put","hash":"0xv8i7h1lhhr31","name":"issues.byOwner","args":["n7"],"ttl":600000},{"op":"put","hash":"0qyp7g10rchbad","name":"issues.byOwner","args":["n8"],"ttl":600000},{"op":"put","hash":"1h618hl0u9rqdi","name":"issues.byOwner","args":["n9"],"ttl":600000}]}]],"state":{"name":"connected"},"views":{"v0":{"data":[],"resultType":"unknown","notifications":0},"v1":{"data":[],"resultType":"unknown","notifications":0},"v2":{"data":[],"resultType":"unknown","notifications":0},"v3":{"data":[],"resultType":"unknown","notifications":0},"v4":{"data":[],"resultType":"unknown","notifications":0},"v5":{"data":[],"resultType":"unknown","notifications":0},"v6":{"data":[],"resultType":"unknown","notifications":0},"v7":{"data":[],"resultType":"unknown","notifications":0},"v8":{"data":[],"resultType":"unknown","notifications":0},"v9":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"ttl: updateTTL from an unparsable TTL","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all","ttl":"5x"},{"op":"advance","ms":10},{"op":"updateTTL","view":"v","ttl":"1m"},{"op":"advance","ms":10},{"op":"updateTTL","view":"v","ttl":"forever"},{"op":"advance","ms":10},{"op":"materialize","view":"w","query":"issues.all","ttl":"2m"},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":null}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":60000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":600000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0},"w":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"ttl: updateTTL to an unparsable TTL, none, and a negative string","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"v","query":"issues.all","ttl":"none"},{"op":"advance","ms":10},{"op":"updateTTL","view":"v","ttl":"abc"},{"op":"advance","ms":10},{"op":"updateTTL","view":"v","ttl":"-1m"},{"op":"advance","ms":10},{"op":"updateTTL","view":"v","ttl":-1},{"op":"advance","ms":10},{"op":"updateTTL","view":"v","ttl":1},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":0}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":-60000}]}]],"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"v":{"data":[],"resultType":"unknown","notifications":0}}}]}, +{"name":"ttl: preload with odd TTLs","steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"preload","preload":"a","query":"issues.all","ttl":"x"},{"op":"preload","preload":"b","query":"issues.all","ttl":"forever"},{"op":"preload","preload":"c","query":"users.all","ttl":0},{"op":"advance","ms":10},{"op":"cleanup","preload":"a"},{"op":"cleanup","preload":"b"},{"op":"cleanup","preload":"b"},{"op":"cleanup","preload":"c"},{"op":"advance","ms":10}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"},"c":{"cached":"pending","complete":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":null},{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":600000},{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":0}]}]],"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"},"c":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"},"c":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"},"c":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"},"c":{"cached":"pending","complete":"pending"}}},{"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"},"c":{"cached":"pending","complete":"pending"}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"},{"op":"del","hash":"1864qec0oaacid"}]}]],"state":{"name":"connected"},"preloads":{"a":{"cached":"pending","complete":"pending"},"b":{"cached":"pending","complete":"pending"},"c":{"cached":"pending","complete":"pending"}}}]}, +{"name":"throttle: queryChangeThrottleMs 0","options":{"queryChangeThrottleMs":0},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"a","query":"issues.all"},{"op":"materialize","view":"b","query":"users.all"},{"op":"destroy","view":"a"},{"op":"advance","ms":1},{"op":"materialize","view":"c","query":"issues.open"}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":1}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"views":{"b":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"b":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1i6n3ig0hcl1v4","name":"issues.open","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"b":{"data":[],"resultType":"unknown","notifications":0},"c":{"data":[],"resultType":"unknown","notifications":1}}}]}, +{"name":"throttle: a negative queryChangeThrottleMs","options":{"queryChangeThrottleMs":-5},"steps":[{"op":"advance","ms":0},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"a","query":"issues.all"},{"op":"advance","ms":1},{"op":"materialize","view":"b","query":"users.all"}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connected"}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":1}}}]}, +{"name":"throttle: changes while connecting go in the connect message","options":{"queryChangeThrottleMs":50},"steps":[{"op":"advance","ms":0},{"op":"materialize","view":"a","query":"issues.all"},{"op":"advance","ms":20},{"op":"receive","message":["connected",{"wsid":"w1"}]},{"op":"materialize","view":"b","query":"users.all"},{"op":"advance","ms":20},{"op":"destroy","view":"a"},{"op":"advance","ms":40}],"expect":[{"opened":[{"url":{"origin":"ws://zero.test","path":"/sync/v53/connect","query":[["clientID","$clientID"],["clientGroupID","$clientGroupID"],["userID","u1"],["baseCookie",""],["lmid","0"],["wsid","$wsid"],["profileID","$profileID"]]},"protocol":{"initConnectionMessage":["initConnection",{"desiredQueriesPatch":[],"clientSchema":{"tables":{"comments":{"columns":{"authorId":{"type":"string"},"createdAt":{"type":"number"},"id":{"type":"string"},"issue_id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"issueLabel":{"columns":{"issueId":{"type":"string"},"labelId":{"type":"string"}},"primaryKey":["issueId","labelId"]},"issues":{"columns":{"closed":{"type":"boolean"},"createdAt":{"type":"number"},"description":{"type":"string"},"id":{"type":"string"},"owner_id":{"type":"string"},"title":{"type":"string"}},"primaryKey":["id"]},"label":{"columns":{"id":{"type":"string"},"name":{"type":"string"}},"primaryKey":["id"]},"revision":{"columns":{"authorId":{"type":"string"},"commentId":{"type":"string"},"id":{"type":"string"},"text":{"type":"string"}},"primaryKey":["id"]},"users":{"columns":{"id":{"type":"string"},"metadata":{"type":"json"},"name":{"type":"string"}},"primaryKey":["id"]}}},"activeClients":["$clientID"]}]}}],"state":{"name":"connecting"}},{"state":{"name":"connecting"},"views":{"a":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connecting"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000}]}]],"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":1}}},{"state":{"name":"connected"},"views":{"a":{"data":[],"resultType":"unknown","notifications":0},"b":{"data":[],"resultType":"unknown","notifications":0}}},{"state":{"name":"connected"},"views":{"b":{"data":[],"resultType":"unknown","notifications":0}}},{"sent":[["changeDesiredQueries",{"desiredQueriesPatch":[{"op":"put","hash":"1y7zr0i1xir4ek","name":"issues.all","args":[],"ttl":300000},{"op":"put","hash":"1864qec0oaacid","name":"users.all","args":[],"ttl":300000},{"op":"del","hash":"1y7zr0i1xir4ek"}]}]],"state":{"name":"connected"},"views":{"b":{"data":[],"resultType":"unknown","notifications":0}}}]} +]} diff --git a/packages/zero-conformance/vitest.config.ts b/packages/zero-conformance/vitest.config.ts index 81f1a70d74..f849267dfc 100644 --- a/packages/zero-conformance/vitest.config.ts +++ b/packages/zero-conformance/vitest.config.ts @@ -9,5 +9,17 @@ export default mergeConfig(config, { browser: { enabled: false, }, + // A materialize that throws in JS has already registered its query, and + // that query's completion promise, which nothing holds, rejects when the + // client closes (`materializeImpl` in zql). The scenarios that check a + // failed materialize (harden-queries) leave one behind; that rejection + // isn't part of what they record. + onUnhandledError: error => + !( + 'error' in error && + error.error === 'app' && + 'details' in error && + (error.details as {kind?: unknown} | undefined)?.kind === 'ClientClosed' + ), }, }); diff --git a/swift/.amp/portals/.service-ports b/swift/.amp/portals/.service-ports new file mode 100644 index 0000000000..4f4eb9ab00 --- /dev/null +++ b/swift/.amp/portals/.service-ports @@ -0,0 +1,5 @@ +{ + "servicePorts": { + "zero-e2e": 30882 + } +} diff --git a/swift/DESIGN.md b/swift/DESIGN.md index 27de93f745..e556933e04 100644 --- a/swift/DESIGN.md +++ b/swift/DESIGN.md @@ -198,10 +198,15 @@ Rationale: minutes, clients inactive for a day are deleted (and the server told with `deleteClients`, and in `initConnection` until it confirms) and client groups without clients are dropped; five minutes after launch and then every - 12 hours, databases no client used for two days are deleted. A group is - disabled after `ClientNotFound`, `SchemaVersionNotSupported`, or a 1009 - close, and the database dropped after `InvalidConnectionRequestBaseCookie` - or `InvalidConnectionRequestLastMutationID`. + 12 hours, databases no client used for two days are deleted. Only our + databases are: of this layout version or an older one, holding their name, + and of a format the client knows. The collector reads the files in the + background; the main actor then keeps any opened or closed since and renames + the others away before they are deleted, so a client opening one meanwhile + starts a new file. A group is disabled after `ClientNotFound`, + `SchemaVersionNotSupported`, or a 1009 close, and the database dropped after + `InvalidConnectionRequestBaseCookie` or + `InvalidConnectionRequestLastMutationID`. - `ZeroStorage.memory` keeps databases for the life of the process, keyed by name, like JS `kvStore: 'mem'`. The client scenarios run on both storages. - Default location `Library/Application Support/Zero` on Apple platforms and @@ -213,7 +218,14 @@ Rationale: - No database-level encryption. - If the store can't be opened, the client runs on memory and calls `onStorageFailure`, as JS does. A failed write stops writing and calls it - too. + too. A write that finds the database busy is kept and written with the + next one, or by `flush()` and `close()`, which try twice more before they + call it a failure. Stored data that doesn't decode is lost client state, + as a corrupt ref count is in JS: the client deletes the database, starts + a new one, and calls `onClientStateNotFound` if the app set it. +- A client writes a poke's snapshot only when its cookie is newer than the + stored one, as Replicache persists, so a client of the group that is + behind another doesn't overwrite it. - One process per store in v1: one `Zero` instance per user and storage key per process. Concurrent cross-process access (app extensions) is deferred, and so is refreshing one instance from another's writes. diff --git a/swift/Sources/Zero/Builder/Builder.swift b/swift/Sources/Zero/Builder/Builder.swift index edb979df74..26b82b4913 100644 --- a/swift/Sources/Zero/Builder/Builder.swift +++ b/swift/Sources/Zero/Builder/Builder.swift @@ -149,8 +149,9 @@ func buildPipelineInternal( for key in csq.correlation.parentField { splitEditKeys.insert(key) } } if isNonFlippedExistsChild { - precondition(ast.start == nil, "EXISTS subqueries must not have start") - precondition(ast.related == nil, "EXISTS subqueries must not have related") + // JS asserts these, which throws. + if ast.start != nil { throw PipelineError("EXISTS subqueries must not have start") } + if ast.related != nil { throw PipelineError("EXISTS subqueries must not have related") } } // Cap needs an unordered connection, but a flipped subquery in the where @@ -198,7 +199,9 @@ func buildPipelineInternal( } if let limitValue = ast.limit { - let limit = Int(limitValue) + guard let limit = Int(exactly: limitValue), limit >= 0 else { + throw PipelineError("Invalid limit \(JSNumber.format(limitValue))") + } if useCap { let cap = Cap(end, limit: limit, partitionKey: partitionKey) delegate.addEdge(end, cap) diff --git a/swift/Sources/Zero/Client/ConnectionManager.swift b/swift/Sources/Zero/Client/ConnectionManager.swift index 6c833dc72c..45429791f9 100644 --- a/swift/Sources/Zero/Client/ConnectionManager.swift +++ b/swift/Sources/Zero/Client/ConnectionManager.swift @@ -244,7 +244,7 @@ final class ConnectionManager { .client( .offline, message: - "Zero was unable to connect for \(Int((disconnectTimeout / 1000).rounded(.down))) seconds and was disconnected" + "Zero was unable to connect for \(JSNumber.format((disconnectTimeout / 1000).rounded(.down))) seconds and was disconnected" )) } } diff --git a/swift/Sources/Zero/Client/Engine.swift b/swift/Sources/Zero/Client/Engine.swift index 9015a16500..37b4973b90 100644 --- a/swift/Sources/Zero/Client/Engine.swift +++ b/swift/Sources/Zero/Client/Engine.swift @@ -93,9 +93,9 @@ final class Engine { loop.stop() for t in lifecycleTimers { t.cancel() } if !repClosed { - database.removeWriteFailureListener(clientID) database.clientStopped(clientID) database.release() + database.removeWriteFailureListener(clientID) } } private let socketFactory: (any SocketFactory)? @@ -173,29 +173,55 @@ final class Engine { userID: options.userID, storageKey: options.storageKey ?? "", mutateURL: options.mutateURL, queryURL: options.queryURL, schemaHash: clientSchema.hash) - var (database, openFailure) = ReplicaDatabase.open(options.storage, name: name) + let mutatorNames = mutators.keys.sorted() + var (database, openFailure, corrupt) = ReplicaDatabase.open(options.storage, name: name) var joined: (clientGroupID: String, group: StoredGroup) do { - joined = try database.initClient( - clientID, mutatorNames: mutators.keys.sorted(), now: base.now) + do { + joined = try database.initClient(clientID, mutatorNames: mutatorNames, now: base.now) + } catch is ReplicaDatabase.CorruptData { + // What the client group holds doesn't decode, so the client state + // is lost: drop the database and start a new one + // (`#handleInvalidRefCount`). + corrupt = true + database.drop() + database.release() + (database, openFailure, _) = ReplicaDatabase.open(options.storage, name: name) + joined = try database.initClient(clientID, mutatorNames: mutatorNames, now: base.now) + } } catch { // The client group can't be read: run on memory, as when the open // fails. database.release() - database = ReplicaDatabase.memory(name: name) - openFailure = error - joined = try database.initClient( - clientID, mutatorNames: mutators.keys.sorted(), now: base.now) + database = ReplicaDatabase.fallback(name: name, storage: options.storage) + openFailure = StorageFailure(error) + joined = try database.initClient(clientID, mutatorNames: mutatorNames, now: base.now) } let (clientGroupID, group) = joined self.database = database self.clientGroupID = clientGroupID self.profileID = database.profileID + // Mutation IDs become `Int`s: drop stored ones that can't be (a corrupt + // or tampered file) rather than trap on them. + func isMutationID(_ id: Double) -> Bool { id >= 0 && JSNumber.isSafeInteger(id) } + let log = self.log + let lastMutationIDs = group.lastMutationIDs.filter { cid, lmid in + if isMutationID(lmid) { return true } + log.error("Dropping invalid stored last mutation ID of \(cid): \(JSNumber.format(lmid))") + return false + } + let mutations = group.mutations.filter { m in + if isMutationID(m.id) { return true } + log.error( + "Dropping stored mutation \"\(m.name)\" of \(m.clientID) with invalid ID " + + JSNumber.format(m.id)) + return false + } self.store = ReplicaStore( clientID: clientID, snapshot: ReplicaStore.Snapshot( - map: group.entries, cookie: group.cookie, lastMutationIDs: group.lastMutationIDs), - pending: group.mutations.map { + map: group.entries, cookie: group.cookie, lastMutationIDs: lastMutationIDs), + pending: mutations.map { ReplicaStore.PendingMutation( id: $0.id, clientID: $0.clientID, name: $0.name, args: $0.args, timestamp: $0.timestamp, writes: []) @@ -238,7 +264,10 @@ final class Engine { onFatalError: { [weak self] error in self?.disconnect(error) }) pokeHandler = PokeHandler( clientID: clientID, schema: schema, scheduler: scheduler, - apply: { [weak self] poke in try self?.applyPoke(poke) }, + apply: { [weak self] poke in + guard let self else { return } + try self.withWriteLock { try self.applyPoke(poke) } + }, onApplied: { [weak self] poke in guard let self else { return } defer { self.settlePendingWaiters() } @@ -267,6 +296,12 @@ final class Engine { self.sendIfConnected(.deleteClients(DeleteClientsBody(clientIDs: [id]))) } } + if corrupt { + // This client already starts on the new database, which is what + // replacing it would do; the app's callback still hears it, as in JS. + log.error("Client state is corrupt; dropped database \(name) and started a new one") + loop.microtask { [weak self] in self?.options.onClientStateNotFound?() } + } if let openFailure { log.warn( "Local store storage failed (\(openFailure.kind.rawValue)) opening \(name); " @@ -500,7 +535,8 @@ final class Engine { private func ping(_ token: Int, _ end: @escaping () -> Void) { let token = nextLoopToken() - send(.ping) + // Before the send, as JS sets `#onPong` first: a transport may deliver + // the pong from inside `send`. onPong = { [weak self] in self?.loop.microtask { guard let self, self.loopToken == token else { return } @@ -508,6 +544,7 @@ final class Engine { self.iterate() } } + send(.ping) _ = scheduler.schedule(after: options.pingTimeoutMs) { [weak self] in guard let self, self.loopToken == token else { return } let error = ConnectionError.client(.pingTimeout, message: "Server ping request failed") @@ -1048,9 +1085,11 @@ final class Engine { pushTimer = nil for t in lifecycleTimers { t.cancel() } lifecycleTimers = [] - database.removeWriteFailureListener(clientID) database.clientStopped(clientID) + // Releasing writes what the database kept while busy; a failure to is + // still this client's to report. database.release() + database.removeWriteFailureListener(clientID) connectionManager.closed() } @@ -1153,9 +1192,22 @@ final class Engine { } /// `zero.mutate`, through the `MutatorProxy` and Replicache's `mutate`. + /// + /// A mutation made while listeners are notified, from a listener, runs + /// once the current task is done, as an async JS mutator commits after + /// the notification: every listener sees the change that notified it + /// before the mutation's, and every view sees both. func mutate(name: String, args: JSONValue?) -> MutatorResult { let client = Deferred() let server = Deferred() + if context.isNotifying { + loop.afterCurrentTask { [self] in + let result = mutate(name: name, args: args) + result.client.onSettle { client.settle($0) } + result.server.onSettle { server.settle($0) } + } + return MutatorResult(client: client, server: server) + } // JS throws this before it looks at the connection; here both outcomes // carry it, and nothing is written or pushed. guard let mutator = mutators[name] else { @@ -1186,22 +1238,82 @@ final class Engine { server.resolve(outcome) } } + // A mutate from inside a mutator, or while a poke rebases, waits for it, + // as JS's waits for Replicache's write lock. + if writeLocked || !waitingForWriteLock.isEmpty { + waitingForWriteLock.append { [weak self] in + self?.runMutation( + mutator, name: name, args: args, ephemeralID: ephemeralID, client: client, server: server) + } + } else { + runMutation( + mutator, name: name, args: args, ephemeralID: ephemeralID, client: client, server: server) + } + return MutatorResult(client: client, server: server) + } + + /// Set while a mutator runs, for a mutation or a rebase: Replicache's + /// write lock. Mutations that start meanwhile wait in + /// `waitingForWriteLock`, in order. + private var writeLocked = false + private var waitingForWriteLock: [() -> Void] = [] + + private func withWriteLock(_ body: () throws -> T) rethrows -> T { + writeLocked = true + defer { + writeLocked = false + if !waitingForWriteLock.isEmpty { + loop.microtask { [weak self] in + guard let self, !self.writeLocked, !self.waitingForWriteLock.isEmpty else { return } + self.waitingForWriteLock.removeFirst()() + } + } + } + return try body() + } + + private func runMutation( + _ mutator: MutatorFunction, name: String, args: JSONValue?, ephemeralID: Int, + client: Deferred, server: Deferred + ) { + withWriteLock { + runMutationLocked( + mutator, name: name, args: args, ephemeralID: ephemeralID, client: client, server: server) + } + } + + private func runMutationLocked( + _ mutator: MutatorFunction, name: String, args: JSONValue?, ephemeralID: Int, + client: Deferred, server: Deferred + ) { + let log = self.log let mutationID = store.nextMutationID(for: clientID) + // Last mutation IDs are checked when they arrive, so this always holds. + guard let intMutationID = Int(exactly: mutationID) else { + let error = ConnectionError.client( + .internal, message: "Invalid mutation ID \(JSNumber.format(mutationID))") + mutationTracker.rejectMutation(ephemeralID, error) + client.resolve(Self.outcome(error)) + return + } let write = KVWrite(store.main) let branch = ivmMain.fork() mutationTracker.mutationIDAssigned(ephemeralID, mutationID, name: name, args: args ?? .null) let tx = Transaction( - clientID: clientID, mutationID: Int(mutationID), reason: .optimistic, token: token, + clientID: clientID, mutationID: intMutationID, reason: .optimistic, token: token, write: write, branch: branch, schema: schema) let result: JSONValue? do { + // JS forks the views' branch for the transaction, which throws once + // that branch has failed. + if let failure = context.branchFailure { throw failure } result = try mutator(tx, args) } catch { mutationTracker.rejectMutation(ephemeralID, error) let outcome = Self.outcome(error) Self.logFailure(log, name, outcome, on: "client") client.resolve(outcome) - return MutatorResult(client: client, server: server) + return } let pending = ReplicaStore.PendingMutation( id: mutationID, clientID: clientID, name: name, args: args ?? .null, @@ -1214,11 +1326,20 @@ final class Engine { clientID: clientID, id: mutationID, name: name, args: pending.args, timestamp: pending.timestamp)) ]) - context.processChanges(diff, rows: tx.rows) + do { + try context.processChanges(diff, rows: tx.rows) + } catch { + // As in JS, the mutation stays committed and pending, but isn't + // pushed now, and its client outcome is the error. + fireWatchers(diff) + let outcome = Self.outcome(error) + Self.logFailure(log, name, outcome, on: "client") + client.resolve(outcome) + return + } requestPush(now: false) fireWatchers(diff) client.resolve(.success(data: result)) - return MutatorResult(client: client, server: server) } private var token: String? { auth.flatMap { $0.isEmpty ? nil : $0 } } @@ -1348,6 +1469,7 @@ final class Engine { } catch { throw PokeError(error.description) } + try checkUpdatedRows(poke.patch, patchWrite) snapshot.map = patchWrite.map var touched = patchWrite.writes.union(store.pendingWrites) @@ -1378,7 +1500,8 @@ final class Engine { cleared: patchWrite.cleared, cookie: snapshot.cookie, lastMutationIDs: snapshot.lastMutationIDs) ]) - context.processChanges(diff) + // A push that fails is a poke error: the client disconnects. + try context.processChanges(diff) fireWatchers(diff) markGotQueriesAuthoritative(ifStillOn: socketAtStart) } @@ -1404,10 +1527,16 @@ final class Engine { var touched = Set() var replayed: [ReplicaStore.PendingMutation] = [] for var m in pending { + // Stored and assigned IDs are checked, so this always holds. + guard let mutationID = Int(exactly: m.id) else { + m.writes = [] + replayed.append(m) + continue + } let write = KVWrite(main) let fork = branch.fork() let tx = Transaction( - clientID: m.clientID, mutationID: Int(m.id), reason: .rebase, token: token, + clientID: m.clientID, mutationID: mutationID, reason: .rebase, token: token, write: write, branch: fork, schema: schema) do { guard let mutator = mutators[m.name] else { @@ -1441,6 +1570,40 @@ final class Engine { return "[\n" + entries.joined(separator: ",\n") + "\n]" } + /// Rejects a poke whose `update` ops leave a row under a key that isn't + /// its primary key: one whose merge drops or changes the key's columns. + /// The table's source would then hold rows that disagree with the + /// replica, which JS's `MemorySource` reports by throwing ("Row already + /// exists", "Row not found") and the poke handler by disconnecting. + private func checkUpdatedRows(_ patch: [KVPatchOp], _ write: KVWrite) throws { + var keyless: [String: [Row]] = [:] + for case .update(let key, _, _) in patch { + guard let value = write.get(key) else { continue } + let table = IVMBranch.sourceName(key) + guard let primaryKey = schema.tables[table]?.primaryKey else { continue } + let row = value.objectValue ?? [:] + if let rowKey = try? Keys.primaryKeyString(table: table, primaryKey: primaryKey, row: row) { + guard StringCompare.equal(rowKey, key) else { + throw PokeError("update of \(key) changes its primary key to \(rowKey)") + } + continue + } + // JS keeps a row whose merge leaves it without a usable primary key, + // and fails once a second one collides with it in the source. + guard let source = ivmMain.source(table) else { continue } + let sourceRow = Row(row, layout: source.layout) + let compare = source.primaryIndex.data.compare + let previous = store.main.get(key).map { Row($0.objectValue ?? [:], layout: source.layout) } + let replacesItself = previous.map { compare($0, sourceRow) == 0 } ?? false + if (!replacesItself && source.primaryIndex.data.contains(sourceRow)) + || keyless[table, default: []].contains(where: { compare($0, sourceRow) == 0 }) + { + throw PokeError("Row already exists: update of \(key)") + } + keyless[table, default: []].append(sourceRow) + } + } + static func badOrderMessage(_ name: String, _ received: String, _ last: String) -> String { "Received \(name) \(received) is < than last snapshot \(name) \(last); ignoring client view" } diff --git a/swift/Sources/Zero/Client/EventLoop.swift b/swift/Sources/Zero/Client/EventLoop.swift index 6cd8246373..aa70413b56 100644 --- a/swift/Sources/Zero/Client/EventLoop.swift +++ b/swift/Sources/Zero/Client/EventLoop.swift @@ -16,6 +16,7 @@ final class EventLoop { private var queue: [() -> Void] = [] private var head = 0 + private var afterTask: [() -> Void] = [] private var depth = 0 private var checkpointRequested = false private let scheduler: any Scheduler @@ -32,6 +33,25 @@ final class EventLoop { if depth == 0 { requestCheckpoint() } } + /// Runs `work` once the current task or call is done, before its + /// microtasks. Outside a task or call (a microtask draining before a + /// task's body), it is queued as a microtask, which runs once the current + /// one is done. + func afterCurrentTask(_ work: @escaping () -> Void) { + guard !stopped else { return } + if depth == 0 { return microtask(work) } + afterTask.append(work) + } + + private func runAfterTask() { + var i = 0 + while i < afterTask.count { + afterTask[i]() + i += 1 + } + afterTask.removeAll() + } + /// Runs a task: the microtasks already queued, `body`, then the /// microtasks it queued. func dispatch(_ body: () throws -> T) rethrows -> T { @@ -39,6 +59,7 @@ final class EventLoop { drain() depth = 1 defer { + runAfterTask() drain() depth = 0 } @@ -51,6 +72,7 @@ final class EventLoop { if depth > 0 { return try body() } depth = 1 defer { + runAfterTask() depth = 0 if head < queue.count { requestCheckpoint() } } @@ -62,6 +84,7 @@ final class EventLoop { func stop() { stopped = true queue.removeAll() + afterTask.removeAll() head = 0 } diff --git a/swift/Sources/Zero/Client/IVMBranch.swift b/swift/Sources/Zero/Client/IVMBranch.swift index 65a76da591..41d44f6def 100644 --- a/swift/Sources/Zero/Client/IVMBranch.swift +++ b/swift/Sources/Zero/Client/IVMBranch.swift @@ -37,7 +37,8 @@ final class IVMBranch { func apply(_ diff: KVDiff, rows written: [String: Row] = [:]) { let prefix = Keys.entitiesPrefix var i = 0 - while i < diff.count { + // JS's throw ends `applyDiffs` at the change an operator failed on. + while i < diff.count, !PipelineFailure.hasFailed { let op = diff[i] guard op.key.utf8.starts(with: prefix.utf8) else { i += 1 diff --git a/swift/Sources/Zero/Client/Keys.swift b/swift/Sources/Zero/Client/Keys.swift index 29203c365c..1d0d99b07c 100644 --- a/swift/Sources/Zero/Client/Keys.swift +++ b/swift/Sources/Zero/Client/Keys.swift @@ -8,8 +8,10 @@ package enum Keys { package struct InvalidPrimaryKeyValue: Error, CustomStringConvertible { package let column: String package let value: JSONValue? + /// valita's message for `primaryKeyValueSchema`, which JS throws as + /// a `TypeError`. package var description: String { - "Invalid primary key value for \(column): \(value.map(JSONSerializer.stringify) ?? "undefined")" + "Expected string, number or boolean. Got \(JSONDecode.valitaDisplay(value))" } } diff --git a/swift/Sources/Zero/Client/MutationTracker.swift b/swift/Sources/Zero/Client/MutationTracker.swift index bfff0ac020..dc6f45ca4e 100644 --- a/swift/Sources/Zero/Client/MutationTracker.swift +++ b/swift/Sources/Zero/Client/MutationTracker.swift @@ -8,7 +8,8 @@ public struct ApplicationError: Error, Sendable, Hashable, CustomStringConvertib public init(_ message: String, details: JSONValue? = nil) { self.message = message - self.details = details + // `details ?? undefined` in JS. + self.details = details == .null ? nil : details } public var description: String { message } @@ -16,6 +17,10 @@ public struct ApplicationError: Error, Sendable, Hashable, CustomStringConvertib /// `wrapWithApplicationError`. static func wrapping(_ error: any Error) -> ApplicationError { if let e = error as? ApplicationError { return e } + // `getErrorDetails` gives an error of another class its name. + if error is Keys.InvalidPrimaryKeyValue { + return ApplicationError(errorMessage(error), details: ["name": "TypeError"]) + } return ApplicationError(errorMessage(error)) } } @@ -225,7 +230,15 @@ final class MutationTracker { let failure: any Error if kind == "app" { failure = ApplicationError( - error.message?.stringValue ?? "Unknown application error: app", details: error.details) + error.message?.stringValue ?? "Unknown application error: app", + // JS keeps details only when truthy (`error.details ? {details} : undefined`). + details: error.details.flatMap { d in + switch d { + case .null, .bool(false), .string(""): nil + case .number(let n) where n == 0 || n.isNaN: nil + default: d + } + }) } else { let message = kind == "oooMutation" diff --git a/swift/Sources/Zero/Client/PokeHandler.swift b/swift/Sources/Zero/Client/PokeHandler.swift index a6f1291328..4dcc2a1e7e 100644 --- a/swift/Sources/Zero/Client/PokeHandler.swift +++ b/swift/Sources/Zero/Client/PokeHandler.swift @@ -77,6 +77,10 @@ final class PokeHandler { handlePokeError("received both pokePart and binary poke chunks") return nil } + if let message = Self.invalidLastMutationID(part) { + handlePokeError(message) + return nil + } receiving!.parts.append(part) return part.lastMutationIDChanges?[JSKey(clientID)] } @@ -113,6 +117,12 @@ final class PokeHandler { handlePokeError("TypeError: " + valitaMessage(error)) return nil } + for part in parts { + if let message = Self.invalidLastMutationID(part) { + handlePokeError(message) + return nil + } + } } var result = PokeEndResult(lastMutationIDChangeForSelf: nil, hasRows: false) for part in parts { @@ -129,6 +139,17 @@ final class PokeHandler { func handleDisconnect() { clear() } + /// Why `part` can't be applied: a last mutation ID that isn't a safe + /// non-negative integer. JS takes any number; mutation IDs here become + /// `Int`s, so such a poke is rejected like other bad poke data. + static func invalidLastMutationID(_ part: PokePartBody) -> String? { + guard let changes = part.lastMutationIDChanges else { return nil } + for (clientID, lmid) in changes where !(lmid >= 0 && JSNumber.isSafeInteger(lmid)) { + return "invalid lastMutationIDChanges for \(clientID): \(JSNumber.format(lmid))" + } + return nil + } + private func startPlayback() { playbackRunning = true _ = scheduler.schedule(after: 0) { [weak self] in self?.scheduledCallback() } @@ -158,6 +179,7 @@ final class PokeHandler { /// `String(error)` of the JS error the failure stands for. static func errorString(_ error: any Error) -> String { if let e = error as? PokeError { return "Error: " + e.message } + if error is Keys.InvalidPrimaryKeyValue { return "TypeError: \(error)" } return "Error: " + String(describing: error) } @@ -192,9 +214,7 @@ final class PokeHandler { for (clientID, lmid) in changes { merged.lastMutationIDChanges[clientID] = lmid } } if let patches = part.desiredQueriesPatches { - for (clientID, patch) in patches.sorted(by: { - StringCompare.utf16Less($0.key.string, $1.key.string) - }) { + for (clientID, patch) in patches.map({ ($0.clientID, $0.patch) }) { for op in patch { merged.patch.append( Self.queryPatchOp(op) { diff --git a/swift/Sources/Zero/Client/QueryContext.swift b/swift/Sources/Zero/Client/QueryContext.swift index 6d5e56a446..d35366dfcc 100644 --- a/swift/Sources/Zero/Client/QueryContext.swift +++ b/swift/Sources/Zero/Client/QueryContext.swift @@ -34,6 +34,13 @@ final class QueryContext { private let closedReason: () -> ConnectionError? /// The hashes of the unnamed queries already warned about. private var warnedLocalOnly = Set() + /// Counts the view notifications under way. + let notifications = NotificationGate() + + /// Whether listeners are being told about a change. JS mutators are + /// async, so a mutation made from a listener commits once the + /// notification is over; `Engine.mutate` defers one made now. + var isNotifying: Bool { notifications.depth > 0 } init( branch: IVMBranch, @@ -136,15 +143,35 @@ final class QueryContext { } } - /// Pushes `diff` into the branch and flushes the views. - func processChanges(_ diff: KVDiff, rows: [String: Row] = [:]) { + /// Why pushing into the branch failed, once it has (`#advanceError` in + /// `ivm-branch.ts`). The pipelines saw part of that push, so they no + /// longer match the sources: as in JS, the branch is unusable from then + /// on, and every later push, `materialize`, and mutation fails with it. + private(set) var branchFailure: PipelineError? + + /// Pushes `diff` into the branch and flushes the views. A push an + /// operator fails (where JS throws from it) still flushes what the views + /// got, as JS's `finally` does, and then throws. + func processChanges(_ diff: KVDiff, rows: [String: Row] = [:]) throws { + if let branchFailure { throw branchFailure } + var failure: PipelineError? batchViewUpdates { - branch.apply(diff, rows: rows) + do { + try PipelineFailure.capture { branch.apply(diff, rows: rows) } + } catch { + failure = error as? PipelineError ?? PipelineError("\(error)") + } endTransaction() } + if let failure { + branchFailure = failure + throw failure + } } private func endTransaction() { + notifications.depth += 1 + defer { notifications.depth -= 1 } for (_, l) in commitListeners { l() } } @@ -155,6 +182,10 @@ final class QueryContext { } func materialize(_ query: AnyQuery, ttl: TTL = .defaultTTL) throws -> MaterializedView { + if let message = query.invalid { throw PipelineError(message) } + // JS's getSource throws once the branch failed. Unlike JS, the query + // isn't registered first. + if let branchFailure { throw branchFailure } let customQueryID = query.customQueryID let ast = query.ast let queryID = @@ -231,13 +262,30 @@ final class QueryContext { } var created: ArrayView! + var hydrationError: (any Error)? batchViewUpdates { let v: ArrayView + // Hydrating fetches the pipeline, which can fail where JS throws. + func hydrate(_ error: ErroredQuery?, complete: Bool) -> ArrayView? { + do { + return try PipelineFailure.capture { + ArrayView(input, format: query.format, error: error, complete: complete) + } + } catch { + hydrationError = error + return nil + } + } switch queryComplete { - case .complete: v = ArrayView(input, format: query.format, error: nil, complete: true) - case .failed(let e): v = ArrayView(input, format: query.format, error: e, complete: false) + case .complete: + guard let h = hydrate(nil, complete: true) else { return } + v = h + case .failed(let e): + guard let h = hydrate(e, complete: false) else { return } + v = h case .pending: - v = ArrayView(input, format: query.format, error: nil, complete: false) + guard let h = hydrate(nil, complete: false) else { return } + v = h onResolved = { [weak v] (r: Completion) in switch r { case .complete: microtask { v?.setResultType(.complete) } @@ -250,6 +298,14 @@ final class QueryContext { removeCommitObserver = onTransactionCommit { [weak v] in v?.flush() } created = v } + if let hydrationError { + // Unlike JS, which leaves the query registered, nothing is left + // behind: there is no view to destroy. + destroyed = true + input.destroy() + removeAddedQuery() + throw hydrationError + } created.onDestroy = { destroyed = true removePendingAttach?() @@ -289,7 +345,7 @@ final class QueryContext { let updateTTL: (TTL) -> Void = customQueryID.map { id in { [weak self] ttl in self?.updateCustomQuery(id, ttl) } } ?? { _ in } - return MaterializedView(created, updateTTL: updateTTL) + return MaterializedView(created, updateTTL: updateTTL, notifications: notifications) } enum RunType: String, Sendable { @@ -309,7 +365,14 @@ final class QueryContext { result.reject(error) return result } - let v = try materialize(query, ttl: ttl ?? .defaultTTL) + let v: MaterializedView + do { + v = try materialize(query, ttl: ttl ?? .defaultTTL) + } catch { + // JS's `run` is async: a query that can't be materialized rejects it. + result.reject(error) + return result + } if type == .unknown { // While the pipelines are not ready the view is deferred and this is // empty: `unknown` does not wait for local data either. @@ -352,6 +415,12 @@ final class QueryContext { complete.reject(error) return Preload(cleanup: {}, cached: cached, complete: complete) } + if let message = query.invalid { + log?.error(message) + cached.reject(PipelineError(message)) + complete.reject(PipelineError(message)) + return Preload(cleanup: {}, cached: cached, complete: complete) + } guard let id = query.customQueryID else { warnLocalOnly(query, "preloading it does nothing, and its cached and complete never settle") return Preload(cleanup: {}, cached: cached, complete: complete) @@ -424,9 +493,17 @@ public final class MaterializedView { var query: AnyQuery? var ttl: TTL = .defaultTTL - init(_ view: ArrayView, updateTTL: @escaping (TTL) -> Void) { + private var notifications: NotificationGate + /// How many notification passes are under way. As in JS, a listener that + /// destroys the view doesn't keep the pass from reaching the others. + private var forwarding = 0 + + init( + _ view: ArrayView, updateTTL: @escaping (TTL) -> Void, notifications: NotificationGate + ) { self.view = view self.updateTTLImpl = updateTTL + self.notifications = notifications forward() } @@ -477,7 +554,20 @@ public final class MaterializedView { removeForward = view.addListener { [weak self] data, type in guard let self else { return } let error = self.view.listenerResultType.1 - for (_, listener) in self.listeners { listener(data, type, error) } + self.notifications.depth += 1 + self.forwarding += 1 + defer { + self.notifications.depth -= 1 + self.forwarding -= 1 + // Destroyed during the pass: its listeners went on hearing it. + if self.destroyed && self.forwarding == 0 { self.listeners = [] } + } + forEachLive(self.listeners) { listener in + listener(data, type, error) + } next: { + [unowned self] in + self.listeners + } } } @@ -491,6 +581,7 @@ public final class MaterializedView { other.destroyed = true view = other.view updateTTLImpl = other.updateTTLImpl + notifications = other.notifications forward() } @@ -500,7 +591,7 @@ public final class MaterializedView { guard !destroyed else { return } destroyed = true removeForward() - listeners = [] + if forwarding == 0 { listeners = [] } view.destroy() } @@ -511,6 +602,12 @@ public final class MaterializedView { } } +/// How many view notifications are under way on a client. +@MainActor +final class NotificationGate { + var depth = 0 +} + /// Keeps a view alive while its snapshot stream is iterated. @MainActor private final class SnapshotSubscription { diff --git a/swift/Sources/Zero/Client/Scheduler.swift b/swift/Sources/Zero/Client/Scheduler.swift index a6a1b0cc3b..2027e12138 100644 --- a/swift/Sources/Zero/Client/Scheduler.swift +++ b/swift/Sources/Zero/Client/Scheduler.swift @@ -52,7 +52,7 @@ public final class SystemScheduler: Scheduler { { let timer = SystemTimer() timer.task = Task { @MainActor in - try? await Task.sleep(for: .milliseconds(max(0, ms))) + try? await Task.sleep(for: .milliseconds(timerDelay(ms))) guard !timer.cancelled else { return } timer.cancelled = true action() @@ -68,7 +68,7 @@ public final class SystemScheduler: Scheduler { let timer = SystemTimer() timer.task = Task { @MainActor in while !timer.cancelled { - try? await Task.sleep(for: .milliseconds(max(1, ms))) + try? await Task.sleep(for: .milliseconds(max(1, timerDelay(ms)))) if timer.cancelled { return } action() } @@ -82,6 +82,13 @@ public final class SystemScheduler: Scheduler { } } +/// The delay `setTimeout` and `setInterval` wait for `ms`: none for NaN, +/// a negative delay, or one past 2^31 - 1 ms, which browsers and Node fire +/// at once. +package func timerDelay(_ ms: Double) -> Double { + ms >= 0 && ms <= 2_147_483_647 ? ms : 0 +} + @MainActor final class SystemTimer: ScheduledTimer { var task: Task? @@ -137,7 +144,7 @@ public final class Deferred { if let result { observer(result) } else { observers.append(observer) } } - private func settle(_ r: Result) { + func settle(_ r: Result) { guard result == nil else { return } result = r let waiters = self.waiters diff --git a/swift/Sources/Zero/Client/Socket.swift b/swift/Sources/Zero/Client/Socket.swift index a737afb1ab..faafa294e4 100644 --- a/swift/Sources/Zero/Client/Socket.swift +++ b/swift/Sources/Zero/Client/Socket.swift @@ -53,12 +53,16 @@ final class TransportConnection: SocketConnection { private weak var events: (any SocketEvents)? private var closed = false private let outgoing: AsyncStream.Continuation + /// The task waiting for `Transport.connect`, until it returns. Closing + /// cancels it, which aborts the handshake, as closing a connecting JS + /// `WebSocket` does. + private var connecting: Task? init(transport: any Transport, url: String, secProtocol: String?, events: any SocketEvents) { self.events = events let (frames, outgoing) = AsyncStream.makeStream() self.outgoing = outgoing - Task { @MainActor [weak self] in + connecting = Task { @MainActor [weak self] in let socket: any Socket do { socket = try await transport.connect(url: url, secProtocol: secProtocol) @@ -66,6 +70,7 @@ final class TransportConnection: SocketConnection { self?.finish(SocketClosed(error)) return } + self?.connecting = nil guard let self, !self.closed else { await socket.close(code: 1000, reason: "") return @@ -107,6 +112,8 @@ final class TransportConnection: SocketConnection { events = nil outgoing.yield(.close(code ?? 1000)) outgoing.finish() + connecting?.cancel() + connecting = nil } private func finish(_ close: SocketClosed) { diff --git a/swift/Sources/Zero/IVM/DeferredInput.swift b/swift/Sources/Zero/IVM/DeferredInput.swift index 4bd0e0ab79..dff168ae93 100644 --- a/swift/Sources/Zero/IVM/DeferredInput.swift +++ b/swift/Sources/Zero/IVM/DeferredInput.swift @@ -46,8 +46,15 @@ package final class DeferredInput: Input { return } input.setOutput(output) - for node in input.fetch(FetchRequest()) { - output.push(.add(node), pusher: input) + do { + try PipelineFailure.capture { + for node in input.fetch(FetchRequest()) { + output.push(.add(node), pusher: input) + } + } + } catch { + input.destroy() + throw error } output.reconcile(pusher: input) self.input = input diff --git a/swift/Sources/Zero/IVM/MemorySource.swift b/swift/Sources/Zero/IVM/MemorySource.swift index 636204b8dd..761fed9dda 100644 --- a/swift/Sources/Zero/IVM/MemorySource.swift +++ b/swift/Sources/Zero/IVM/MemorySource.swift @@ -282,7 +282,10 @@ package final class MemorySource: Source { let index = primaryIndex let canBulkLoad = connections.isEmpty && indexes.count == 1 && index.data.isEmpty if !canBulkLoad { - for row in rows { push(.add(row)) } + for row in rows { + if PipelineFailure.hasFailed { return } + push(.add(row)) + } return } let compare = index.comparator.compareRows @@ -311,6 +314,7 @@ package final class MemorySource: Source { if shouldSplitEdit { pushEpoch += 1 genPushAndWrite(.remove(oldRow), epoch: pushEpoch) + if PipelineFailure.hasFailed { return } pushEpoch += 1 genPushAndWrite(.add(row), epoch: pushEpoch) return @@ -322,6 +326,8 @@ package final class MemorySource: Source { private func genPushAndWrite(_ change: SourceChange, epoch: Int) { genPush(change, epoch: epoch) + // JS's throw skips the write. + if PipelineFailure.hasFailed { return } writeChange(change) } @@ -353,6 +359,8 @@ package final class MemorySource: Source { case .remove(let row): .remove(Node(row: row)) } filterPush(outputChange, output, conn.input, conn.filters?.predicate) + // JS's throw ends the push here, before the other connections. + if PipelineFailure.hasFailed { return } } i = 0 while i < connections.count { diff --git a/swift/Sources/Zero/IVM/Take.swift b/swift/Sources/Zero/IVM/Take.swift index 1996367427..34dd7b1d43 100644 --- a/swift/Sources/Zero/IVM/Take.swift +++ b/swift/Sources/Zero/IVM/Take.swift @@ -61,11 +61,14 @@ final class Take: Operator, TakeBoundProvider { func fetch(_ req: FetchRequest) -> NodeStream { NodeStream { [self] in - precondition( - partitionKey == nil - || (req.constraint != nil - && constraintContainsPartitionKey(req.constraint, partitionKey)), - "Partitioned take does not allow unpartitioned fetches") + // A flipped join fetches its child without a constraint, so a limited + // flipped EXISTS gets here. JS throws, and `materialize` with it. + if let partitionKey, + req.constraint == nil || !constraintContainsPartitionKey(req.constraint, partitionKey) + { + PipelineFailure.report("Partitioned take does not allow unpartitioned fetches") + return AnyIterator { nil } + } let key = takeStateKey(req.constraint) guard let state = storage[key] else { if constraintMatchesPartitionKey(req.constraint, partitionKey) { diff --git a/swift/Sources/Zero/IVM/View.swift b/swift/Sources/Zero/IVM/View.swift index 8142d34295..e95bb7a3b6 100644 --- a/swift/Sources/Zero/IVM/View.swift +++ b/swift/Sources/Zero/IVM/View.swift @@ -111,10 +111,12 @@ enum ViewApply { case .add(let node): if format.singular { if case .one(var old?)? = parent.relationships[relationship] { - precondition( - schema.compareRows(old.row, node.row) == 0, - "Singular relationship '\(relationship)' should not have multiple rows. You may need to declare this relationship with the `many` helper instead of the `one` helper in your schema." - ) + // Server data can do this, with two rows for a `one` relationship. + guard schema.compareRows(old.row, node.row) == 0 else { + return fail( + "Singular relationship '\(relationship)' should not have multiple rows. You may need to declare this relationship with the `many` helper instead of the `one` helper in your schema." + ) + } old.refCount += 1 parent.relationships[relationship] = .one(old) } else { @@ -139,7 +141,7 @@ enum ViewApply { case .remove(let node): if format.singular { guard case .one(var old?)? = parent.relationships[relationship] else { - preconditionFailure("node does not exist") + return fail("node does not exist") } if old.refCount == 1 { parent.relationships[relationship] = .one(nil) @@ -150,7 +152,10 @@ enum ViewApply { } else { var list = takeList(&parent, relationship) let pos = search(list, node.row, schema.compareRows) - precondition(pos >= 0, "node does not exist") + guard pos >= 0 else { + parent.relationships[relationship] = .many(list) + return fail("node does not exist") + } if list[pos].refCount == 1 { list.remove(at: pos) } else { @@ -168,8 +173,11 @@ enum ViewApply { return false // not in the view } if format.singular { - guard case .one(var existing?)? = parent.relationships.removeValue(forKey: relationship) - else { preconditionFailure("node does not exist") } + let removed = parent.relationships.removeValue(forKey: relationship) + guard case .one(var existing?)? = removed else { + parent.relationships[relationship] = removed + return fail("node does not exist") + } let changed = apply( &existing, child.change, childSchema, child.relationshipName, childFormat) parent.relationships[relationship] = .one(existing) @@ -177,7 +185,10 @@ enum ViewApply { } var list = takeList(&parent, relationship) let pos = search(list, node.row, schema.compareRows) - precondition(pos >= 0, "node does not exist") + guard pos >= 0 else { + parent.relationships[relationship] = .many(list) + return fail("node does not exist") + } let changed = apply( &list[pos], child.change, childSchema, child.relationshipName, childFormat) parent.relationships[relationship] = .many(list) @@ -186,7 +197,7 @@ enum ViewApply { case .edit(let node, let oldNode): if format.singular { guard case .one(var existing?)? = parent.relationships[relationship] else { - preconditionFailure("node does not exist") + return fail("node does not exist") } applyEdit(&existing, node.row) parent.relationships[relationship] = .one(existing) @@ -197,7 +208,7 @@ enum ViewApply { if schema.compareRows(oldNode.row, node.row) != 0 { // The sort key changed, so the row may move. let oldPos = search(list, oldNode.row, schema.compareRows) - precondition(oldPos >= 0, "old node does not exist") + guard oldPos >= 0 else { return fail("old node does not exist") } let rawPos = search(list, node.row, schema.compareRows) let found = rawPos >= 0 let pos = found ? rawPos : ~rawPos @@ -230,12 +241,19 @@ enum ViewApply { return true } let pos = search(list, oldNode.row, schema.compareRows) - precondition(pos >= 0, "node does not exist") + guard pos >= 0 else { return fail("node does not exist") } applyEdit(&list[pos], node.row) return true } } + /// Reports what JS asserts here (the view and its input disagree) and + /// leaves the tree as it was: the push or hydration then fails. + private static func fail(_ message: String) -> Bool { + PipelineFailure.report(message) + return false + } + /// Applies `.add` for each node, taking the list out of `parent` once /// rather than for each node. static func addAll( @@ -417,7 +435,11 @@ package final class ArrayView: Output { private func fireListeners() { let (data, type) = listenerArgs() - for (_, listener) in listeners { listener(data, type) } + forEachLive(listeners) { listener in + listener(data, type) + } next: { [unowned self] in + self.listeners + } } package func destroy() { onDestroy?() } @@ -502,3 +524,32 @@ extension Array { if i == count { append(entry) } else { insert(entry, at: i) } } } + +/// Calls `body` for each element of `elements()`, read again after every +/// call, as JS iterates a `Set` that changes while it is iterated: one +/// removed before its turn is skipped, one added is called in the same pass. +/// Elements are in increasing `id` order. +func forEachLive( + _ start: [(id: Int, listener: T)], _ body: (T) -> Void, + next elements: () -> [(id: Int, listener: T)] +) { + var current = start + var last = Int.min + while let i = firstIndex(after: last, in: current) { + let element = current[i] + last = element.id + body(element.listener) + current = elements() + } +} + +private func firstIndex(after id: Int, in elements: [(id: Int, listener: T)]) -> Int? { + // Ids increase along the array: binary search for the first above `id`. + var low = 0 + var high = elements.count + while low < high { + let mid = (low + high) / 2 + if elements[mid].id <= id { low = mid + 1 } else { high = mid } + } + return low < elements.count ? low : nil +} diff --git a/swift/Sources/Zero/JSON/JSNumber.swift b/swift/Sources/Zero/JSON/JSNumber.swift index 8efb789505..0c1aaecb87 100644 --- a/swift/Sources/Zero/JSON/JSNumber.swift +++ b/swift/Sources/Zero/JSON/JSNumber.swift @@ -156,6 +156,13 @@ package enum JSNumber { n >= -2_147_483_648 && n <= 2_147_483_647 && n == n.rounded(.towardZero) } + /// `Number.isSafeInteger(n)` in JS: an integer of at most 2^53 - 1 in + /// magnitude, which an `Int` holds exactly. + @inlinable + package static func isSafeInteger(_ n: Double) -> Bool { + n.magnitude <= 9_007_199_254_740_991 && n == n.rounded(.towardZero) + } + /// `ToInt32(n)` in JS, for an `n` that `isInt32` accepted. @inlinable package static func int32Bits(_ n: Double) -> UInt32 { diff --git a/swift/Sources/Zero/JSON/JSONDecoding.swift b/swift/Sources/Zero/JSON/JSONDecoding.swift index c97149d0ea..c5f4286ab7 100644 --- a/swift/Sources/Zero/JSON/JSONDecoding.swift +++ b/swift/Sources/Zero/JSON/JSONDecoding.swift @@ -3,18 +3,21 @@ package struct DecodingError: Error, CustomStringConvertible, Equatable { package var path: [String] package var message: String + /// What valita writes after the path, such as ` Got "x"`. + package var got: String - package init(_ message: String, path: [String] = []) { + package init(_ message: String, path: [String] = [], got: String = "") { self.message = message self.path = path + self.got = got } package var description: String { - path.isEmpty ? message : "\(message) at \(path.joined(separator: "."))" + (path.isEmpty ? message : "\(message) at \(path.joined(separator: "."))") + got } func at(_ key: String) -> DecodingError { - DecodingError(message, path: [key] + path) + DecodingError(message, path: [key] + path, got: got) } } diff --git a/swift/Sources/Zero/Protocol/Poke.swift b/swift/Sources/Zero/Protocol/Poke.swift index 3cbb0e0254..b2712693fe 100644 --- a/swift/Sources/Zero/Protocol/Poke.swift +++ b/swift/Sources/Zero/Protocol/Poke.swift @@ -60,14 +60,27 @@ package struct PokeStartBody: Sendable, Hashable, JSONCodable { } } +/// One client's entry in a poke part's `desiredQueriesPatches`. +package struct DesiredQueriesPatch: Sendable, Hashable { + package var clientID: JSKey + package var patch: QueriesPatch + + package init(clientID: JSKey, patch: QueriesPatch) { + self.clientID = clientID + self.patch = patch + } +} + /// One part of a poke (`pokePartBodySchema` in /// `packages/zero-protocol/src/poke.ts`). package struct PokePartBody: Sendable, Hashable, JSONCodable { package var pokeID: String /// The new last mutation ID of each client that changed. package var lastMutationIDChanges: [JSKey: Double]? - /// Changes to each client's desired queries, by client ID. - package var desiredQueriesPatches: [JSKey: QueriesPatch]? + /// Changes to each client's desired queries, in the order the server sent + /// the clients: a `clear` for one client and a `put` for another must apply + /// in that order. + package var desiredQueriesPatches: [DesiredQueriesPatch]? /// Changes to the queries whose rows `rowsPatch` syncs. package var gotQueriesPatch: QueriesPatch? package var rowsPatch: RowsPatch? @@ -75,7 +88,7 @@ package struct PokePartBody: Sendable, Hashable, JSONCodable { package init( pokeID: String, lastMutationIDChanges: [JSKey: Double]? = nil, - desiredQueriesPatches: [JSKey: QueriesPatch]? = nil, gotQueriesPatch: QueriesPatch? = nil, + desiredQueriesPatches: [DesiredQueriesPatch]? = nil, gotQueriesPatch: QueriesPatch? = nil, rowsPatch: RowsPatch? = nil, mutationsPatch: MutationsPatch? = nil ) { self.pokeID = pokeID @@ -90,7 +103,15 @@ package struct PokePartBody: Sendable, Hashable, JSONCodable { let r = try JSONObjectReader(json, "pokePart body") lastMutationIDChanges = try r.optional("lastMutationIDChanges", JSONDecode.numberRecord) desiredQueriesPatches = try r.optional("desiredQueriesPatches") { (v) throws(DecodingError) in - try JSONDecode.keyedRecord(v, JSONDecode.queriesPatch) + var out: [DesiredQueriesPatch] = [] + for (k, e) in try JSONDecode.object(v) { + do throws(DecodingError) { + out.append(DesiredQueriesPatch(clientID: JSKey(k), patch: try JSONDecode.queriesPatch(e))) + } catch { + throw error.at(k) + } + } + return out } gotQueriesPatch = try r.optional("gotQueriesPatch", JSONDecode.queriesPatch) rowsPatch = try r.optional("rowsPatch") { (v) throws(DecodingError) in @@ -107,7 +128,10 @@ package struct PokePartBody: Sendable, Hashable, JSONCodable { o.set("lastMutationIDChanges", lastMutationIDChanges.map(JSONValue.numberRecord)) o.set( "desiredQueriesPatches", - desiredQueriesPatches.map { JSONValue.object(JSONObject($0) { .array($0.map(\.json)) }) }) + desiredQueriesPatches.map { + JSONValue.object( + JSONObject($0.map { ($0.clientID.string, JSONValue.array($0.patch.map(\.json))) })) + }) o.set("gotQueriesPatch", gotQueriesPatch.map { JSONValue.array($0.map(\.json)) }) o.set("rowsPatch", rowsPatch.map { JSONValue.array($0.map(\.json)) }) o.set("mutationsPatch", mutationsPatch.map { JSONValue.array($0.map(\.json)) }) diff --git a/swift/Sources/Zero/Protocol/ProtocolDecoding.swift b/swift/Sources/Zero/Protocol/ProtocolDecoding.swift index c09af0e013..d5dd8b34fa 100644 --- a/swift/Sources/Zero/Protocol/ProtocolDecoding.swift +++ b/swift/Sources/Zero/Protocol/ProtocolDecoding.swift @@ -10,15 +10,32 @@ extension JSONDecode { } /// `v.literalUnion(...)` of the raw values of `R`. - static func literalUnion( + static func literalUnion( _ v: JSONValue, _: R.Type = R.self ) throws(DecodingError) -> R where R.RawValue == String { guard case .string(let s) = v, let r = R(rawValue: s) else { - throw DecodingError("Expected a \(R.self) literal, got \(v)") + // valita: `Expected literal value "A", "B" or "C" at Got "x"`. + let names = R.allCases.map { JSONSerializer.stringify(.string($0.rawValue)) } + let list = + names.count < 2 + ? names.joined() + : names.dropLast().joined(separator: ", ") + " or " + names.last! + throw DecodingError("Expected literal value \(list)", got: " Got \(valitaDisplay(v))") } return r } + /// `toDisplay` in valita.ts: how its messages show a value. + static func valitaDisplay(_ v: JSONValue?) -> String { + switch v { + case nil: "undefined" + case .null?: "null" + case .array?: "array" + case .object?: "object" + case let v?: JSONSerializer.stringify(v) + } + } + /// `schema.nullable()`. static func nullable( _ v: JSONValue, _ decode: (JSONValue) throws(DecodingError) -> T @@ -74,21 +91,21 @@ extension JSONDecode { extension JSONObjectReader { /// A required `v.literalUnion(...)` field. - func literalUnion( + func literalUnion( _ key: String, _ type: R.Type = R.self ) throws(DecodingError) -> R where R.RawValue == String { try required(key) { (v) throws(DecodingError) in try JSONDecode.literalUnion(v, type) } } /// An optional `v.literalUnion(...)` field. - func optionalLiteralUnion( + func optionalLiteralUnion( _ key: String, _ type: R.Type = R.self ) throws(DecodingError) -> R? where R.RawValue == String { try optional(key) { (v) throws(DecodingError) in try JSONDecode.literalUnion(v, type) } } /// A required field whose value is one of `allowed`. - func oneOf( + func oneOf( _ key: String, _ allowed: Set ) throws(DecodingError) -> R where R.RawValue == String { try required(key) { (v) throws(DecodingError) in diff --git a/swift/Sources/Zero/Protocol/ProtocolEnums.swift b/swift/Sources/Zero/Protocol/ProtocolEnums.swift index 94e2aab85f..5d2f3ba13f 100644 --- a/swift/Sources/Zero/Protocol/ProtocolEnums.swift +++ b/swift/Sources/Zero/Protocol/ProtocolEnums.swift @@ -1,6 +1,7 @@ /// The kind of a protocol error /// (`packages/zero-protocol/src/error-kind-enum.ts`). package enum ErrorKind: String, Sendable, Hashable, CaseIterable { + // In the order of `errorKindSchema`, which valita's messages list. case authInvalidated = "AuthInvalidated" case clientNotFound = "ClientNotFound" case invalidConnectionRequest = "InvalidConnectionRequest" @@ -9,17 +10,17 @@ package enum ErrorKind: String, Sendable, Hashable, CaseIterable { case invalidConnectionRequestClientDeleted = "InvalidConnectionRequestClientDeleted" case invalidMessage = "InvalidMessage" case invalidPush = "InvalidPush" - case pushFailed = "PushFailed" - case mutationFailed = "MutationFailed" case mutationRateLimited = "MutationRateLimited" - case rebalance = "Rebalance" - case rehome = "Rehome" - case transformFailed = "TransformFailed" + case mutationFailed = "MutationFailed" case unauthorized = "Unauthorized" case versionNotSupported = "VersionNotSupported" case schemaVersionNotSupported = "SchemaVersionNotSupported" - case serverOverloaded = "ServerOverloaded" case `internal` = "Internal" + case rebalance = "Rebalance" + case rehome = "Rehome" + case serverOverloaded = "ServerOverloaded" + case pushFailed = "PushFailed" + case transformFailed = "TransformFailed" /// The kinds of a basic error body (`basicErrorKindSchema` in /// `packages/zero-protocol/src/error.ts`). diff --git a/swift/Sources/Zero/Query/AnyQuery.swift b/swift/Sources/Zero/Query/AnyQuery.swift index b502d9be9e..c462b59076 100644 --- a/swift/Sources/Zero/Query/AnyQuery.swift +++ b/swift/Sources/Zero/Query/AnyQuery.swift @@ -37,9 +37,11 @@ public struct StaticParameter: Sendable, Hashable { /// named by string; the typed builder wraps this one. /// /// The AST is always normalized: each step builds its part of the AST in -/// normalized form. Misuse that JS reports by throwing, such as an unknown -/// relationship or a `limit` inside a junction relationship, is a -/// programming error here and stops the program. +/// normalized form. Misuse that JS reports by throwing from the builder, +/// such as a negative `limit` or a `limit` inside a junction relationship, +/// leaves that step out and makes the query invalid: materializing, +/// running, or preloading it throws JS's message. An unknown relationship +/// is a programming error and stops the program. public struct AnyQuery: Sendable { public let schema: ZeroSchema public let table: String @@ -49,6 +51,8 @@ public struct AnyQuery: Sendable { public let customQueryID: CustomQueryID? /// The two-hop relationship this query is the destination of, if any. let currentJunction: String? + /// Why the query is invalid: the first misuse JS's builder throws for. + let invalid: String? public init(_ schema: ZeroSchema, _ table: String, system: AST.System = .client) { self.init( @@ -58,7 +62,7 @@ public struct AnyQuery: Sendable { init( schema: ZeroSchema, table: String, ast: AST, format: Format, system: AST.System, - customQueryID: CustomQueryID?, currentJunction: String? + customQueryID: CustomQueryID?, currentJunction: String?, invalid: String? = nil ) { self.schema = schema self.table = table @@ -67,14 +71,21 @@ public struct AnyQuery: Sendable { self.system = system self.customQueryID = customQueryID self.currentJunction = currentJunction + self.invalid = invalid } - private func with(ast: AST? = nil, format: Format? = nil) -> AnyQuery { + private func with( + ast: AST? = nil, format: Format? = nil, invalid: String? = nil + ) -> AnyQuery { AnyQuery( schema: schema, table: table, ast: ast ?? self.ast, format: format ?? self.format, - system: system, customQueryID: customQueryID, currentJunction: currentJunction) + system: system, customQueryID: customQueryID, currentJunction: currentJunction, + invalid: self.invalid ?? invalid) } + /// This query, unchanged but invalid: a builder step JS throws for. + private func invalidated(_ message: String) -> AnyQuery { with(invalid: message) } + private func base(_ table: String, alias: String, format: Format, junction: String?) -> AnyQuery { AnyQuery( schema: schema, table: table, ast: AST(table: table, alias: alias), format: format, @@ -91,7 +102,8 @@ public struct AnyQuery: Sendable { public func nameAndArgs(_ name: String, _ args: [JSONValue]) -> AnyQuery { AnyQuery( schema: schema, table: table, ast: ast, format: format, system: system, - customQueryID: CustomQueryID(name: name, args: args), currentJunction: currentJunction) + customQueryID: CustomQueryID(name: name, args: args), currentJunction: currentJunction, + invalid: invalid) } /// At most one row; the view holds the row or nothing rather than a list. @@ -120,7 +132,9 @@ public struct AnyQuery: Sendable { } public func `where`(_ factory: (ExpressionBuilder) -> AST.Condition) -> AnyQuery { - `where`(factory(ExpressionBuilder(self))) + let builder = ExpressionBuilder(self) + let condition = factory(builder) + return `where`(condition).with(invalid: builder.invalid.message) } public func `where`(_ condition: AST.Condition) -> AnyQuery { @@ -153,10 +167,10 @@ public struct AnyQuery: Sendable { } public func limit(_ limit: Int) -> AnyQuery { - precondition(limit >= 0, "Limit must be non-negative") - precondition(Int32(exactly: limit) != nil, "Limit must be an integer") + if limit < 0 { return invalidated("Limit must be non-negative") } + if Int32(exactly: limit) == nil { return invalidated("Limit must be an integer") } if let currentJunction { - preconditionFailure( + return invalidated( "Limit is not supported in junction relationships yet. Junction relationship being limited: \(currentJunction)" ) } @@ -167,7 +181,7 @@ public struct AnyQuery: Sendable { public func orderBy(_ field: String, _ direction: AST.Direction) -> AnyQuery { if let currentJunction { - preconditionFailure( + return invalidated( "Order by is not supported in junction relationships yet. Junction relationship being ordered: \(currentJunction)" ) } @@ -185,6 +199,7 @@ public struct AnyQuery: Sendable { let hops = relationshipHops(relationship) var ast = self.ast var format = self.format + var invalid: String? switch hops.count { case 1: let hop = hops[0] @@ -197,6 +212,7 @@ public struct AnyQuery: Sendable { AST.CorrelatedSubquery( correlation: correlation(hop), subquery: q.ast, system: system)) format.relationships[relationship] = q.format + invalid = q.invalid case 2: let (first, second) = (hops[0], hops[1]) let q = sub( @@ -214,16 +230,18 @@ public struct AnyQuery: Sendable { AST.CorrelatedSubquery( correlation: correlation(first), subquery: junction, system: system, hidden: true)) format.relationships[relationship] = q.format + invalid = q.invalid default: preconditionFailure("Invalid relationship \(relationship)") } - return with(ast: ast, format: format) + return with(ast: ast, format: format, invalid: invalid) } /// `#exists`: the condition `exists(relationship)` adds. + /// Also returns why the subquery is invalid, if it is. func existsCondition( _ relationship: String, flip: Bool?, scalar: Bool?, _ sub: ((AnyQuery) -> AnyQuery)? - ) -> AST.Condition { + ) -> (AST.Condition, invalid: String?) { let sub = sub ?? { $0 } let hops = relationshipHops(relationship) switch hops.count { @@ -233,10 +251,12 @@ public struct AnyQuery: Sendable { base( hop.destSchema, alias: subqueryAliasPrefix + relationship, format: Format(), junction: nil)) - return .correlatedSubquery( - related: AST.CorrelatedSubquery( - correlation: correlation(hop), subquery: q.ast, system: system), - op: .exists, flip: flip, scalar: scalar) + return ( + .correlatedSubquery( + related: AST.CorrelatedSubquery( + correlation: correlation(hop), subquery: q.ast, system: system), + op: .exists, flip: flip, scalar: scalar), q.invalid + ) case 2: let (first, second) = (hops[0], hops[1]) let q = sub( @@ -251,10 +271,12 @@ public struct AnyQuery: Sendable { table: first.destSchema, alias: subqueryAliasPrefix + relationship, where: AST.normalizeCondition(inner)) // The junction hop carries `flip` but not `scalar`, as in JS. - return .correlatedSubquery( - related: AST.CorrelatedSubquery( - correlation: correlation(first), subquery: junction, system: system), - op: .exists, flip: flip, scalar: nil) + return ( + .correlatedSubquery( + related: AST.CorrelatedSubquery( + correlation: correlation(first), subquery: junction, system: system), + op: .exists, flip: flip, scalar: nil), q.invalid + ) default: preconditionFailure("Invalid relationship \(relationship)") } @@ -294,7 +316,13 @@ func insertRelated( /// Builds the conditions of a `where` (`ExpressionBuilder` in /// `packages/zql/src/query/expression.ts`). public struct ExpressionBuilder: Sendable { + /// Why a subquery of the conditions built is invalid, for `where`. + final class Invalid: @unchecked Sendable { + var message: String? + } + let query: AnyQuery + let invalid = Invalid() init(_ query: AnyQuery) { self.query = query } @@ -349,7 +377,9 @@ public struct ExpressionBuilder: Sendable { _ relationship: String, flip: Bool? = nil, scalar: Bool? = nil, _ sub: ((AnyQuery) -> AnyQuery)? = nil ) -> AST.Condition { - query.existsCondition(relationship, flip: flip, scalar: scalar, sub) + let (condition, message) = query.existsCondition(relationship, flip: flip, scalar: scalar, sub) + if invalid.message == nil { invalid.message = message } + return condition } } diff --git a/swift/Sources/Zero/Query/Predicate.swift b/swift/Sources/Zero/Query/Predicate.swift index 31ad4fca8d..05baa8c15e 100644 --- a/swift/Sources/Zero/Query/Predicate.swift +++ b/swift/Sources/Zero/Query/Predicate.swift @@ -7,6 +7,41 @@ package struct PipelineError: Error, CustomStringConvertible { package init(_ description: String) { self.description = description } } +/// An error an operator finds while a pipeline is fetched, where JS throws +/// from inside the fetch and the error comes out of whatever hydrates the +/// view (`materialize`, `run`). Fetches are lazy streams that can't throw, +/// so the operator reports the error here and yields nothing, and the code +/// that hydrates throws it once the fetch is done. +package final class PipelineFailure: @unchecked Sendable { + private var error: PipelineError? + + @TaskLocal private static var current: PipelineFailure? + + /// Runs `body`, and throws the first error an operator reported while it + /// ran. + package static func capture(_ body: () throws -> T) throws -> T { + let failure = PipelineFailure() + let result = try $current.withValue(failure) { try body() } + if let error = failure.error { throw error } + return result + } + + /// Whether an operator reported an error to the running `capture`. Where + /// JS's throw would unwind, the source stops pushing. + static var hasFailed: Bool { current?.error != nil } + + /// Reports `message` to the running `capture`. The client fetches and + /// pushes only inside one; anything else (a pipeline driven directly) + /// gets the error logged, and the operator yields nothing. + static func report(_ message: String) { + guard let current else { + DefaultLogSink().log(level: .error, message: message, context: [:]) + return + } + if current.error == nil { current.error = PipelineError(message) } + } +} + /// `createPredicate(condition)` for a condition without subqueries. func createPredicate(_ condition: AST.Condition) throws(PipelineError) -> RowPredicate { switch condition { diff --git a/swift/Sources/Zero/Query/ZeroValue.swift b/swift/Sources/Zero/Query/ZeroValue.swift index c69cefd025..86b23431e0 100644 --- a/swift/Sources/Zero/Query/ZeroValue.swift +++ b/swift/Sources/Zero/Query/ZeroValue.swift @@ -63,7 +63,7 @@ extension Int: ZeroValue { @inlinable public init(zeroValue: JSONValue) throws(ZeroValueError) { guard case .number(let n) = zeroValue, let i = Int(exactly: n), - abs(i) <= 1 << 53 + i.magnitude <= 1 << 53 else { throw .mismatch("an integer", zeroValue) } self = i } diff --git a/swift/Sources/Zero/Storage/ReplicaDatabase.swift b/swift/Sources/Zero/Storage/ReplicaDatabase.swift index 75a2ff366f..ff1104bef1 100644 --- a/swift/Sources/Zero/Storage/ReplicaDatabase.swift +++ b/swift/Sources/Zero/Storage/ReplicaDatabase.swift @@ -1,4 +1,6 @@ +import CSQLite import Dispatch +import Synchronization /// Where a client keeps its replica between launches. public enum ZeroStorage: Sendable, Hashable { @@ -136,6 +138,8 @@ enum StorageOp: Sendable { group: String, changes: [(String, JSONValue?)], cleared: Bool, cookie: JSONValue, lastMutationIDs: [JSKey: Double]) case addMutation(group: String, StoredMutation) + /// Drops the mutations the IDs confirm, keeping the snapshot. + case deleteConfirmedMutations(group: String, lastMutationIDs: [JSKey: Double]) } /// The database a client stores its replica in, shared by the clients of @@ -172,6 +176,36 @@ final class ReplicaDatabase { private static var open: [String: ReplicaDatabase] = [:] /// The profile ID of memory databases, one per process as in JS. private static var memoryProfileID: String? + /// The names of the memory databases clients fell back to after their + /// SQLite store failed (`fellBack` in `mem-fallback-store.ts`), with the + /// directories they stand in for. Drops from those directories drop + /// these too. + private static var fellBack: [String: Set] = [:] + /// When each SQLite file was last opened or closed in this process, as a + /// count of such events, so the collector skips a file used after it + /// started reading the directory. + private static var lastUsed: [String: Int] = [:] + private static var uses = 0 + /// The cookie of each client group's stored snapshot. + private var snapshotCookies: [String: JSONValue] = [:] + + /// The stored data doesn't decode, so the client state is lost + /// (`InvalidRefCountError` in JS): the database is dropped. + struct CorruptData: Error, CustomStringConvertible { + let description: String + } + + /// A failure reading the database: a storage failure, unless the data + /// read doesn't decode. + private static func readFailure(_ error: any Error) -> any Error { + if error is SQLiteError || error is StorageFailure { return StorageFailure(error) } + return CorruptData(description: String(describing: error)) + } + + private static func markUsed(_ path: String) { + lastUsed[path] = uses + uses += 1 + } private init(name: String, key: String, sqlite: SQLiteStore?) { self.name = name @@ -182,11 +216,14 @@ final class ReplicaDatabase { /// Opens the database named `name` in `storage`, or the one already open /// in this process. A database that can't be opened is replaced by the - /// memory one of its name, and the failure returned. - static func open(_ storage: ZeroStorage, name: String) -> (ReplicaDatabase, StorageFailure?) { + /// memory one of its name, and the failure returned. A file whose data + /// doesn't decode is deleted and a new one started, and `corrupt` is set. + static func open( + _ storage: ZeroStorage, name: String + ) -> (ReplicaDatabase, StorageFailure?, corrupt: Bool) { switch storage { case .memory: - return (memory(name: name), nil) + return (memory(name: name), nil, false) case .sqlite(let directory): do { let dir = try directory ?? defaultDirectory() @@ -194,23 +231,44 @@ final class ReplicaDatabase { let path = dir + "/" + fileName(name) if let db = open[path] { db.users += 1 - return (db, nil) + return (db, nil, false) } - let store = try SQLiteStore(path: path, name: name, directory: dir) - let db = ReplicaDatabase(name: name, key: path, sqlite: store) + var corrupt = false + let db: ReplicaDatabase do { - try store.sync { try db.load(from: $0) } - } catch { - store.close() - throw error + db = try load(path: path, name: name, directory: dir) + } catch is CorruptData { + corrupt = true + _ = SQLiteStore.unlinkDatabase(path) + db = try load(path: path, name: name, directory: dir) } db.users = 1 open[path] = db - return (db, nil) + markUsed(path) + return (db, nil, corrupt) } catch { - return (memory(name: name), StorageFailure(error)) + return (fallback(name: name, storage: storage), StorageFailure(error), false) + } + } + } + + private static func load(path: String, name: String, directory: String) throws -> ReplicaDatabase + { + let store = try SQLiteStore(path: path, name: name, directory: directory) + let db = ReplicaDatabase(name: name, key: path, sqlite: store) + do { + try store.sync { connection in + do { + try db.load(from: connection) + } catch { + throw readFailure(error) + } } + } catch { + _ = store.close() + throw error } + return db } /// The memory database named `name`, which lives for the process and is @@ -220,6 +278,15 @@ final class ReplicaDatabase { shared(key: "memory:" + name, name: name, sqlite: nil) } + /// The memory database a client whose SQLite database in `storage` + /// failed runs on. + static func fallback(name: String, storage: ZeroStorage) -> ReplicaDatabase { + if case .sqlite(let directory) = storage { + fellBack[name, default: []].insert(directory ?? (try? defaultDirectory()) ?? "") + } + return memory(name: name) + } + private static func shared(key: String, name: String, sqlite: SQLiteStore?) -> ReplicaDatabase { if let db = open[key] { db.users += 1 @@ -240,8 +307,11 @@ final class ReplicaDatabase { func release() { users -= 1 guard users == 0, let sqlite else { return } - if Self.open[key] === self { Self.open[key] = nil } - sqlite.close() + if Self.open[key] === self { + Self.open[key] = nil + Self.markUsed(key) + } + if let error = sqlite.close() { fail(StorageFailure(error)) } } /// Records that `clientID` runs until `clientStopped`. `onDelete` gets @@ -267,8 +337,14 @@ final class ReplicaDatabase { running.filter { $0.clientGroupID == id }.map(\.clientID) } - /// Waits for the writes queued so far. - func flush() { sqlite?.flush() } + /// Waits for the writes queued so far, and writes those kept because the + /// database was busy. If they still can't be written, that is a storage + /// failure. + func flush() { + guard let sqlite else { return } + if let error = sqlite.flush() { fail(StorageFailure(error)) } + Self.finishCollections(of: sqlite) + } private func load(from db: SQLiteDatabase) throws { try db.query("SELECT id, mutator_names, disabled FROM client_groups ORDER BY seq") { row in @@ -287,15 +363,17 @@ final class ReplicaDatabase { profileID = try SQLiteStore.profileID(in: sqlite!.directory) } - /// The snapshot and pending mutations of group `id`. - private func loadGroup(_ id: String) throws(StorageFailure) -> StoredGroup { + /// The snapshot and pending mutations of group `id`. Throws + /// `CorruptData` when they don't decode, and a `StorageFailure` when they + /// can't be read. + private func loadGroup(_ id: String) throws -> StoredGroup { if let memoryGroups { return memoryGroups[id] ?? StoredGroup() } guard let sqlite else { return StoredGroup() } do { return try sqlite.sync { try SQLiteStore.loadGroup(id, from: $0) } } catch { - let failure = StorageFailure(error) - fail(failure) + let failure = Self.readFailure(error) + if let failure = failure as? StorageFailure { fail(failure) } throw failure } } @@ -303,6 +381,7 @@ final class ReplicaDatabase { /// Applies `ops` to the in-memory state now and queues them for SQLite. func apply(_ ops: [StorageOp]) { guard !dropped else { return } + let ops = ops.map(skippingOlderSnapshot) for op in ops { applyToMirror(op) } if memoryGroups != nil { for op in ops { applyToMemory(op) } @@ -329,6 +408,19 @@ final class ReplicaDatabase { for (_, listener) in writeFailureListeners { listener(f) } } + /// A snapshot no newer than the stored one, from a client of the group + /// that is behind another, isn't written: only the mutations it confirms + /// are deleted (`persistDD31` persists a snapshot only when + /// `compareCookiesForSnapshots` says it is newer). + private func skippingOlderSnapshot(_ op: StorageOp) -> StorageOp { + guard case .updateSnapshot(let group, _, _, let cookie, let lmids) = op else { return op } + if let stored = snapshotCookies[group], compareCookies(cookie, stored) <= 0 { + return .deleteConfirmedMutations(group: group, lastMutationIDs: lmids) + } + snapshotCookies[group] = cookie + return op + } + private func applyToMirror(_ op: StorageOp) { switch op { case .putGroup(let g): @@ -339,6 +431,7 @@ final class ReplicaDatabase { } case .deleteGroup(let id): groups.removeAll { $0.id == id } + snapshotCookies[id] = nil case .putClient(let c): if let i = clients.firstIndex(where: { $0.id == c.id }) { clients[i] = c @@ -352,7 +445,7 @@ final class ReplicaDatabase { deletedClients = pairs case .setProfileID(let id): profileID = id - case .updateSnapshot, .addMutation: + case .updateSnapshot, .addMutation, .deleteConfirmedMutations: break } } @@ -375,6 +468,8 @@ final class ReplicaDatabase { memoryGroups![group] = g case .addMutation(let group, let m): memoryGroups![group, default: StoredGroup()].mutations.append(m) + case .deleteConfirmedMutations(let group, let lmids): + memoryGroups![group]?.mutations.removeAll { $0.id <= lmids[JSKey($0.clientID)] ?? 0 } case .putClient, .deleteClients, .setDeletedClients, .setProfileID: break } @@ -385,11 +480,12 @@ final class ReplicaDatabase { /// Adds client `clientID` to the newest client group that registers the /// same mutators and isn't disabled, or to a new empty one /// (`initClientV6` with client group forking off). Returns the group and - /// what it holds. Throws, adding nothing, when the database failed or - /// the group can't be read. + /// what it holds. Throws, adding nothing, a `StorageFailure` when the + /// database failed or the group can't be read, and `CorruptData` when + /// what it holds doesn't decode. func initClient( _ clientID: String, mutatorNames: [String], now: Double - ) throws(StorageFailure) -> ( + ) throws -> ( clientGroupID: String, group: StoredGroup ) { if let failure { throw failure } @@ -402,6 +498,7 @@ final class ReplicaDatabase { }) { groupID = match.id group = try loadGroup(groupID) + snapshotCookies[groupID] = group.cookie } else { groupID = randomID() ops.append( @@ -465,10 +562,18 @@ final class ReplicaDatabase { } /// Deletes the database (`dropDatabase`). The next client to open it - /// starts empty; this one's later writes are dropped. + /// starts empty; this one's later writes are dropped. Dropping it again + /// does nothing: the file may be a new database by then. func drop() { + guard !dropped else { return } dropped = true - if Self.open[key] === self { Self.open[key] = nil } + // Another database of this name opened since owns the file now. + if let other = Self.open[key], other !== self { + _ = sqlite?.close() + return + } + Self.open[key] = nil + if sqlite != nil { Self.markUsed(key) } sqlite?.drop() } @@ -503,7 +608,16 @@ final class ReplicaDatabase { result.deleted.append(db.name) } guard case .sqlite = storage else { return result } + // The memory databases clients of this directory fell back to go too + // (`dropTargets`); those of `.memory` clients stay. let directory = String(prefix.dropLast()) + for (name, directories) in fellBack + where directories.contains(directory) && matches(replicacheName(name)) { + fellBack[name] = nil + guard let db = open["memory:" + name] else { continue } + db.drop() + if !result.deleted.contains(name) { result.deleted.append(name) } + } let files = await Task.detached { SQLiteStore.dropDatabases(in: directory) { matches(replicacheName($0)) } }.value @@ -512,13 +626,25 @@ final class ReplicaDatabase { return result } - /// Deletes, in the background, the other databases in this database's - /// directory whose clients all last ran more than `maxAge` ago. A - /// database open in this process is kept. + /// Deletes the other databases in this database's directory whose + /// clients all last ran more than `maxAge` ago (`collectIDBDatabases`). + /// They are found in the background; then, on the main actor, those + /// opened or closed in this process since are kept, and the others moved + /// out of the way before the background deletes them, so a client that + /// opens one meanwhile starts a new file rather than writing to a deleted + /// one. func collectDatabases(now: Double, maxAge: Double) { guard let sqlite, failure == nil, !dropped else { return } - let keep = Set(Self.open.keys) - sqlite.collectDatabases(keeping: keep, now: now, maxAge: maxAge) + sqlite.findCollectable(now: now, maxAge: maxAge, since: Self.uses) + } + + /// Deletes the databases `store`'s collections found. + static func finishCollections(of store: SQLiteStore) { + for found in store.takeCollectable() { + for path in found.paths where open[path] == nil && lastUsed[path, default: -1] < found.since { + SQLiteStore.retire(path) + } + } } // MARK: - Names and places @@ -561,6 +687,18 @@ final class SQLiteStore: @unchecked Sendable { private var failed = false /// The writes that found the database busy, written with the next. private var retrying: [StorageOp] = [] + /// The databases collections found, for the main actor to delete. + private let collectable = Mutex<[Collectable]>([]) + /// How many times `flush()` and `close()` try the writes kept because + /// the database was busy, each waiting out the busy timeout. + static let busyRetries = 1 + + /// Databases a collection found unused, and when it started, in + /// `ReplicaDatabase`'s count of opens and closes. + struct Collectable: Sendable { + let paths: [String] + let since: Int + } let path: String let directory: String @@ -622,7 +760,34 @@ final class SQLiteStore: @unchecked Sendable { } } - func flush() { queue.sync {} } + /// Waits for the queued writes, and writes those kept because the + /// database was busy. Returns the error when they still can't be + /// written; nothing more is written then. + func flush() -> (any Error)? { queue.sync { writeKept() } } + + /// Writes the ops kept because the database was busy, trying up to + /// `busyRetries` times. On the queue. + private func writeKept() -> (any Error)? { + guard let db, !failed, !retrying.isEmpty else { return nil } + var last: any Error = SQLiteError(code: SQLITE_BUSY, message: "database is locked") + for _ in 0.. (any Error)? { queue.sync { + let error = writeKept() db?.close() db = nil + return error } } @@ -678,7 +847,7 @@ final class SQLiteStore: @unchecked Sendable { return files } - private static func unlinkDatabase(_ path: String) -> Bool { + static func unlinkDatabase(_ path: String) -> Bool { var deleted = true for suffix in ["", "-wal", "-shm"] where unlink(path + suffix) != 0 && errno != ENOENT { deleted = false @@ -712,16 +881,30 @@ final class SQLiteStore: @unchecked Sendable { return result } - /// See `ReplicaDatabase.collectDatabases`. - func collectDatabases(keeping: Set, now: Double, maxAge: Double) { + /// Finds, in the background, the other databases of the directory no + /// client used for `maxAge`, for `ReplicaDatabase.finishCollections`. + /// Only databases of ours are considered: of this layout version or an + /// older one, with the name they hold, and of a format this client knows + /// (`gatherDatabaseInfoForCollect` never collects a newer format). + func findCollectable(now: Double, maxAge: Double, since: Int) { let directory = self.directory - queue.async { - for path in Self.databaseFiles(in: directory) { - guard !keeping.contains(path) else { continue } + let own = path + queue.async { [self] in + Self.deleteRetired(in: directory) + var paths: [String] = [] + for path in Self.databaseFiles(in: directory) where path != own { + guard let layout = Self.layoutVersion(ofFile: path), + layout <= ReplicaDatabase.sqliteLayoutVersion + else { continue } + var name: String? var recent = false do { let db = try SQLiteDatabase(path: path) defer { db.close() } + try db.query("SELECT value FROM meta WHERE key = 'name'") { name = $0.text(0) } + guard let name, let format = Self.formatVersion(ofName: name), + format <= ReplicaDatabase.formatVersion + else { continue } try db.query("SELECT max(heartbeat) FROM clients") { row in recent = now - row.real(0) < maxAge } @@ -729,9 +912,63 @@ final class SQLiteStore: @unchecked Sendable { // Not a database of ours, or busy: leave it. continue } - if !recent { _ = Self.unlinkDatabase(path) } + if !recent { paths.append(path) } + } + guard !paths.isEmpty else { return } + collectable.withLock { $0.append(Collectable(paths: paths, since: since)) } + Task { @MainActor [self] in ReplicaDatabase.finishCollections(of: self) } + } + } + + /// What the collections found since the last call. + func takeCollectable() -> [Collectable] { + collectable.withLock { found in + defer { found = [] } + return found + } + } + + /// The layout version in a file name `zero-v-.sqlite`. + static func layoutVersion(ofFile path: String) -> Int? { + let name = path.split(separator: "/").last.map(String.init) ?? path + guard name.hasPrefix("zero-v") else { return nil } + return Int(name.dropFirst("zero-v".count).prefix { $0 != "-" }) + } + + /// The Replicache format version in `rep:::`. + static func formatVersion(ofName name: String) -> Int? { + let parts = name.split(separator: ":", omittingEmptySubsequences: false) + guard name.hasPrefix("rep:"), parts.count >= 4 else { return nil } + return Int(parts[parts.count - 2]) + } + + private static let retiredMarker = ".collected-" + + /// Moves the database at `path` out of the way and deletes it in the + /// background. A rename, so a client opening `path` from now on starts a + /// new file, and cheap enough for the main actor. + static func retire(_ path: String) { + let retired = path + retiredMarker + randomID() + // The WAL first: a database without its WAL loses the latest commits, + // a new one with an old WAL would be worse. + for suffix in ["-wal", "-shm", ""] { _ = rename(path + suffix, retired + suffix) } + DispatchQueue.global(qos: .utility).async { _ = unlinkDatabase(retired) } + } + + /// Deletes the databases retired before the app last stopped. + private static func deleteRetired(in directory: String) { + guard let dir = opendir(directory) else { return } + var files: [String] = [] + while let entry = readdir(dir) { + let name = withUnsafeBytes(of: entry.pointee.d_name) { raw in + String(decoding: raw.prefix { $0 != 0 }, as: UTF8.self) + } + if name.hasPrefix("zero-"), name.contains(retiredMarker) { + files.append(directory + "/" + name) } } + closedir(dir) + for file in files { _ = unlink(file) } } private static func json(_ v: JSONValue) -> SQLiteDatabase.Value { @@ -797,6 +1034,12 @@ final class SQLiteStore: @unchecked Sendable { "DELETE FROM mutations WHERE client_group_id = ? AND client_id = ? AND id <= ?", [.text(group), .text(clientID.string), .real(lmid)]) } + case .deleteConfirmedMutations(let group, let lmids): + for (clientID, lmid) in lmids { + try db.run( + "DELETE FROM mutations WHERE client_group_id = ? AND client_id = ? AND id <= ?", + [.text(group), .text(clientID.string), .real(lmid)]) + } case .addMutation(let group, let m): try db.run( """ diff --git a/swift/Sources/Zero/Zero.docc/JSParity.md b/swift/Sources/Zero/Zero.docc/JSParity.md index 3bf236fbde..ed5ffe1898 100644 --- a/swift/Sources/Zero/Zero.docc/JSParity.md +++ b/swift/Sources/Zero/Zero.docc/JSParity.md @@ -127,16 +127,54 @@ is written or sent. - **The main actor.** Queries, views, and mutators run synchronously on the main actor. Views and live queries are up to date when `mutate` returns. - Only the network and storage are asynchronous. + Only the network and storage are asynchronous. The exception is a + `mutate` from a view listener: as a JS mutator commits after the + notification that ran it, it runs once the current notification and the + rest of its task are done, so every listener ends on the latest rows and + every view sees each change. Its `client` outcome settles then. +- **Queries that can't be built.** A query JS rejects when it is + materialized, such as a flipped `whereExists` over a subquery with a + `limit`, makes `materialize` and `liveQuery` throw and `run` reject with + the same message. Unlike JS, nothing stays registered with the server. +- **Updates a query can't take.** When applying a change makes a view's + pipeline fail where JS throws (such as a `one` relationship that two rows + match, or a flipped `whereExists` over a `limit` that a new row reaches), + the views keep what they got of the change, and from then on, as in JS, + the client's views can't be updated: a poke that fails puts the + connection in the `error` state, a mutation that fails is committed and + its `client` outcome is the error, and later mutations, `materialize`, + and `run` fail with the same message. - **Nulls in columns that can't be null.** When the server sends null for a column the schema says isn't optional, a `String`, `Double`, `Int`, or `Bool` property reads as `""`, `0`, or `false`. JS hands the null through. An enum or JSON column still fails to read. +- **Values of the wrong type for their column.** JS can't compare values + of different types, such as a number in a column of strings: a mutation + that writes one to a column a view sorts by fails, and so does every + mutation after it, and a poke that brings one disconnects the client + again after every reconnect. Swift sorts such a value with the others + (by type, then by its JSON) and keeps working. A mutator can only write + one through the untyped `tx.mutate.` calls; the server rejects + such a mutation, and its row goes when the next poke comes. +- **Error messages.** Messages about data the client can't read follow + valita's wording, as JS's do. A server frame that isn't JSON is the + exception: JS shows the engine's `JSON.parse` message, and Swift its + own. - **JSON objects.** A `JSONValue` object is a ``JSONObject``, which keeps its keys as a JS object does: integer-like keys first in ascending order, then the others in the order they were set. Keys are compared by UTF-16 code unit, so `"é"` and `"e\u{301}"` are two keys, as they are in JS. JSON text round-trips as `JSON.stringify` writes it. +- **Lone surrogates.** A Swift `String` can't hold half of a surrogate + pair, so JSON text with an escaped lone surrogate (`"\ud800"`) reads it + as U+FFFD, the replacement character; JS keeps the code unit. Two values + that differ only in lone surrogates, such as the keys `"\ud800"` and + `"\udc00"` of one object, are then the same value. +- **Mutation IDs.** A poke whose `lastMutationIDChanges` holds a number + that isn't a safe non-negative integer (`Number.isSafeInteger`) is + rejected as bad poke data: the client disconnects with a poke error. JS + takes any number. A stored pending mutation with such an ID is dropped + when the client opens, and logged as an error. - **JSON nesting depth.** JSON nested more than 1,500 arrays and objects deep is rejected ("JSON nested too deeply"); JS accepts whatever fits its stack, and zero-cache writes up to about 4,100 levels. A poke is about 5 @@ -161,3 +199,8 @@ is written or sent. - **Storage.** The replica is a SQLite database per user and URL, not IndexedDB. One process uses it at a time; there are no tabs to share it with. +- **Corrupt local data.** When the stored replica doesn't decode, JS drops + the database and calls `onClientStateNotFound`, which reloads the page by + default. Swift drops it and the client starts on a new, empty database at + once; `onClientStateNotFound` is called only if the app set it. Pending + mutations in the dropped database are lost, as in JS. diff --git a/swift/Sources/ZeroTesting/ManualScheduler.swift b/swift/Sources/ZeroTesting/ManualScheduler.swift index 2c3b3e9acf..b0462dd4c5 100644 --- a/swift/Sources/ZeroTesting/ManualScheduler.swift +++ b/swift/Sources/ZeroTesting/ManualScheduler.swift @@ -42,7 +42,7 @@ public final class ManualScheduler: Scheduler { ) -> any ScheduledTimer { - add(due: monotonicNow + max(0, ms), interval: nil, action) + add(due: monotonicNow + timerDelay(ms), interval: nil, action) } public func scheduleRepeating( @@ -50,7 +50,8 @@ public final class ManualScheduler: Scheduler { ) -> any ScheduledTimer { - add(due: monotonicNow + max(1, ms), interval: max(1, ms), action) + let interval = max(1, timerDelay(ms)) + return add(due: monotonicNow + interval, interval: interval, action) } private func add( diff --git a/swift/Sources/ZeroVirtual/VirtualizerCore.swift b/swift/Sources/ZeroVirtual/VirtualizerCore.swift index 85d51edc13..a731ff7a2c 100644 --- a/swift/Sources/ZeroVirtual/VirtualizerCore.swift +++ b/swift/Sources/ZeroVirtual/VirtualizerCore.swift @@ -580,7 +580,8 @@ package final class VirtualizerCore< private static func minPageSize(_ options: Options) -> Int { guard let min = options.minPageSize else { return defaultMinPageSize } - return makeEven(max(2, min)) + // At most 2^30, so a page query's limit (one more) fits a query limit. + return makeEven(Swift.min(max(2, min), 1 << 30)) } private static func restorable(_ options: Options) -> ScrollState? { @@ -894,8 +895,8 @@ package final class VirtualizerCore< private func updatePageSize() { let min = Self.minPageSize(options) let height = host?.viewportHeight ?? 0 - let size = - height > 0 ? max(min, makeEven(Int((height / rowEstimate).rounded(.up)) * 3)) : min + let rows = (height / rowEstimate).rounded(.up) * 3 + let size = height > 0 ? max(min, makeEven(rows < 0x1p30 ? Int(rows) : 1 << 30)) : min if size > pageSize { pageSize = size version += 1 diff --git a/swift/Tests/ConformanceTests/ClientApp.swift b/swift/Tests/ConformanceTests/ClientApp.swift index 65cc104c71..cea485cb52 100644 --- a/swift/Tests/ConformanceTests/ClientApp.swift +++ b/swift/Tests/ConformanceTests/ClientApp.swift @@ -19,6 +19,17 @@ enum ClientApp { "issues.byOwner": { args in zql("issue").where("ownerId", args ?? .null).orderBy("id", .asc) }, "issues.byID": { args in zql("issue").where("id", args ?? .null).related("owner").one() }, "issues.firstTwo": { _ in zql("issue").orderBy("title", .asc).limit(2) }, + "issues.withAuthoredComments": { _ in + zql("issue") + .whereExists("comments") { $0.whereExists("author", flip: true) { $0.limit(3) } } + .orderBy("id", .asc) + }, + "issues.withOneComment": { _ in zql("issue").related("oneComment").orderBy("id", .asc) }, + "issues.withFirstComment": { _ in + zql("issue") + .whereExists("comments", flip: true) { $0.orderBy("id", .asc).limit(1) } + .orderBy("id", .asc) + }, "users.all": { _ in zql("user").orderBy("id", .asc) }, "issueLabels.all": { _ in zql("issueLabel").orderBy("issueId", .asc).orderBy("labelId", .asc) @@ -116,6 +127,33 @@ enum ClientApp { try tx.mutate.issue.insert(issueRow(args)) throw ApplicationError("failed after writing") }, + // Runs CRUD operations on any table, then throws, reads tables back, or + // returns `return` (`harden.ops` in app.ts). + JSONMutator(name: "harden.ops") { tx, args in + if args?["failOnRebase"] == true, tx.reason == .rebase { + throw ApplicationError("fails on rebase") + } + for o in args?["ops"]?.arrayValue ?? [] { + let table = tx.mutate.table(o["table"]?.stringValue ?? "") + let value = Row(o["value"]?.objectValue ?? [:]) + switch o["op"]?.stringValue { + case "insert": try table.insert(value) + case "upsert": try table.upsert(value) + case "update": try table.update(value) + case "delete": try table.delete(value) + default: throw Failure("Unknown op") + } + } + if let fail = args?["fail"]?.stringValue { + throw ApplicationError(fail, details: args?["details"]) + } + if let read = args?["read"]?.arrayValue { + var out: JSONObject = [:] + for t in read.compactMap(\.stringValue) { out[t] = try tx.run(zql(t)).json ?? .null } + return ["mutationID": .number(Double(tx.mutationID)), "read": .object(out)] + } + return args?["return"] + }, JSONMutator(name: "comment.add") { tx, args in var row: Row = [ "authorId": field(args, "authorId", "u1"), diff --git a/swift/Tests/ConformanceTests/ClientConformanceTests.swift b/swift/Tests/ConformanceTests/ClientConformanceTests.swift index 76240b592b..af9e13cfa8 100644 --- a/swift/Tests/ConformanceTests/ClientConformanceTests.swift +++ b/swift/Tests/ConformanceTests/ClientConformanceTests.swift @@ -20,8 +20,8 @@ struct ClientConformanceTests { try Self.run(storage: .sqlite(directory: directory)) } - static func run(storage: ZeroStorage) throws { - let file = try Vectors.file("client") + static func run(storage: ZeroStorage, vectors: String = "client") throws { + let file = try Vectors.file(vectors) let startTime = file["shared"]?["startTime"]?.numberValue ?? 0 let only = ProcessInfo.processInfo.environment["ZERO_CLIENT_SCENARIO"] var failed = 0 @@ -39,7 +39,7 @@ struct ClientConformanceTests { passed += 1 } } - print("[client scenarios on \(storage)] \(passed) passed, \(failed) failed") + print("[\(vectors) scenarios on \(storage)] \(passed) passed, \(failed) failed") } static func firstMismatch( @@ -95,6 +95,15 @@ final class ScenarioRunner { var runs: [(String, Settled)] = [] var preloads: [(String, Settled, Settled, () -> Void)] = [] + /// A listener added by a `listen` step. + final class ListenerState { + var remove: () -> Void = {} + var calls: [JSONValue] = [] + var threw: String? + } + + var listeners: [(String, ListenerState)] = [] + let storage: ZeroStorage /// The scenario's own storage, as `#scenario-` in `harness.ts`. let scenarioKey: Int @@ -174,6 +183,7 @@ final class ScenarioRunner { placeholders[zero.clientGroupID] = "$clientGroupID" ids["$clientID"] = zero.clientID views = [] + listeners = [] retired = [] mutations = [] runs = [] @@ -312,6 +322,11 @@ final class ScenarioRunner { payload = Array(JSONSerializer.stringify(substitute(step["parts"] ?? [])).utf8) } try socket(step["socket"]).receive(.binary([PokeChunk.messageType] + payload)) + case "receiveText": + try socket(step["socket"]).receive(text: step["text"]!.stringValue!) + case "receiveBinary": + try socket(step["socket"]).receive( + .binary((step["bytes"]?.arrayValue ?? []).map { UInt8($0.numberValue!) })) case "socketClose": try socket(step["socket"]).serverClose( code: Int(step["code"]?.numberValue ?? 1006), reason: step["reason"]?.stringValue ?? "", @@ -320,12 +335,53 @@ final class ScenarioRunner { scheduler.advance(by: step["ms"]?.numberValue ?? 0) case "visibility": zero.setVisible(step["state"] == "visible") + case "onState": + let name = step["state"]!.stringValue! + let then = step["then"]! + var unsubscribe: (() -> Void)? + unsubscribe = zero.connection.subscribe { [weak self] state in + guard let self, state.status.rawValue == name else { return } + unsubscribe?() + unsubscribe = nil + if then["op"] == "close" { + self.zero.close() + } else { + self.zero.connection.connect(auth: then["auth"]?.stringValue) + } + } case "connect": zero.connection.connect(auth: step["auth"]?.stringValue) case "close": zero.close() case "restart": try restart(step) + case "listen": + let name = step["view"]!.stringValue! + guard let view = views.first(where: { $0.0 == name })?.1.view else { + throw ClientApp.Failure("No view \(name)") + } + let after = Int(step["after"]?.numberValue ?? 1) + var pending = step["actions"]?.arrayValue + let entry = ListenerState() + let listener = step["listener"]!.stringValue! + listeners.removeAll { $0.0 == listener } + listeners.append((listener, entry)) + var count = 0 + entry.remove = view.addListener { [unowned self] data, resultType, _ in + entry.calls.append(["data": data.json ?? .null, "resultType": .string(resultType.rawValue)]) + count += 1 + if count > after, let actions = pending { + pending = nil + for action in actions { + do { try self.perform(action) } catch { + entry.threw = entry.threw ?? errorMessage(error) + } + } + } + } + case "unlisten": + let name = step["listener"]!.stringValue! + listeners.first { $0.0 == name }?.1.remove() default: throw ClientApp.Failure("Unknown op \(op)") } @@ -412,6 +468,17 @@ final class ScenarioRunner { result["events"] = .array(events) events = [] } + if !listeners.isEmpty { + var o: JSONObject = [:] + for (name, l) in listeners { + var v: JSONObject = ["calls": .array(l.calls)] + if let threw = l.threw { v["threw"] = .string(threw) } + o[name] = .object(v) + l.calls = [] + l.threw = nil + } + result["listeners"] = .object(o) + } results.append(.object(result)) } zero.close() diff --git a/swift/Tests/ConformanceTests/HardenConnectionTests.swift b/swift/Tests/ConformanceTests/HardenConnectionTests.swift new file mode 100644 index 0000000000..9ccc6d7511 --- /dev/null +++ b/swift/Tests/ConformanceTests/HardenConnectionTests.swift @@ -0,0 +1,23 @@ +import Foundation +import Testing + +@testable import Zero + +/// Replays the connection hardening scenarios +/// (`packages/zero-conformance/src/client/scenarios/harden-connection.ts`, +/// `vectors/client-harden-connection.json`) with the client scenario runner. +/// Set `ZERO_CLIENT_SCENARIO` to run only the scenarios whose name contains it. +@Suite(.enabled(if: Vectors.available)) +@MainActor +struct HardenConnectionTests { + @Test func scenariosOnMemory() throws { + try ClientConformanceTests.run(storage: .memory, vectors: "client-harden-connection") + } + + @Test func scenariosOnSQLite() throws { + let directory = NSTemporaryDirectory() + "zero-harden-connection-" + UUID().uuidString + defer { try? FileManager.default.removeItem(atPath: directory) } + try ClientConformanceTests.run( + storage: .sqlite(directory: directory), vectors: "client-harden-connection") + } +} diff --git a/swift/Tests/ConformanceTests/HardenIVMTests.swift b/swift/Tests/ConformanceTests/HardenIVMTests.swift new file mode 100644 index 0000000000..5ee4332089 --- /dev/null +++ b/swift/Tests/ConformanceTests/HardenIVMTests.swift @@ -0,0 +1,210 @@ +import Testing + +@testable import Zero + +/// Replays the hardening IVM vectors from +/// `packages/zero-conformance/src/ivm/harden-ivm.test.ts` (written to +/// `generated/harden-ivm-*.json`): deep `related`/`whereExists` nesting, +/// junctions, `flip`/`scalar`, OR of EXISTS, batches of pushes flushed once, +/// and whether the view notified its listeners after each batch. +/// +/// Numbers JSON can't carry are tagged as `{"$number": "NaN"}` and so on. +@Suite(.enabled(if: Vectors.available)) +struct HardenIVMTests { + /// Errors JS throws that Swift throws too, rather than trapping on. + /// Errors JS throws from a push that Swift reports rather than trapping. + static let pushErrors = [ + "Partitioned take does not allow unpartitioned fetches", + "Singular relationship", + "node does not exist", + "old node does not exist", + ] + + static let comparableErrors = [ + "not(exists()) is not supported on the client", + "Partitioned take does not allow unpartitioned fetches", + "Singular relationship", + ] + + static func decode(_ v: JSONValue) -> JSONValue { + switch v { + case .array(let a): return .array(a.map(decode)) + case .object(let o): + if o.count == 1, let s = o["$number"]?.stringValue { + switch s { + case "NaN": return .number(.nan) + case "Infinity": return .number(.infinity) + case "-Infinity": return .number(-.infinity) + default: return .number(-0.0) + } + } + return .object(o.mapValues(decode)) + default: return v + } + } + + static func encode(_ v: JSONValue) -> JSONValue { + switch v { + case .number(let n) where n.isNaN: return ["$number": "NaN"] + case .number(let n) where n == .infinity: return ["$number": "Infinity"] + case .number(let n) where n == -.infinity: return ["$number": "-Infinity"] + case .number(let n) where n == 0 && n.sign == .minus: return ["$number": "-0"] + case .array(let a): return .array(a.map(encode)) + case .object(let o): return .object(o.mapValues(encode)) + default: return v + } + } + + static func positiveZero(_ v: JSONValue) -> JSONValue { + switch v { + case .number(let n) where n == 0: return .number(0) + case .array(let a): return .array(a.map(positiveZero)) + case .object(let o): return .object(o.mapValues(positiveZero)) + default: return v + } + } + + enum Outcome { case passed, skipped, failed(String) } + + static func run(_ c: JSONValue, schema: ZeroSchema, datasets: [JSONValue]) throws -> Outcome { + let result = c["result"]! + if let error = result["error"]?.stringValue, + !comparableErrors.contains(where: { error.hasPrefix($0) }) + && !(result["batch"] != nil && pushErrors.contains(where: { error.hasPrefix($0) })) + { + // A JS assertion where Swift has a precondition: not compared. + return .skipped + } + // JS threw from this batch's push: no steps were recorded. + let failingBatch = result["batch"]?.numberValue.map { Int($0) } + let dataset = datasets[Int(c["dataset"]!.numberValue!)] + var sources: [String: MemorySource] = [:] + for name in schema.tableNames { + let source = MemorySource(schema.tables[name]!) + for row in dataset["data"]?[name]?.arrayValue ?? [] { + source.push(.add(Row(decode(row).objectValue!))) + } + sources[name] = source + } + let built = IVMConformanceTests.build(schema, decode(c["program"]!)) + // With -0 read as 0: JS's `start` keeps 0 where Swift keeps -0, which + // JSON (and so the hash and the server) can't tell apart. + if positiveZero(built.ast.json) != positiveZero(decode(c["ast"]!)) { + return .failed("builder ast\n swift: \(encode(built.ast.json))\n js: \(c["ast"]!)") + } + let format = try Format(json: c["format"]!) + if built.format != format { + return .failed("builder format\n swift: \(built.format.json)\n js: \(c["format"]!)") + } + let recorder: IVMConformanceTests.Recorder + let view: ArrayView + var notified = 0 + do { + let input = try buildPipeline(built.ast, IVMConformanceTests.Delegate(sources), queryID: "q") + recorder = IVMConformanceTests.Recorder(input) + view = try PipelineFailure.capture { ArrayView(recorder, format: format, complete: true) } + view.addListener { _, _ in notified += 1 } + } catch { + if let expected = result["error"]?.stringValue { + return "\(error)" == expected ? .passed : .failed("error \(error) != \(expected)") + } + return .failed("unexpected error \(error)") + } + if let expected = result["error"], failingBatch == nil { + return .failed("expected error \(expected)") + } + func viewJSON() -> JSONValue { encode(view.data.json ?? .null) } + // `ViewSnapshot`: applying the difference to the previous IDs gives the + // new ones, and `updated` is the rows in both whose JSON changed. + var differ = RowDiffer(primaryKey: recorder.getSchema().primaryKey) + var previous = differ.next(view.data, .complete, nil) + func checkSnapshot() -> String? { + let next = differ.next(view.data, .complete, nil) + defer { previous = next } + guard previous.ids.applying(next.difference) == next.ids else { + return "snapshot difference: \(previous.ids) + \(next.difference) != \(next.ids)" + } + let old = Dictionary( + previous.data.entries.map { (RowID($0.row, primaryKey: differ.primaryKey), $0.json) }, + uniquingKeysWith: { a, _ in a }) + var updated = Set() + for e in next.data.entries { + let id = RowID(e.row, primaryKey: differ.primaryKey) + if let o = old[id], o != e.json { updated.insert(id) } + } + return updated == next.updated + ? nil : "snapshot updated: \(next.updated) != \(updated)" + } + if failingBatch == nil, viewJSON() != result["initial"]! { + return .failed("initial view\n swift: \(viewJSON())\n js: \(result["initial"]!)") + } + let steps = result["steps"]?.arrayValue ?? [] + for (i, batch) in dataset["batches"]!.arrayValue!.enumerated() { + recorder.changes.removeAll() + notified = 0 + do { + // As `QueryContext.processChanges` pushes. + try PipelineFailure.capture { + // As `IVMBranch.apply`, which stops at a failed push. + for push in batch.arrayValue! where !PipelineFailure.hasFailed { + let (table, change) = IVMConformanceTests.sourceChange(decode(push)) + sources[table]!.push(change) + } + } + } catch { + if i == failingBatch, "\(error)" == result["error"]!.stringValue! { return .passed } + return .failed("batch \(i): unexpected error \(error); js: \(result)") + } + if i == failingBatch { return .failed("batch \(i): expected error \(result["error"]!)") } + view.flush() + // Steps were recorded only when no push threw. + if failingBatch != nil { continue } + let step = steps[i] + let changes = encode(.array(recorder.changes)) + if changes != step["changes"]! { + return .failed( + "batch \(i) \(batch) changes\n swift: \(changes)\n js: \(step["changes"]!)") + } + if viewJSON() != step["data"]! { + return .failed( + "batch \(i) \(batch) view\n swift: \(viewJSON())\n js: \(step["data"]!)") + } + if let mismatch = checkSnapshot() { return .failed("batch \(i): \(mismatch)") } + if .bool(notified > 0) != step["notified"]! { + return .failed( + "batch \(i) \(batch) notified: swift \(notified > 0), js \(step["notified"]!)") + } + } + return .passed + } + + static func runAll(_ file: JSONValue, maxReported: Int = 10) throws { + let shared = file["shared"]! + let schema = try ZeroSchema(json: shared["schema"]!) + let datasets = shared["datasets"]!.arrayValue! + var failures = 0 + var passed = 0 + var skipped = 0 + for c in file["cases"]!.arrayValue! { + switch try run(c, schema: schema, datasets: datasets) { + case .passed: passed += 1 + case .skipped: skipped += 1 + case .failed(let mismatch): + failures += 1 + if failures <= maxReported { + Issue.record("\(c["name"]!.stringValue!): \(c["program"]!)\n\(mismatch)") + } + } + } + print("[harden ivm] \(passed) passed, \(failures) failed, \(skipped) skipped") + #expect(failures == 0, "\(failures) cases failed") + } + + @Test(.enabled(if: Vectors.generatedAvailable)) func hardenFuzz() throws { + try Self.runAll(try Vectors.generatedFile("harden-ivm-fuzz")) + } + + @Test(.enabled(if: Vectors.generatedAvailable)) func hardenSpecialNumbers() throws { + try Self.runAll(try Vectors.generatedFile("harden-ivm-special")) + } +} diff --git a/swift/Tests/ConformanceTests/HardenJSONTests.swift b/swift/Tests/ConformanceTests/HardenJSONTests.swift new file mode 100644 index 0000000000..818e6da765 --- /dev/null +++ b/swift/Tests/ConformanceTests/HardenJSONTests.swift @@ -0,0 +1,117 @@ +import Testing + +@testable import Zero + +/// Differential fuzz of the JSON parser and serializer, the downstream +/// message decoder, and the binary poke chunk decoder, against the vectors of +/// `packages/zero-conformance/src/primitives/harden-json.test.ts`: mutated +/// inputs must be accepted and rejected as JS does, without trapping. +@Suite(.enabled(if: Vectors.available)) +struct HardenJSONTests { + /// One case: `JSON.parse` acceptance, and `JSON.stringify` of the result. + /// JSON nested deeper than `JSONParser.maxDepth` is rejected by design + /// (JSParity.md), so that rejection is not a difference. + /// Returns the value parsed, if any, for the caller to free. + @discardableResult + static func checkJSON(_ c: JSONValue) -> JSONValue? { + let input = c["input"]!.stringValue! + let ok = c["ok"]!.boolValue! + let value: JSONValue + do throws(JSONParser.Error) { + value = try JSONParser.parse(input) + } catch { + #expect( + !ok || error.message == "JSON nested too deeply", + "rejected \(String(reflecting: input)): \(error)") + return nil + } + #expect(ok, "accepted \(String(reflecting: input))") + guard ok else { return value } + let expected = Self.replacingLoneSurrogateEscapes(c["stringify"]!.stringValue!) + let actual = JSONSerializer.stringify(value) + #expect( + actual.utf16.elementsEqual(expected), + "\(String(reflecting: input)): Swift \(actual.debugDescription), JS \(String(decoding: expected, as: UTF16.self).debugDescription)" + ) + return value + } + + /// `JSON.stringify` output with each escaped lone surrogate (`\ud800`), + /// which a Swift `String` can't hold, as the U+FFFD the parser reads it + /// as (`JSONParser`). + static func replacingLoneSurrogateEscapes(_ s: String) -> [UInt16] { + let units = Array(s.utf16) + var out: [UInt16] = [] + var i = 0 + while i < units.count { + guard units[i] == 0x5C, i + 1 < units.count else { + out.append(units[i]) + i += 1 + continue + } + if units[i + 1] == 0x75, i + 6 <= units.count, + let unit = UInt16(String(decoding: units[i + 2..] = [] + for m in messages { payloads.append(try PokeChunk.payload(ofBinaryMessage: m)) } + _ = try PokeChunk.decode(payloads) + #expect(ok, "accepted \(c["messages"]!)") + } catch { + #expect(!ok, "rejected \(c["messages"]!): \(error)") + } + } + } +} diff --git a/swift/Tests/ConformanceTests/HardenMutationTests.swift b/swift/Tests/ConformanceTests/HardenMutationTests.swift new file mode 100644 index 0000000000..152854be1d --- /dev/null +++ b/swift/Tests/ConformanceTests/HardenMutationTests.swift @@ -0,0 +1,41 @@ +import Foundation +import Testing +import ZeroTesting + +@testable import Zero + +/// Replays the mutation hardening scenarios +/// (`packages/zero-conformance/src/client/scenarios/harden-mutations.ts`, +/// vectors in `client-harden-mutations.json`) with the client scenario +/// runner, one test per scenario. +@Suite(.enabled(if: Vectors.available)) +@MainActor +struct HardenMutationConformanceTests { + nonisolated static let names: [String] = + ((try? Vectors.file("client-harden-mutations"))?["cases"]?.arrayValue ?? []) + .compactMap { $0["name"]?.stringValue } + + @Test(arguments: names) func onMemory(_ name: String) throws { + try Self.run(name, storage: .memory) + } + + @Test(arguments: names) func onSQLite(_ name: String) throws { + let directory = NSTemporaryDirectory() + "zero-harden-mutations-" + UUID().uuidString + defer { try? FileManager.default.removeItem(atPath: directory) } + try Self.run(name, storage: .sqlite(directory: directory)) + } + + static func run(_ name: String, storage: ZeroStorage) throws { + let file = try Vectors.file("client-harden-mutations") + let startTime = file["shared"]?["startTime"]?.numberValue ?? 0 + let scenario = try #require( + file["cases"]?.arrayValue?.first { $0["name"]?.stringValue == name }) + let runner = try ScenarioRunner(scenario, startTime: startTime, storage: storage) + let actual = runner.run() + if let mismatch = ClientConformanceTests.firstMismatch( + scenario, scenario["expect"]?.arrayValue ?? [], actual) + { + Issue.record(Comment(rawValue: "\(name)\n\(mismatch)")) + } + } +} diff --git a/swift/Tests/ConformanceTests/HardenQueryTests.swift b/swift/Tests/ConformanceTests/HardenQueryTests.swift new file mode 100644 index 0000000000..0e6b45be99 --- /dev/null +++ b/swift/Tests/ConformanceTests/HardenQueryTests.swift @@ -0,0 +1,24 @@ +import Foundation +import Testing + +@testable import Zero + +/// Replays the hardening client scenarios +/// (`packages/zero-conformance/src/client/scenarios/harden-queries.ts`): +/// listeners that re-enter the client from a notification, TTLs JS parses +/// oddly, query change throttling, and pokes that leave a view unchanged. +/// Set `ZERO_CLIENT_SCENARIO` to run the scenarios whose name contains it. +@Suite(.enabled(if: Vectors.available)) +@MainActor +struct HardenQueryTests { + @Test func scenariosOnMemory() throws { + try ClientConformanceTests.run(storage: .memory, vectors: "harden-queries") + } + + @Test func scenariosOnSQLite() throws { + let directory = NSTemporaryDirectory() + "zero-harden-scenarios-" + UUID().uuidString + defer { try? FileManager.default.removeItem(atPath: directory) } + try ClientConformanceTests.run( + storage: .sqlite(directory: directory), vectors: "harden-queries") + } +} diff --git a/swift/Tests/ConformanceTests/HardenTrapTests.swift b/swift/Tests/ConformanceTests/HardenTrapTests.swift new file mode 100644 index 0000000000..82591cde1f --- /dev/null +++ b/swift/Tests/ConformanceTests/HardenTrapTests.swift @@ -0,0 +1,72 @@ +import Testing + +@testable import Zero + +// Exit tests run on macOS and Linux only, not in the iOS simulator. +#if os(macOS) || os(Linux) + /// Queries the JS client rejects by throwing from `materialize` (an + /// `assert` in `buildPipeline` or in `Take.fetch`), so an app can catch the + /// error, where Swift stops the process with a `precondition`. Found by + /// `packages/zero-conformance/src/ivm/harden-ivm.test.ts`; the JS errors are + /// in the comments. Each case runs in an exit test, so a trap fails only + /// that test: the expected outcome is that the process survives. + @Suite(.enabled(if: Vectors.available)) + struct HardenTrapTests { + /// The error materializing `program` throws, if any. + @discardableResult + static func materialize(_ program: String) -> String? { + let schema = ClientApp.schema + var sources: [String: MemorySource] = [:] + for name in schema.tableNames { sources[name] = MemorySource(schema.tables[name]!) } + sources["issue"]!.push( + .add([ + "id": "i1", "title": "a", "description": "", "closed": false, "ownerId": "u1", + "createdAt": 1, + ])) + sources["user"]!.push(.add(["id": "u1", "name": "a", "metadata": .null])) + sources["comment"]!.push( + .add(["id": "c1", "authorId": "u1", "issueId": "i1", "text": "", "createdAt": 1])) + sources["revision"]!.push( + .add(["id": "r1", "authorId": "u1", "commentId": "c1", "text": ""])) + let query = IVMConformanceTests.build(schema, try! JSONParser.parse(Array(program.utf8))) + do { + let input = try buildPipeline( + query.ast, IVMConformanceTests.Delegate(sources), queryID: "q") + // As `materialize` hydrates. + _ = try PipelineFailure.capture { ArrayView(input, format: query.format, complete: true) } + } catch { + // What JS does: materialize throws. + return "\(error)" + } + return nil + } + + /// JS: `EXISTS subqueries must not have related` (thrown). Swift: + /// `precondition` in `Builder.swift:153`. + @Test func existsSubqueryWithRelatedThrowsRatherThanTraps() async { + await #expect(processExitsWith: .success) { + HardenTrapTests.materialize( + #"{"table":"revision","ops":[{"op":"whereExists","rel":"comment","sub":[{"op":"related","rel":"issue"}]}]}"# + ) + } + } + + static let flippedExistsOverLimit = + #"{"table":"issue","ops":[{"op":"whereExists","rel":"oneComment","sub":[{"op":"limit","n":2},{"op":"whereExists","rel":"issue"}],"flip":true}]}"# + + /// JS: `Partitioned take does not allow unpartitioned fetches` (thrown). + /// Swift trapped on a `precondition` in `Take.fetch`. + @Test func flippedExistsOverLimitedSubqueryThrowsRatherThanTraps() async { + await #expect(processExitsWith: .success) { + HardenTrapTests.materialize(HardenTrapTests.flippedExistsOverLimit) + } + } + + /// The same, with the message JS throws. + @Test func flippedExistsOverLimitedSubqueryThrowsJSError() { + #expect( + Self.materialize(Self.flippedExistsOverLimit) + == "Partitioned take does not allow unpartitioned fetches") + } + } +#endif diff --git a/swift/Tests/ConformanceTests/SweepWireTests.swift b/swift/Tests/ConformanceTests/SweepWireTests.swift index 5f52155a3d..27b856d518 100644 --- a/swift/Tests/ConformanceTests/SweepWireTests.swift +++ b/swift/Tests/ConformanceTests/SweepWireTests.swift @@ -82,7 +82,7 @@ struct SweepWireTests { } let text = poke(JSONParser.maxDepth - 5) #expect(throws: (any Error).self) { try Downstream(text: poke(JSONParser.maxDepth - 4)) } - let messages = try await onThread(stackSize: 512 << 10) { + let messages = try await Self.onThread(stackSize: 512 << 10) { let message = try Downstream(text: text) let encoded = JSONSerializer.stringify(message.json) let decoded = try Downstream(text: encoded) @@ -94,7 +94,7 @@ struct SweepWireTests { #expect(messages.encodedLength == text.utf8.count) #expect(messages.all[0] == messages.all[1]) // Freeing still recurses, in `Array`'s deinit, and fits as well. - try await onThread(stackSize: 512 << 10) { messages.all = [] } + try await Self.onThread(stackSize: 512 << 10) { messages.all = [] } } final class Messages: @unchecked Sendable { @@ -108,7 +108,7 @@ struct SweepWireTests { } /// Runs `body` on a thread with a stack of `stackSize` bytes. - private func onThread( + static func onThread( stackSize: Int, _ body: @escaping @Sendable () throws -> T ) async throws -> T { try await withCheckedThrowingContinuation { (done: CheckedContinuation) in diff --git a/swift/Tests/E2ETests/ChaosHarness.swift b/swift/Tests/E2ETests/ChaosHarness.swift new file mode 100644 index 0000000000..df8d7b807c --- /dev/null +++ b/swift/Tests/E2ETests/ChaosHarness.swift @@ -0,0 +1,189 @@ +import Foundation +import Synchronization +import Testing +import Zero + +/// The fixture database, read and written with `psql` behind zero-cache's +/// back. `ZERO_E2E_PG_DB` overrides its URL. +enum Postgres { + static let url = + ProcessInfo.processInfo.environment["ZERO_E2E_PG_DB"] + ?? "postgresql://postgres:postgres@127.0.0.1:5432/zero_e2e" + + /// Runs `sql` and returns its rows, each with its columns joined by `|`. + @discardableResult + static func query(_ sql: String) throws -> [String] { + #if os(macOS) || os(Linux) + let process = Process() + process.executableURL = URL(fileURLWithPath: "/usr/bin/env") + process.arguments = ["psql", url, "-v", "ON_ERROR_STOP=1", "-qAt", "-c", sql] + let out = Pipe() + let err = Pipe() + process.standardOutput = out + process.standardError = err + try process.run() + let data = out.fileHandleForReading.readDataToEndOfFile() + let errors = err.fileHandleForReading.readDataToEndOfFile() + process.waitUntilExit() + guard process.terminationStatus == 0 else { + throw PostgresError(message: String(decoding: errors, as: UTF8.self)) + } + return String(decoding: data, as: UTF8.self).split(separator: "\n").map(String.init) + #else + // No `Process` on iOS; the chaos tests run on macOS and Linux. + throw PostgresError(message: "psql can't run on this platform") + #endif + } + + struct PostgresError: Error, CustomStringConvertible { + var message: String + var description: String { "psql: \(message)" } + } + + /// The IDs of the open issues whose ID starts with `prefix`. + static func openIssueIDs(_ prefix: String) throws -> Set { + Set(try query("SELECT id FROM issues WHERE NOT closed AND id LIKE '\(prefix)%'")) + } + + /// Every issue whose ID starts with `prefix`, as `id|title|closed`. + static func issues(_ prefix: String) throws -> Set { + Set( + try query("SELECT id || '|' || title || '|' || closed FROM issues WHERE id LIKE '\(prefix)%'") + ) + } + + /// The last mutation ID zero-cache recorded for `clientID`. + static func lastMutationID(_ clientID: String) throws -> Int? { + try query( + "SELECT \"lastMutationID\" FROM zero_0.clients WHERE \"clientID\" = '\(clientID)'" + ).first.flatMap { Int($0) } + } +} + +/// Collects a client's log lines. +final class LogCollector: LogSink { + private let lines = Mutex<[String]>([]) + + func log(level: LogLevel, message: String, context: [String: String]) { + lines.withLock { $0.append("[\(level)] \(message)") } + } + + var all: [String] { lines.withLock { $0 } } + func count(_ substring: String) -> Int { all.filter { $0.contains(substring) }.count } +} + +/// Makes clients and waits on them. +@MainActor +struct Chaos { + let directory = NSTemporaryDirectory() + "zero-chaos-" + UUID().uuidString + let prefix = "chaos-\(UUID().uuidString.prefix(8))-" + static let mutators: [any Mutator] = [E2EApp.create, E2EApp.close] + + /// A client of `cacheURL` (zero-cache itself by default). + func makeZero( + cacheURL: String? = nil, storageKey: String = UUID().uuidString, sqlite: Bool = true, + logs: LogCollector? = nil, configure: (inout ZeroOptions) -> Void = { _ in } + ) throws -> Zero { + let origin = cacheURL ?? E2E.url!.replacingOccurrences(of: "ws://", with: "http://") + var options = ZeroOptions( + cacheURL: origin, storageKey: storageKey, + storage: sqlite ? .sqlite(directory: directory) : .memory) + options.logLevel = .info + options.logSink = logs + configure(&options) + return try Zero( + schema: .test, mutators: Self.mutators, options: options, + socketFactory: TransportSocketFactory(NIOTransport())) + } + + func cleanUp() { + try? FileManager.default.removeItem(atPath: directory) + _ = try? Postgres.query("DELETE FROM issues WHERE id LIKE '\(prefix)%'") + } + + /// Inserts `count` open issues `seed-` straight into Postgres, + /// with descriptions of `descriptionBytes`. + func seed(_ count: Int, descriptionBytes: Int = 0, tag: String = "seed") async throws { + try Postgres.query( + """ + INSERT INTO issues (id, title, description, closed, owner_id, "createdAt") + SELECT '\(prefix)\(tag)-' || lpad(n::text, 6, '0'), 'Seeded ' || n, + repeat('x', \(descriptionBytes)), false, NULL, 1000 + n + FROM generate_series(1, \(count)) AS n + """) + try await replicated(prefix + tag + "-" + String(format: "%06d", count)) + } + + /// Waits until zero-cache has replicated `id`: a client straight to it + /// sees the issue. + func replicated(_ id: String) async throws { + let probe = try makeZero(sqlite: false) + defer { probe.close() } + let issue = try probe.liveQuery(E2EApp.byID(id)) + try await eventually("\(id) to replicate") { issue.data != nil } + } + + func connected(_ zero: Zero, timeout: Duration = .seconds(30)) async throws { + try await eventually("\(zero.clientID) to connect", timeout: timeout) { + zero.connection.state.status == .connected + } + } +} + +/// Waits until `condition` holds. +@MainActor +func eventually( + _ what: String, timeout: Duration = .seconds(30), _ condition: () throws -> Bool +) async throws { + let deadline = ContinuousClock.now + timeout + while try !condition() { + guard ContinuousClock.now < deadline else { + Issue.record("Timed out after \(timeout) waiting for \(what)") + throw CancellationError() + } + try await Task.sleep(for: .milliseconds(20)) + } +} + +/// Whether `condition` holds within `timeout`, without recording an issue. +@MainActor +func within(_ timeout: Duration, _ condition: () throws -> Bool) async throws -> Bool { + let deadline = ContinuousClock.now + timeout + while try !condition() { + if ContinuousClock.now >= deadline { return false } + try await Task.sleep(for: .milliseconds(20)) + } + return true +} + +/// `value` within `timeout`, or nil. +@MainActor +func settled(_ deferred: Deferred, within timeout: Duration) async -> V? { + let deadline = ContinuousClock.now + timeout + while !deferred.isSettled { + if ContinuousClock.now >= deadline { return nil } + try? await Task.sleep(for: .milliseconds(20)) + } + return try? await deferred.value +} + +/// The server outcomes a client heard, by mutation ID. +@MainActor +final class OutcomeLog { + private(set) var byID: [Int: [MutationOutcome]] = [:] + func add(_ o: MutationServerOutcome) { byID[o.mutationID, default: []].append(o.outcome) } +} + +/// Expects `got` to equal `want`, and reports a short difference. +func expectSame( + _ got: Set, _ want: Set, _ what: String = "rows", + sourceLocation: SourceLocation = #_sourceLocation +) { + guard got != want else { return } + let missing = want.subtracting(got).sorted() + let extra = got.subtracting(want).sorted() + let message = + "\(what): \(got.count) of \(want.count); missing \(missing.count) \(missing.prefix(5)), " + + "extra \(extra.count) \(extra.prefix(5))" + Issue.record(Comment(rawValue: message), sourceLocation: sourceLocation) +} diff --git a/swift/Tests/E2ETests/ChaosProxy.swift b/swift/Tests/E2ETests/ChaosProxy.swift new file mode 100644 index 0000000000..8c21329b1d --- /dev/null +++ b/swift/Tests/E2ETests/ChaosProxy.swift @@ -0,0 +1,310 @@ +import NIOCore +import NIOPosix + +#if canImport(Glibc) + import Glibc +#elseif canImport(Darwin) + import Darwin +#endif + +/// A TCP proxy in front of zero-cache that the chaos tests steer: it can +/// refuse connections, accept them and never answer, complete the +/// WebSocket upgrade and then go silent, stall or throttle what the server +/// sends, and cut connections, abruptly or partway through a frame. +/// +/// Everything runs on one event loop of its own, so the knobs are plain +/// state on that loop and each change is applied in order. +final class ChaosProxy: @unchecked Sendable { + enum Accept { + /// Forward to zero-cache. + case pass + /// Close the connection at once. + case refuse + /// Accept, read, and never answer. + case blackhole + /// Forward the HTTP upgrade both ways, then drop everything the server + /// sends: an open WebSocket that never says `connected`. + case silentAfterUpgrade + } + + /// The knobs, read on the proxy's loop. + struct Knobs { + var accept: Accept = .pass + /// Bytes per second from the server to the client; nil for no limit, + /// 0 to stall. + var downstreamRate: Int? + /// Holds what the client sends. + var stallUpstream = false + /// Cuts each connection once the server has sent this many bytes on it: + /// those bytes reach the client, then the connection closes abruptly. + var cutAfterDownstreamBytes: Int? + } + + let port: Int + private let group: MultiThreadedEventLoopGroup + private let loop: any EventLoop + private let server: any Channel + private let upstreamHost: String + private let upstreamPort: Int + // Confined to `loop`. + private var knobs = Knobs() + private var links: [ObjectIdentifier: Link] = [:] + private var acceptedCount = 0 + private var ticker: RepeatedTask? + + /// Proxies `127.0.0.1:` to the zero-cache at `upstream` (a `ws://` + /// or `http://` origin). + init(upstream: String) async throws { + let rest = upstream.split(separator: "://").last.map(String.init) ?? upstream + let authority = rest.split(separator: "/").first.map(String.init) ?? rest + let parts = authority.split(separator: ":") + upstreamHost = String(parts[0]) + upstreamPort = parts.count > 1 ? Int(parts[1])! : 80 + group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + loop = group.next() + let box = ProxyBox() + server = try await ServerBootstrap(group: group) + .serverChannelOption(.socketOption(.so_reuseaddr), value: 1) + .childChannelInitializer { channel in + channel.eventLoop.makeCompletedFuture { + try box.proxy!.accepted(channel) + } + } + .bind(host: "127.0.0.1", port: 0).get() + port = server.localAddress!.port! + box.proxy = self + ticker = loop.scheduleRepeatedTask(initialDelay: .milliseconds(10), delay: .milliseconds(10)) { + [unowned self] _ in + for link in self.links.values { link.pump(tick: true) } + } + } + + /// The origin a client connects to, `http://127.0.0.1:`. + var cacheURL: String { "http://127.0.0.1:\(port)" } + + /// Changes the knobs, and applies them to the open connections. + func set(_ change: @escaping @Sendable (inout Knobs) -> Void) async { + try? await loop.submit { + change(&self.knobs) + for link in self.links.values { link.pump(tick: false) } + }.get() + } + + /// Closes every open connection abruptly, without a WebSocket close. + func cutAll() async { + try? await loop.submit { + for link in self.links.values { link.close(abrupt: true) } + }.get() + } + + /// The connections open now. + var openConnections: Int { + get async { (try? await loop.submit { self.links.count }.get()) ?? -1 } + } + + /// The connections accepted so far. + var accepted: Int { + get async { (try? await loop.submit { self.acceptedCount }.get()) ?? -1 } + } + + func shutdown() async { + try? await loop.submit { + self.ticker?.cancel() + for link in self.links.values { link.close(abrupt: true) } + }.get() + try? await server.close() + try? await group.shutdownGracefully() + } + + // MARK: - On the loop + + fileprivate func accepted(_ client: any Channel) throws { + acceptedCount += 1 + switch knobs.accept { + case .refuse: + client.close(promise: nil) + return + case .blackhole: + let link = Link(proxy: self, client: client, silent: .all) + links[ObjectIdentifier(link)] = link + try client.pipeline.syncOperations.addHandler(ClientSide(link)) + case .pass, .silentAfterUpgrade: + let link = Link( + proxy: self, client: client, + silent: knobs.accept == .silentAfterUpgrade ? .afterHeaders : .none) + links[ObjectIdentifier(link)] = link + try client.pipeline.syncOperations.addHandler(ClientSide(link)) + ClientBootstrap(group: loop) + .channelInitializer { channel in + channel.eventLoop.makeCompletedFuture { + try channel.pipeline.syncOperations.addHandler(ServerSide(link)) + } + } + .connect(host: upstreamHost, port: upstreamPort) + .whenComplete { result in + switch result { + case .success(let channel): link.serverConnected(channel) + case .failure: link.close(abrupt: true) + } + } + } + } + + fileprivate func removed(_ link: Link) { + links[ObjectIdentifier(link)] = nil + } + + fileprivate var currentKnobs: Knobs { knobs } + + /// One proxied connection. + fileprivate final class Link { + enum Silence { case none, afterHeaders, all } + + unowned let proxy: ChaosProxy + let client: any Channel + var server: (any Channel)? + var silent: Silence + var headerTail: [UInt8] = [] + var headersDone = false + var up: [ByteBuffer] = [] + var down: [ByteBuffer] = [] + var downSent = 0 + var budget = 0 + var closed = false + var serverGone = false + + init(proxy: ChaosProxy, client: any Channel, silent: Silence) { + self.proxy = proxy + self.client = client + self.silent = silent + } + + func serverConnected(_ channel: any Channel) { + if closed { + channel.close(promise: nil) + return + } + server = channel + pump(tick: false) + } + + func fromClient(_ buffer: ByteBuffer) { + guard silent != .all else { return } + up.append(buffer) + pump(tick: false) + } + + func fromServer(_ buffer: ByteBuffer) { + var buffer = buffer + switch silent { + case .all: return + case .afterHeaders: + if headersDone { return } + // Forward up to the end of the upgrade response's headers. + let bytes = buffer.readableBytesView + var end: Int? + for (i, b) in bytes.enumerated() { + headerTail.append(b) + if headerTail.count > 4 { headerTail.removeFirst() } + if headerTail == [13, 10, 13, 10] { + end = i + 1 + break + } + } + if let end { + headersDone = true + buffer = buffer.getSlice(at: buffer.readerIndex, length: end)! + } + case .none: break + } + down.append(buffer) + pump(tick: false) + } + + func pump(tick: Bool) { + guard !closed else { return } + let knobs = proxy.currentKnobs + if let server, !knobs.stallUpstream, !up.isEmpty { + for b in up { server.write(b, promise: nil) } + up = [] + server.flush() + } + if let rate = knobs.downstreamRate { + if tick { budget = min(budget + max(rate / 100, 0), max(rate, 1)) } + } else { + budget = Int.max + } + var wrote = false + while !down.isEmpty, budget > 0 { + var b = down.removeFirst() + var n = min(b.readableBytes, budget) + if let cut = knobs.cutAfterDownstreamBytes { n = min(n, max(cut - downSent, 0)) } + if n < b.readableBytes { + let head = b.readSlice(length: n)! + down.insert(b, at: 0) + b = head + } + if b.readableBytes > 0 { + client.write(b, promise: nil) + wrote = true + } + downSent += n + if knobs.downstreamRate != nil { budget -= n } + if let cut = knobs.cutAfterDownstreamBytes, downSent >= cut { + client.flush() + close(abrupt: true) + return + } + } + if wrote { client.flush() } + if serverGone, down.isEmpty { close(abrupt: false) } + } + + func close(abrupt: Bool) { + guard !closed else { return } + closed = true + proxy.removed(self) + for channel in [client, server].compactMap({ $0 }) { + if abrupt, let options = channel as? any SocketOptionProvider { + // A reset rather than an orderly shutdown. + _ = options.setSoLinger(linger(l_onoff: 1, l_linger: 0)) + } + channel.close(promise: nil) + } + } + } + + private final class ClientSide: ChannelInboundHandler { + typealias InboundIn = ByteBuffer + let link: Link + init(_ link: Link) { self.link = link } + func channelRead(context: ChannelHandlerContext, data: NIOAny) { + link.fromClient(unwrapInboundIn(data)) + } + func channelInactive(context: ChannelHandlerContext) { link.close(abrupt: false) } + func errorCaught(context: ChannelHandlerContext, error: any Error) { + link.close(abrupt: true) + } + } + + private final class ServerSide: ChannelInboundHandler { + typealias InboundIn = ByteBuffer + let link: Link + init(_ link: Link) { self.link = link } + func channelRead(context: ChannelHandlerContext, data: NIOAny) { + link.fromServer(unwrapInboundIn(data)) + } + func channelInactive(context: ChannelHandlerContext) { + // The server went away: deliver what it sent, then close. + link.serverGone = true + link.pump(tick: false) + } + func errorCaught(context: ChannelHandlerContext, error: any Error) { + link.close(abrupt: true) + } + } +} + +private final class ProxyBox: @unchecked Sendable { + var proxy: ChaosProxy? +} diff --git a/swift/Tests/E2ETests/HardenE2ETests.swift b/swift/Tests/E2ETests/HardenE2ETests.swift new file mode 100644 index 0000000000..24a344654f --- /dev/null +++ b/swift/Tests/E2ETests/HardenE2ETests.swift @@ -0,0 +1,525 @@ +import Foundation +import Testing +import Zero + +/// Chaos against the real zero-cache: connections cut, stalled, and +/// refused by a `ChaosProxy`, data changed in Postgres behind the client's +/// back, mutations replayed across outages and relaunches. The oracle is +/// convergence: afterwards the client holds what Postgres holds, every +/// mutation applied exactly once. +@Suite(.enabled(if: E2E.url != nil), .serialized, .timeLimit(.minutes(2))) +@MainActor +struct HardenE2ETests { + let chaos = Chaos() + + /// The IDs of `chaos.prefix` in a live query of the open issues. + func ids(_ open: LiveQuery<[TestIssue]>) -> Set { + Set(open.data.map(\.id).filter { $0.hasPrefix(chaos.prefix) }) + } + + // MARK: - Servers that don't answer + + /// A server that accepts the TCP connection and never answers the + /// upgrade. The connect timeout (10 s, `CONNECT_TIMEOUT_MS`) gives up on + /// it and closes the socket; JS's `#disconnect` closes the still + /// connecting `WebSocket`, which aborts the handshake. Then the client + /// reconnects once the server answers, and no socket is left behind. + @Test func hungUpgradeTimesOutClosesTheSocketAndRecovers() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + await proxy.set { $0.accept = .blackhole } + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL) + defer { zero.close() } + while await proxy.accepted < 1 { try await Task.sleep(for: .milliseconds(20)) } + await proxy.set { $0.accept = .pass } + // 10 s for the timeout, 5 s of backoff. + try await chaos.connected(zero, timeout: .seconds(25)) + #expect(await proxy.accepted == 2) + // The hung socket was closed when its attempt timed out. + let deadline = ContinuousClock.now + .seconds(2) + while await proxy.openConnections > 1, ContinuousClock.now < deadline { + try await Task.sleep(for: .milliseconds(20)) + } + #expect(await proxy.openConnections == 1, "the timed-out socket leaked") + zero.close() + await proxy.shutdown() + } + + /// A server that completes the WebSocket upgrade and never sends + /// `connected`: the ack timeout closes the socket, and the client + /// connects once the server behaves. + @Test func openSocketWithoutConnectedTimesOutAndRecovers() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + await proxy.set { $0.accept = .silentAfterUpgrade } + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL) + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + while await proxy.accepted < 1 { try await Task.sleep(for: .milliseconds(20)) } + await proxy.set { $0.accept = .pass } + #expect(zero.connection.state.status == .connecting) + try await chaos.connected(zero, timeout: .seconds(25)) + try await eventually("the open issues") { open.resultType == .complete } + let deadline = ContinuousClock.now + .seconds(2) + while await proxy.openConnections > 1, ContinuousClock.now < deadline { + try await Task.sleep(for: .milliseconds(20)) + } + #expect(await proxy.openConnections == 1) + zero.close() + await proxy.shutdown() + } + + // MARK: - Cuts and slow reads + + /// The connection is cut partway through a large initial poke, several + /// times. The partial poke must not be applied, and the client must + /// converge on Postgres once a connection survives. + @Test func cutsMidInitialPokeConverge() async throws { + defer { chaos.cleanUp() } + try await chaos.seed(3000, descriptionBytes: 300) + let expected = try Postgres.openIssueIDs(chaos.prefix) + #expect(expected.count == 3000) + let proxy = try await ChaosProxy(upstream: E2E.url!) + await proxy.set { $0.cutAfterDownstreamBytes = 150_000 } + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL, sqlite: false) + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + while await proxy.accepted < 3 { try await Task.sleep(for: .milliseconds(20)) } + // Never a partial result: either nothing of the seed, or all of it. + #expect( + ids(open).isEmpty || ids(open) == expected, "\(ids(open).count) of \(expected.count) rows") + #expect(open.resultType != .complete) + await proxy.set { $0.cutAfterDownstreamBytes = nil } + try await eventually("the issues after the cuts", timeout: .seconds(40)) { + open.resultType == .complete && ids(open) == expected + } + zero.close() + await proxy.shutdown() + } + + /// A slow reader: the client takes a ~3 MB initial poke at 600 KB/s. + /// Each message the server sends resets the ping timer, so the client + /// must not give up on the connection while the poke trickles in. + @Test func slowReaderLargePokeCompletesOnOneConnection() async throws { + defer { chaos.cleanUp() } + try await chaos.seed(3000, descriptionBytes: 1000) + let expected = try Postgres.openIssueIDs(chaos.prefix) + let proxy = try await ChaosProxy(upstream: E2E.url!) + await proxy.set { $0.downstreamRate = 600_000 } + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL, sqlite: false) + defer { zero.close() } + let start = ContinuousClock.now + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the throttled poke", timeout: .seconds(50)) { + open.resultType == .complete + } + let elapsed = ContinuousClock.now - start + print("[chaos] throttled poke of \(expected.count) rows in \(elapsed)") + expectSame(ids(open), expected) + #expect(await proxy.accepted == 1, "the client reconnected during the slow poke") + zero.close() + await proxy.shutdown() + } + + // MARK: - Mutations across outages + + /// Mutations made while zero-cache is unreachable are replayed in order + /// once it is back, each applied exactly once: Postgres holds what the + /// client predicted, and the server's last mutation ID for the client is + /// the number of mutations. + @Test func offlineMutationsReplayInOrderExactlyOnce() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL) + defer { zero.close() } + let outcomes = OutcomeLog() + let stopListening = zero.addMutationOutcomeListener { outcomes.add($0) } + defer { stopListening() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + + await proxy.set { $0.accept = .refuse } + await proxy.cutAll() + try await eventually("the cut") { zero.connection.state.status != .connected } + + var results: [MutatorResult] = [] + var expectedOpen: Set = [] + for n in 0..<30 { + let id = "\(chaos.prefix)\(n)" + results.append(zero.mutate(E2EApp.create(.init(id: id, title: "Offline \(n)", ownerId: nil)))) + expectedOpen.insert(id) + if n % 3 == 0 { + // Only applies after the create before it. + results.append(zero.mutate(E2EApp.close(id))) + expectedOpen.remove(id) + } + } + #expect(ids(open) == expectedOpen) + try await Task.sleep(for: .seconds(1)) + await proxy.set { $0.accept = .pass } + + for (i, r) in results.enumerated() { + let outcome = await settled(r.server, within: .seconds(30)) + #expect(outcome == .success(data: nil), "mutation \(i + 1)") + } + #expect(try Postgres.openIssueIDs(chaos.prefix) == expectedOpen) + #expect(try Postgres.issues(chaos.prefix).count == 30) + #expect(try Postgres.lastMutationID(zero.clientID) == results.count) + try await eventually("the client to settle") { ids(open) == expectedOpen } + #expect(outcomes.byID.count == results.count) + #expect(outcomes.byID.values.allSatisfy { $0 == [.success(data: nil)] }, "\(outcomes.byID)") + + let fresh = try chaos.makeZero(sqlite: false) + defer { fresh.close() } + let freshOpen = try fresh.liveQuery(E2EApp.open()) + try await eventually("a fresh client") { freshOpen.resultType == .complete } + #expect(ids(freshOpen) == expectedOpen) + zero.close() + await proxy.shutdown() + } + + /// Mutations made while the connection is cut over and over: some are + /// pushed and lost, some pushed twice, some confirmed only by the + /// reconnect's last mutation ID. Each is applied exactly once. + @Test func mutationsRacingConnectionCutsApplyExactlyOnce() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL) + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + var results: [MutatorResult] = [] + var expected: Set = [] + for n in 0..<60 { + let id = "\(chaos.prefix)\(n)" + results.append(zero.mutate(E2EApp.create(.init(id: id, title: "Racing \(n)", ownerId: nil)))) + expected.insert(id) + if n % 7 == 3 { await proxy.cutAll() } + try await Task.sleep(for: .milliseconds(30)) + } + for (i, r) in results.enumerated() { + let outcome = await settled(r.server, within: .seconds(40)) + #expect(outcome == .success(data: nil), "mutation \(i + 1)") + } + #expect(try Postgres.openIssueIDs(chaos.prefix) == expected) + #expect(try Postgres.lastMutationID(zero.clientID) == results.count) + try await eventually("the client to settle") { ids(open) == expected } + print("[chaos] \(await proxy.accepted) connections for 60 racing mutations") + zero.close() + await proxy.shutdown() + } + + /// The server applies a mutation but its response is lost: the server + /// stalls on the way back, the ping times out, and the connection is + /// cut. The reconnect's last mutation ID confirms the mutation, which + /// must not be applied again. + @Test func lostPushResponseConfirmedByReconnect() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL) { $0.pingTimeoutMs = 1000 } + defer { zero.close() } + let outcomes = OutcomeLog() + let stopListening = zero.addMutationOutcomeListener { outcomes.add($0) } + defer { stopListening() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + await proxy.set { $0.downstreamRate = 0 } + let id = "\(chaos.prefix)lost-response" + let created = zero.mutate(E2EApp.create(.init(id: id, title: "Lost response", ownerId: nil))) + try await eventually("the server to apply it") { + try Postgres.openIssueIDs(chaos.prefix) == [id] + } + // The ping fails, and the client reconnects once the server is heard. + try await eventually("the ping timeout") { zero.connection.state.status != .connected } + await proxy.set { $0.downstreamRate = nil } + #expect(await settled(created.server, within: .seconds(20)) == .success(data: nil)) + try await chaos.connected(zero) + #expect(try Postgres.lastMutationID(zero.clientID) == 1) + try await eventually("the confirmed row") { open.data.contains { $0.id == id } } + #expect(outcomes.byID.count <= 1) + // Still syncing. + let next = zero.mutate(E2EApp.close(id)) + #expect(await settled(next.server, within: .seconds(10)) == .success(data: nil)) + try await eventually("the close to sync") { !open.data.contains { $0.id == id } } + zero.close() + await proxy.shutdown() + } + + /// A push that never reaches the server: what the client sends is held, + /// then the connection is cut. The reconnect pushes it again. + @Test func pushLostOnTheWayIsSentAgain() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL) + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + await proxy.set { $0.stallUpstream = true } + let issueIDs = (0..<5).map { "\(chaos.prefix)held-\($0)" } + let results = issueIDs.map { + zero.mutate(E2EApp.create(.init(id: $0, title: "Held", ownerId: nil))) + } + try await Task.sleep(for: .milliseconds(300)) + #expect(try Postgres.openIssueIDs(chaos.prefix).isEmpty) + await proxy.cutAll() + await proxy.set { $0.stallUpstream = false } + for r in results { + #expect(await settled(r.server, within: .seconds(20)) == .success(data: nil)) + } + #expect(try Postgres.openIssueIDs(chaos.prefix) == Set(issueIDs)) + #expect(try Postgres.lastMutationID(zero.clientID) == 5) + zero.close() + await proxy.shutdown() + } + + /// The server stops sending while the socket stays open: the ping + /// times out (`pingTimeoutMs`), the client drops the connection and + /// opens a new one, and catches up on what changed in Postgres meanwhile. + @Test func stalledServerIsDetectedByPingAndCaughtUp() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL) { $0.pingTimeoutMs = 1000 } + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + await proxy.set { $0.downstreamRate = 0 } + let id = "\(chaos.prefix)while-stalled" + try Postgres.query( + """ + INSERT INTO issues (id, title, description, closed, owner_id, "createdAt") + VALUES ('\(id)', 'Behind the back', '', false, 'u3', 7) + """) + // Ping after 1 s of silence, give up after 1 s more. + try await eventually("the ping timeout", timeout: .seconds(5)) { + zero.connection.state.status != .connected + } + #expect(await proxy.openConnections == 0) + await proxy.set { $0.downstreamRate = nil } + try await eventually("the row written behind the client's back") { + open.data.first { $0.id == id }?.owner?.name == "Çağrı" + } + #expect(await proxy.accepted >= 2) + zero.close() + await proxy.shutdown() + } + + // MARK: - Postgres behind the client's back + + /// Inserts, updates, and deletes made with psql arrive, including a + /// related row with a large JSON value. + @Test func postgresWritesArriveIncludingLargeJSON() async throws { + defer { + chaos.cleanUp() + _ = try? Postgres.query("DELETE FROM users WHERE id LIKE '\(chaos.prefix)%'") + } + let zero = try chaos.makeZero(sqlite: false) + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + let user = "\(chaos.prefix)user" + let id = "\(chaos.prefix)psql" + // ~2 MB of JSON: 20,000 contacts. + try Postgres.query( + """ + INSERT INTO users (id, name, metadata) SELECT '\(user)', 'Big', + jsonb_build_object('registrar', 'google', 'email', 'big@example.com', + 'altContacts', jsonb_agg(n || ':' || repeat('é😀', 30) ORDER BY n)) + FROM generate_series(1, 20000) AS n; + INSERT INTO issues (id, title, description, closed, owner_id, "createdAt") + VALUES ('\(id)', 'From psql', '', false, '\(user)', 8); + """) + let start = ContinuousClock.now + try await eventually("the issue and its big owner") { + open.data.first { $0.id == id }?.owner?.metadata != nil + } + print("[chaos] 2 MB JSON row arrived in \(ContinuousClock.now - start)") + let contacts = try #require(open.data.first { $0.id == id }?.owner?.metadata?.altContacts) + #expect(contacts.count == 20000) + #expect(contacts.last == "20000:" + String(repeating: "é😀", count: 30)) + try Postgres.query("UPDATE issues SET title = 'Renamed in psql' WHERE id = '\(id)'") + try await eventually("the update") { + open.data.first { $0.id == id }?.title == "Renamed in psql" + } + try Postgres.query("DELETE FROM issues WHERE id = '\(id)'") + try await eventually("the delete") { !open.data.contains { $0.id == id } } + zero.close() + } + + /// Two clients of one user create the same issue at once. The server's + /// insert is `ON CONFLICT DO NOTHING` (`makeServerTableCRUD` in + /// `packages/zero-server/src/custom.ts`), so both succeed and the first + /// wins; both clients must drop their optimistic row for Postgres's. + @Test func twoClientsRacingOnOneRowConverge() async throws { + defer { chaos.cleanUp() } + let a = try chaos.makeZero(sqlite: false) + let b = try chaos.makeZero(sqlite: false) + defer { + a.close() + b.close() + } + let openA = try a.liveQuery(E2EApp.open()) + let openB = try b.liveQuery(E2EApp.open()) + try await eventually("both clients") { + openA.resultType == .complete && openB.resultType == .complete + } + for round in 0..<10 { + let id = "\(chaos.prefix)race-\(round)" + let ra = a.mutate(E2EApp.create(.init(id: id, title: "A", ownerId: nil))) + let rb = b.mutate(E2EApp.create(.init(id: id, title: "B", ownerId: nil))) + let oa = await settled(ra.server, within: .seconds(10)) + let ob = await settled(rb.server, within: .seconds(10)) + #expect(oa == .success(data: nil), "round \(round)") + #expect(ob == .success(data: nil), "round \(round)") + let title = try Postgres.query("SELECT title FROM issues WHERE id = '\(id)'").first + try await eventually("round \(round) to converge") { + openA.data.first { $0.id == id }?.title == title + && openB.data.first { $0.id == id }?.title == title + } + } + } + + // MARK: - Queries + + /// Hundreds of queries materialized and destroyed in a burst. The server + /// ends up with the queries still open active, the client keeps + /// syncing, and every destroyed query is let go. + @Test func queryChurnSettlesOnTheServer() async throws { + defer { chaos.cleanUp() } + let zero = try chaos.makeZero(sqlite: false) + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + var kept: [LiveQuery] = [] + for n in 0..<300 { + let q = try zero.liveQuery(E2EApp.byID("\(chaos.prefix)churn-\(n)"), ttl: .none) + if n % 50 == 0 { kept.append(q) } else { q.view.destroy() } + if n % 20 == 0 { try await Task.sleep(for: .milliseconds(1)) } + } + try await eventually("the kept queries") { kept.allSatisfy { $0.resultType == .complete } } + let id = "\(chaos.prefix)churn-100" + let created = zero.mutate(E2EApp.create(.init(id: id, title: "Churned", ownerId: nil))) + #expect(await settled(created.server, within: .seconds(10)) == .success(data: nil)) + try await eventually("a kept query to see its row") { kept[2].data?.title == "Churned" } + // `issues.open` and the kept `issues.byID`s. + let client = zero.clientID + try await eventually("the server's desired queries", timeout: .seconds(15)) { + try Postgres.query( + """ + SELECT count(*) FROM "zero_0/cvr".desires + WHERE "clientID" = '\(client)' AND NOT coalesce(deleted, false) + AND "inactivatedAt" IS NULL + """ + ).first == String(1 + kept.count) + } + _ = kept + } + + // MARK: - Storage + + /// Clients opened on one SQLite store, each making a mutation and closed + /// at once, before it could push. The last one pushes them all, each + /// applied once. + @Test func rapidCloseAndReopenOnSQLiteLosesNoMutation() async throws { + defer { chaos.cleanUp() } + let key = UUID().uuidString + var clientIDs: [String] = [] + for n in 0..<15 { + let zero = try chaos.makeZero(storageKey: key) + clientIDs.append(zero.clientID) + _ = zero.mutate( + E2EApp.create(.init(id: "\(chaos.prefix)\(n)", title: "Reopened \(n)", ownerId: nil))) + zero.close() + } + let last = try chaos.makeZero(storageKey: key) + defer { last.close() } + let open = try last.liveQuery(E2EApp.open()) + let expected = Set((0..<15).map { "\(chaos.prefix)\($0)" }) + try await eventually("every mutation in Postgres", timeout: .seconds(30)) { + try Postgres.openIssueIDs(chaos.prefix) == expected + } + for id in clientIDs { #expect(try Postgres.lastMutationID(id) == 1, "\(id)") } + try await eventually("the last client to see them") { ids(open) == expected } + } + + /// A relaunch while the server is down and mutations are pending: the + /// new client shows them from its store, and pushes them once the + /// server is back. + @Test func relaunchWithPendingMutationsWhileServerIsDown() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + await proxy.set { $0.accept = .refuse } + let key = UUID().uuidString + let first = try chaos.makeZero(cacheURL: proxy.cacheURL, storageKey: key) + let issueIDs = (0..<5).map { "\(chaos.prefix)pending-\($0)" } + for id in issueIDs { + _ = first.mutate(E2EApp.create(.init(id: id, title: "Pending", ownerId: nil))) + } + _ = first.mutate(E2EApp.close(issueIDs[0])) + let firstID = first.clientID + first.close() + + let second = try chaos.makeZero(cacheURL: proxy.cacheURL, storageKey: key) + defer { second.close() } + let open = try second.liveQuery(E2EApp.open()) + let expected = Set(issueIDs.dropFirst()) + try await eventually("the pending rows from the store") { ids(open) == expected } + await proxy.set { $0.accept = .pass } + try await eventually("the mutations in Postgres", timeout: .seconds(30)) { + try Postgres.openIssueIDs(chaos.prefix) == expected + } + #expect(try Postgres.issues(chaos.prefix).count == 5) + #expect(try Postgres.lastMutationID(firstID) == 6) + try await eventually("the confirmed rows") { + open.resultType == .complete && ids(open) == expected + } + second.close() + await proxy.shutdown() + } + + /// Clients opened and closed in a burst, some mid-handshake: every + /// socket is closed. + @Test func rapidOpenCloseLeavesNoSockets() async throws { + defer { chaos.cleanUp() } + let proxy = try await ChaosProxy(upstream: E2E.url!) + for n in 0..<20 { + let zero = try chaos.makeZero(cacheURL: proxy.cacheURL, sqlite: false) + _ = try zero.liveQuery(E2EApp.open()) + try await Task.sleep(for: .milliseconds(n * 5)) + zero.close() + } + let deadline = ContinuousClock.now + .seconds(5) + while await proxy.openConnections > 0, ContinuousClock.now < deadline { + try await Task.sleep(for: .milliseconds(50)) + } + #expect(await proxy.openConnections == 0) + await proxy.shutdown() + } + + /// A mutation too large for zero-cache (over its 10 MB message limit): + /// the server closes with 1009. The client must not loop resending it, + /// and must sync again (JS reloads with a new client group). + @Test func oversizedMutationDoesNotWedgeTheClient() async throws { + defer { chaos.cleanUp() } + let logs = LogCollector() + let zero = try chaos.makeZero(logs: logs) + defer { zero.close() } + let open = try zero.liveQuery(E2EApp.open()) + try await eventually("the open issues") { open.resultType == .complete } + let big = zero.mutate( + E2EApp.create( + .init( + id: "\(chaos.prefix)big", title: String(repeating: "x", count: 11 << 20), ownerId: nil))) + _ = await settled(big.server, within: .seconds(15)) + try await Task.sleep(for: .seconds(2)) + let id = "\(chaos.prefix)after-big" + let after = zero.mutate(E2EApp.create(.init(id: id, title: "After", ownerId: nil))) + let afterOutcome = await settled(after.server, within: .seconds(20)) + print("[chaos] after the oversized mutation: \(String(describing: afterOutcome))") + try await eventually("a mutation after the oversized one", timeout: .seconds(20)) { + try Postgres.openIssueIDs(chaos.prefix).contains(id) + } + print("[chaos] 1009 closes: \(logs.count("code=1009"))") + #expect(logs.count("code=1009") <= 1) + #expect(try !Postgres.openIssueIDs(chaos.prefix).contains("\(chaos.prefix)big")) + } +} diff --git a/swift/Tests/E2ETests/NIOTransport.swift b/swift/Tests/E2ETests/NIOTransport.swift index 27458a2d77..dec123fb02 100644 --- a/swift/Tests/E2ETests/NIOTransport.swift +++ b/swift/Tests/E2ETests/NIOTransport.swift @@ -1,3 +1,4 @@ +import Foundation import NIOCore import NIOHTTP1 import NIOPosix @@ -20,11 +21,27 @@ public struct NIOTransport: Transport { public func connect(url: String, secProtocol: String?) async throws -> any Socket { let (host, port, uri) = try Self.parse(url) + // Cancelling the connect closes the channel, as closing a browser + // WebSocket that is still connecting aborts it. + let opening = OpeningChannel() + return try await withTaskCancellationHandler { + try await connect( + host: host, port: port, uri: uri, url: url, secProtocol: secProtocol, opening) + } onCancel: { + opening.cancel() + } + } + + private func connect( + host: String, port: Int, uri: String, url: String, secProtocol: String?, + _ opening: OpeningChannel + ) async throws -> any Socket { let upgrade: EventLoopFuture = try await ClientBootstrap( group: MultiThreadedEventLoopGroup.singleton ) .connect(host: host, port: port) { channel in - channel.eventLoop.makeCompletedFuture { + opening.opened(channel) + return channel.eventLoop.makeCompletedFuture { let upgrader = NIOTypedWebSocketClientUpgrader(maxFrameSize: 1 << 24) { channel, _ in channel.eventLoop.makeCompletedFuture { @@ -68,6 +85,30 @@ public struct NIOTransport: Transport { } } +/// The channel of a connect in progress, closed if the connect is +/// cancelled. +final class OpeningChannel: @unchecked Sendable { + private let lock = NSLock() + private var channel: (any Channel)? + private var cancelled = false + + func opened(_ channel: any Channel) { + let cancelled = lock.withLock { + self.channel = channel + return self.cancelled + } + if cancelled { channel.close(promise: nil) } + } + + func cancel() { + let channel = lock.withLock { + cancelled = true + return self.channel + } + channel?.close(promise: nil) + } +} + /// One reader at a time: `receive` is called by a single task. final class NIOSocket: Socket, @unchecked Sendable { private var inbound: AsyncThrowingStream.AsyncIterator @@ -95,7 +136,19 @@ final class NIOSocket: Socket, @unchecked Sendable { outbound.yield( WebSocketFrame( fin: true, opcode: .pong, maskKey: .random(), data: frame.unmaskedData)) - case .connectionClose: return + case .connectionClose: + // A close other than a normal one is reported with its + // code, as a browser's `CloseEvent` has it. + var data = frame.unmaskedData + if let code = data.readWebSocketErrorCode() { + let number = Int(UInt16(webSocketErrorCode: code)) + if number != 1000 { + throw SocketClosed( + code: number, reason: data.readString(length: data.readableBytes) ?? "", + wasClean: true) + } + } + return default: break } } diff --git a/swift/Tests/ZeroTests/HardenConnectionTests.swift b/swift/Tests/ZeroTests/HardenConnectionTests.swift new file mode 100644 index 0000000000..011dc7ee07 --- /dev/null +++ b/swift/Tests/ZeroTests/HardenConnectionTests.swift @@ -0,0 +1,220 @@ +import Testing +import ZeroTesting + +@testable import Zero + +/// Edges of the connection the client scenarios can't express: log levels, +/// the `maxHeaderLength` boundary, and a transport that calls back from +/// inside `send`. Expected values follow `packages/zero-client/src/client/zero.ts`. +@Suite @MainActor struct HardenConnectionTests { + static let create = defineMutator("issue.create") { (tx, id: String) in + try tx.mutate.insert( + Issue(id: id, title: "", description: "", closed: false, createdAt: 1)) + } + + let scheduler = ManualScheduler(now: 1_700_000_000_000) + let log = RecordingLogSink() + + func makeZero( + _ sockets: any SocketFactory, maxHeaderLength: Int? = nil, auth: String? = nil + ) throws -> Zero { + var options = ZeroOptions( + cacheURL: "http://zero.test", userID: "u", storageKey: randomID(), storage: .memory) + options.logSink = log + options.auth = auth + options.onClientStateNotFound = {} + options.onUpdateNeeded = { _ in } + if let maxHeaderLength { options.maxHeaderLength = maxHeaderLength } + return try Zero( + schema: .test, mutators: [Self.create], options: options, scheduler: scheduler, + socketFactory: sockets) + } + + func connect(_ sockets: MockSocketFactory) throws -> MockSocket { + scheduler.runDueTimers() + let socket = try #require(sockets.sockets.last) + socket.receive(text: #"["connected",{"wsid":"w"}]"#) + scheduler.runDueTimers() + return socket + } + + func levels(startingWith prefix: String) -> [LogLevel] { + log.entries.filter { $0.message.hasPrefix(prefix) }.map(\.level) + } + + // MARK: - Log levels (075b4215e, 79adc09fb, 42316d521) + + /// 075b4215e: a ClientNotFound while connecting is logged at warn, both + /// the error message and the connect failure, and nothing is logged as an + /// error. + @Test func clientNotFoundIsLoggedAtWarn() throws { + let sockets = MockSocketFactory() + let zero = try makeZero(sockets) + scheduler.runDueTimers() + try #require(sockets.sockets.last).receive( + text: #"["error",{"kind":"ClientNotFound","message":"m","origin":"zeroCache"}]"#) + scheduler.runDueTimers() + #expect(levels(startingWith: "ClientNotFound:\n\n") == [.warn]) + #expect(levels(startingWith: "Failed to connect") == [.warn]) + #expect(log.entries.filter { $0.level == .error } == []) + zero.close() + } + + /// 79adc09fb: an auth error while connected (including a push or + /// transform the API server answered with 401 or 403) is logged at warn, + /// and nothing is logged as an error. + @Test(arguments: [ + #"{"kind":"Unauthorized","message":"m","origin":"zeroCache"}"#, + #"{"kind":"AuthInvalidated","message":"m","origin":"zeroCache"}"#, + #"{"kind":"PushFailed","message":"m","origin":"zeroCache","reason":"http","status":401,"mutationIDs":[]}"#, + #"{"kind":"TransformFailed","message":"m","origin":"zeroCache","reason":"http","status":403,"queryIDs":["q"]}"#, + ]) + func authErrorsAreLoggedAtWarn(_ body: String) throws { + let sockets = MockSocketFactory() + let zero = try makeZero(sockets, auth: "t1") + let socket = try connect(sockets) + socket.receive(text: #"["error",\#(body)]"#) + scheduler.runDueTimers() + #expect(zero.connection.state.status == .needsAuth) + let kind = try #require(try JSONParser.parse(Array(body.utf8))["kind"]?.stringValue) + #expect(levels(startingWith: "\(kind):\n\n") == [.warn]) + #expect(log.entries.filter { $0.level == .error } == []) + zero.close() + } + + /// 075b4215e: an error the client can't recover from keeps the error + /// level, for the message and the connect failure. + @Test func versionNotSupportedIsLoggedAtError() throws { + let sockets = MockSocketFactory() + let zero = try makeZero(sockets) + scheduler.runDueTimers() + try #require(sockets.sockets.last).receive( + text: #"["error",{"kind":"VersionNotSupported","message":"m","origin":"zeroCache"}]"#) + scheduler.runDueTimers() + #expect(levels(startingWith: "VersionNotSupported:\n\n") == [.error]) + #expect(levels(startingWith: "Failed to connect") == [.error]) + zero.close() + } + + // MARK: - maxHeaderLength + + /// `createSocket` moves `initConnection` out of the header only when the + /// encoded protocol is longer than `maxHeaderLength` (`>`), so a header + /// of exactly that length still carries it. + @Test func initConnectionAtExactlyMaxHeaderLengthStaysInTheHeader() throws { + let probe = MockSocketFactory() + let first = try makeZero(probe, maxHeaderLength: 1_000_000) + scheduler.runDueTimers() + let length = try #require(probe.sockets.last?.secProtocol).utf8.count + first.close() + + for (maxHeaderLength, inHeader) in [(length, true), (length - 1, false)] { + let sockets = MockSocketFactory() + let zero = try makeZero(sockets, maxHeaderLength: maxHeaderLength) + scheduler.runDueTimers() + let socket = try #require(sockets.sockets.last) + let decoded = try SecProtocols.decode(socket.secProtocol ?? "") + #expect((decoded["initConnectionMessage"] != nil) == inHeader, "\(maxHeaderLength)") + socket.receive(text: #"["connected",{"wsid":"w"}]"#) + scheduler.runDueTimers() + let sentInit = socket.messages.contains { $0.hasPrefix(#"["initConnection""#) } + #expect(sentInit == !inHeader, "\(maxHeaderLength)") + zero.close() + } + } + + // MARK: - A transport that calls back from inside send + + /// Delivers what `reply` returns for a sent frame before `send` returns. + final class EagerSocketFactory: SocketFactory { + let mock = MockSocketFactory() + var reply: (String, MockSocket) -> Void = { _, _ in } + + final class Connection: SocketConnection { + let socket: MockSocket + let factory: EagerSocketFactory + init(_ socket: MockSocket, _ factory: EagerSocketFactory) { + self.socket = socket + self.factory = factory + } + func send(_ text: String) { + socket.send(text) + factory.reply(text, socket) + } + func close(code: Int?) { socket.close(code: code) } + } + + func open( + url: String, secProtocol: String?, events: any SocketEvents + ) + -> any SocketConnection + { + _ = mock.open(url: url, secProtocol: secProtocol, events: events) + return Connection(mock.sockets.last!, self) + } + } + + /// A pong delivered from inside the ping's `send` still counts: the + /// client stays connected on the same socket. + @Test(.timeLimit(.minutes(1))) + func pongFromInsideThePingSend() throws { + let factory = EagerSocketFactory() + let zero = try makeZero(factory) + let socket = try connect(factory.mock) + factory.reply = { text, socket in + if text.hasPrefix(#"["ping""#) { socket.receive(text: #"["pong",{}]"#) } + } + for _ in 0..<3 { scheduler.advance(by: 5000) } + #expect(zero.connection.state == .connected) + #expect(factory.mock.sockets.count == 1) + #expect(socket.messages.filter { $0.hasPrefix(#"["ping""#) }.count == 3) + zero.close() + } + + /// The socket drops from inside the push's `send`: the client + /// reconnects and pushes the mutation again on the new socket. + @Test(.timeLimit(.minutes(1))) + func closeFromInsideThePushSend() throws { + let factory = EagerSocketFactory() + let zero = try makeZero(factory) + let first = try connect(factory.mock) + factory.reply = { text, socket in + if text.hasPrefix(#"["push""#), socket === first { socket.serverClose(code: 1006) } + } + zero.mutate(Self.create("a")) + scheduler.runDueTimers() + #expect(factory.mock.sockets.count == 2) + let second = try connect(factory.mock) + #expect(zero.connection.state == .connected) + #expect(second.messages.filter { $0.hasPrefix(#"["push""#) }.count == 1) + zero.close() + } + + /// An auth error delivered from inside the `send` of the handshake's + /// `changeDesiredQueries`: the error wins, and the client waits in + /// needs-auth with no socket instead of reporting connected. + @Test(.timeLimit(.minutes(1))) + func authErrorFromInsideTheHandshakeSend() throws { + let factory = EagerSocketFactory() + let zero = try makeZero(factory) + let all = defineQuery("issues.all") { () in zql.issue.orderBy(\.id, .asc) } + scheduler.runDueTimers() + let view = try zero.materialize(all()) + factory.reply = { text, socket in + if text.hasPrefix(#"["changeDesiredQueries""#) { + socket.receive( + text: #"["error",{"kind":"Unauthorized","message":"no","origin":"zeroCache"}]"#) + } + } + let socket = try #require(factory.mock.sockets.last) + socket.receive(text: #"["connected",{"wsid":"w"}]"#) + scheduler.runDueTimers() + #expect(socket.closed) + #expect(zero.connection.state.status == .needsAuth) + scheduler.advance(by: 20_000) + #expect(zero.connection.state.status == .needsAuth) + #expect(factory.mock.sockets.count == 1) + view.destroy() + zero.close() + } +} diff --git a/swift/Tests/ZeroTests/HardenCrashTests.swift b/swift/Tests/ZeroTests/HardenCrashTests.swift new file mode 100644 index 0000000000..e9e3c551f3 --- /dev/null +++ b/swift/Tests/ZeroTests/HardenCrashTests.swift @@ -0,0 +1,369 @@ +import Foundation +import Testing +import ZeroTesting + +@testable import Zero + +// Exit tests run on macOS and Linux only, not in the iOS simulator. +#if os(macOS) || os(Linux) + /// Bad input that used to trap the process. The JS client throws, logs, or + /// disconnects on each of these; a trap in Swift kills the app. Each + /// scenario runs in a child process (an exit test), so a trap fails only + /// its own test, and checks what the client does instead with + /// `precondition`, which fails the child. + @Suite @MainActor struct HardenCrashTests { + static let create = defineMutator("issue.create") { (tx, id: String) in + try tx.mutate.insert( + Issue(id: id, title: "", description: "", closed: false, createdAt: 1)) + } + + /// A client on memory, a manual clock, and a mock socket. + @MainActor struct Harness { + let scheduler = ManualScheduler(now: 1_700_000_000_000) + let sockets = MockSocketFactory() + let zero: Zero + + init() throws { + var options = ZeroOptions( + cacheURL: "http://zero.test", userID: "u", storageKey: randomID(), storage: .memory) + options.logSink = RecordingLogSink() + zero = try Zero( + schema: .test, mutators: [HardenCrashTests.create], options: options, + scheduler: scheduler, socketFactory: sockets) + } + + func connect() throws -> MockSocket { try Self.connect(sockets, scheduler) } + + func poke(_ socket: MockSocket, from base: String?, to cookie: String, _ part: String) { + Self.poke(socket, scheduler, from: base, to: cookie, part) + } + + /// The message `query` fails to materialize with, as JS's builder + /// throws it. + func invalidMessage(_ query: AnyQuery) -> String? { + do { + try zero.materialize(query).destroy() + return nil + } catch { + return String(describing: error) + } + } + + /// Opens the newest socket and acknowledges it. + static func connect( + _ sockets: MockSocketFactory, _ scheduler: ManualScheduler + ) throws + -> MockSocket + { + scheduler.runDueTimers() + let socket = try #require(sockets.sockets.last) + socket.receive(text: #"["connected",{"wsid":"w"}]"#) + scheduler.runDueTimers() + return socket + } + + /// A poke of one part. + static func poke( + _ socket: MockSocket, _ scheduler: ManualScheduler, from base: String?, to cookie: String, + _ part: String + ) { + let base = base.map { "\"\($0)\"" } ?? "null" + socket.receive(text: #"["pokeStart",{"pokeID":"\#(cookie)","baseCookie":\#(base)}]"#) + socket.receive(text: #"["pokePart",{"pokeID":"\#(cookie)",\#(part)}]"#) + socket.receive(text: #"["pokeEnd",{"pokeID":"\#(cookie)","cookie":"\#(cookie)"}]"#) + scheduler.runDueTimers() + } + } + + // MARK: - Rows the server sends + + /// An `Int` column holding -2^63, a number JSON can carry and a bigint + /// column of Postgres can hold. JS hands the number through. Swift: + /// `Int(exactly:)` gives `Int.min`, and `abs(Int.min)` overflows. + @Test func intColumnAtInt64Min() async { + await #expect(processExitsWith: .success) { + let decoded = try? Int(zeroValue: .number(-0x1p63)) + // Not an integer within 2^53: a decoding error, not a trap. + precondition(decoded == nil) + } + } + + // MARK: - Last mutation IDs the server sends + + /// The server's last mutation ID for this client is any JSON number. JS + /// gives the next mutation the ID `lmid + 1`, a number. Swift converts + /// it to `Transaction.mutationID`, an `Int`, so it rejects a poke whose + /// last mutation ID isn't a safe integer and disconnects. + @Test func hugeLastMutationIDThenMutate() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.hugeLastMutationIDThenMutateScenario() + } + } + + static func hugeLastMutationIDThenMutateScenario() throws { + let h = try Harness() + let socket = try h.connect() + h.poke(socket, from: nil, to: "1", #""lastMutationIDChanges":{"\#(h.zero.clientID)":1e300}"#) + precondition(socket.closed) + precondition(h.zero.connection.state.status == .error) + // Rejected, as every mutation is while the connection is in error. + let result = h.zero.mutate(Self.create("a")) + precondition(h.zero.engine.store.pending.isEmpty && result.client.isSettled) + } + + /// The same through storage: a pending mutation whose stored ID is huge + /// (a tampered or corrupt file). Opening the client replays it. JS + /// replays it with that ID; Swift drops it and logs an error, rather than + /// trap on every launch. + @Test func hugeStoredMutationIDReplayed() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.hugeStoredMutationIDReplayedScenario() + } + } + + static func hugeStoredMutationIDReplayedScenario() throws { + let directory = NSTemporaryDirectory() + "harden-" + randomID() + defer { try? FileManager.default.removeItem(atPath: directory) } + func open() throws -> Zero { + var options = ZeroOptions( + userID: "u", storageKey: "k", storage: .sqlite(directory: directory)) + options.logSink = RecordingLogSink() + return try Zero( + schema: .test, mutators: [HardenCrashTests.create], options: options, + scheduler: ManualScheduler(now: 1_700_000_000_000), socketFactory: nil) + } + let first = try open() + first.mutate(Self.create("a")) + first.close() + let path = + directory + "/" + + ReplicaDatabase.fileName( + ReplicaDatabase.name( + userID: "u", storageKey: "k", mutateURL: nil, queryURL: nil, + schemaHash: ZeroSchema.test.clientSchema.hash)) + let db = try SQLiteDatabase(path: path) + try db.run("UPDATE mutations SET id = 1e300") + var ids: [Double] = [] + try db.query("SELECT id FROM mutations") { ids.append($0.real(0)) } + precondition(ids == [1e300]) + let second = try open() + precondition(second.engine.store.pending.isEmpty) + second.close() + } + + // MARK: - Row patches the server sends + + /// `update` row ops whose `merge` leaves the row without its primary key + /// (no row existed, so the merge is the whole row). Both rows have a null + /// key, so to the table's source they are one row. JS: `MemorySource`'s + /// `assert` throws "Row already exists", the poke fails, and the client + /// disconnects. Swift rejects the poke before the sources see it. + @Test func updateOpsLeavingRowsWithoutPrimaryKey() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.updateOpsLeavingRowsWithoutPrimaryKeyScenario() + } + } + + static func updateOpsLeavingRowsWithoutPrimaryKeyScenario() throws { + let h = try Harness() + let view = try h.zero.materialize(zql.issue) + defer { view.destroy() } + let socket = try h.connect() + h.poke( + socket, from: nil, to: "1", + #""rowsPatch":[{"op":"update","tableName":"issues","id":{"id":"a"},"merge":{"title":"x"}},{"op":"update","tableName":"issues","id":{"id":"b"},"merge":{"title":"y"}}]"# + ) + precondition(socket.closed) + precondition(view.data.entries.isEmpty) + } + + /// An `update` that moves a row to the primary key of another, then + /// deletes of both. JS: `MemorySource` throws "Row not found" and the + /// client disconnects. Swift rejects the update, leaving both rows. + @Test func updateOpChangingPrimaryKeyThenDeletes() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.updateOpChangingPrimaryKeyThenDeletesScenario() + } + } + + static func updateOpChangingPrimaryKeyThenDeletesScenario() throws { + let h = try Harness() + let view = try h.zero.materialize(zql.issue) + defer { view.destroy() } + let socket = try h.connect() + func row(_ id: String) -> String { + #"{"op":"put","tableName":"issues","value":{"id":"\#(id)","title":"","description":"","closed":false,"owner_id":null,"createdAt":1}}"# + } + h.poke(socket, from: nil, to: "1", #""rowsPatch":[\#(row("a")),\#(row("b"))]"#) + h.poke( + socket, from: "1", to: "2", + #""rowsPatch":[{"op":"update","tableName":"issues","id":{"id":"a"},"merge":{"id":"b"}}]"#) + precondition(socket.closed) + precondition(view.data.entries.map { $0.row["id"] } == ["a", "b"]) + h.zero.connection.connect() + let next = try h.connect() + precondition(next !== socket) + h.poke( + next, from: "1", to: "3", + #""rowsPatch":[{"op":"del","tableName":"issues","id":{"id":"a"}},{"op":"del","tableName":"issues","id":{"id":"b"}}]"# + ) + precondition(!next.closed) + precondition(view.data.entries.isEmpty) + } + + /// Rows that don't match the schema: wrong types, missing and extra + /// columns, and a table the schema doesn't have. JS keeps them as sent; + /// a typed read of a row that doesn't fit throws rather than traps. + @Test func rowsThatDoNotMatchTheSchema() async throws { + let h = try Harness() + let view = try h.zero.materialize(zql.issue.orderBy(\.id, .asc)) + defer { view.destroy() } + let socket = try h.connect() + h.poke( + socket, from: nil, to: "1", + #""rowsPatch":[{"op":"put","tableName":"issues","value":{"id":1,"title":2,"description":null,"closed":"yes","owner_id":{},"createdAt":"x","extra":[1]}},{"op":"put","tableName":"issues","value":{"id":"m"}},{"op":"put","tableName":"issues","value":{"id":true,"createdAt":1e400}},{"op":"put","tableName":"nope","value":{"id":"z"}}]"# + ) + #expect(!socket.closed) + #expect(view.data.entries.count == 3) + await #expect(throws: ZeroValueError.self) { try await h.zero.run(zql.issue) } + let one = try await h.zero.run(zql.issue.where(\.id, "m").one()) + #expect(one?.title == "" && one?.createdAt == 0 && one?.closed == false) + } + + /// A row without its primary key, or with a null one. JS: parsing the key + /// throws, the poke fails, and the client reconnects. + @Test(arguments: [#"{"title":"t"}"#, #"{"id":null}"#, #"{"id":[1]}"#]) + func rowWithoutAUsablePrimaryKey(_ value: String) throws { + let h = try Harness() + let socket = try h.connect() + h.poke( + socket, from: nil, to: "1", + #""rowsPatch":[{"op":"put","tableName":"issues","value":\#(value)}]"# + ) + #expect(socket.closed) + } + + // MARK: - Timers + + /// A backoff error from zero-cache with a huge `minBackoffMs`. JS passes + /// it to `setTimeout`, which fires at once for a delay past 2^31 - 1 ms; + /// so does `SystemScheduler`, which used to trap making a `Duration` of + /// it. + @Test(.timeLimit(.minutes(1))) func hugeServerBackoff() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.hugeServerBackoffScenario() + } + } + + static func hugeServerBackoffScenario() async throws { + let sockets = MockSocketFactory() + let zero = try Zero( + schema: .test, + options: ZeroOptions( + cacheURL: "http://zero.test", userID: "u", storageKey: randomID(), storage: .memory), + scheduler: SystemScheduler(), socketFactory: sockets) + try await Task.sleep(for: .milliseconds(100)) + let socket = try #require(sockets.sockets.last) + socket.receive( + text: + #"["error",{"kind":"Rebalance","message":"m","origin":"zeroCache","minBackoffMs":1e300}]"# + ) + try await Task.sleep(for: .milliseconds(100)) + zero.close() + } + + /// `pingTimeoutMs: .infinity`. JS: `setTimeout(f, Infinity)` fires at + /// once, and so does `SystemScheduler`. + @Test(.timeLimit(.minutes(1))) func infinitePingTimeout() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.infinitePingTimeoutScenario() + } + } + + static func infinitePingTimeoutScenario() async throws { + let sockets = MockSocketFactory() + var options = ZeroOptions( + cacheURL: "http://zero.test", userID: "u", storageKey: randomID(), storage: .memory) + options.pingTimeoutMs = .infinity + let zero = try Zero( + schema: .test, options: options, scheduler: SystemScheduler(), socketFactory: sockets) + try await Task.sleep(for: .milliseconds(100)) + let socket = try #require(sockets.sockets.last) + socket.receive(text: #"["connected",{"wsid":"w"}]"#) + try await Task.sleep(for: .milliseconds(100)) + zero.close() + } + + // MARK: - Query arguments the app passes + + /// `limit(-1)`. JS throws "Limit must be non-negative" from `limit`. + /// Swift: the query is invalid, and materializing, running, or + /// preloading it throws that message. + @Test func negativeLimit() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.invalidQueryScenario( + zql.issue.limit(-1).any, "Limit must be non-negative") + } + } + + static func invalidQueryScenario(_ query: AnyQuery, _ message: String) async throws { + let h = try Harness() + precondition(h.invalidMessage(query) == message) + do { + // `run` is async in JS: the error rejects its result. + _ = try await h.zero.run(query).value + preconditionFailure("run succeeded") + } catch { + precondition(String(describing: error) == message) + } + let preload = h.zero.preload(query.nameAndArgs("q", [])) + do { + try await preload.complete.value + preconditionFailure("preload completed") + } catch { + precondition(String(describing: error) == message) + } + } + + /// A limit past 2^31 - 1, such as `Int.max` for "no limit". JS throws + /// "Limit must be an integer" (`(limit | 0) !== limit`). + @Test func limitPastInt32() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.invalidQueryScenario( + zql.issue.limit(Int.max).any, "Limit must be an integer") + } + } + + /// `whereExists` whose subquery asks for `related` rows. JS: the + /// builder's `assert` throws "EXISTS subqueries must not have related" + /// when the query is materialized, and so does Swift. + @Test func existsSubqueryWithRelated() async { + await #expect(processExitsWith: .success) { + try await HardenCrashTests.existsSubqueryWithRelatedScenario() + } + } + + static func existsSubqueryWithRelatedScenario() throws { + let h = try Harness() + precondition( + h.invalidMessage(zql.issue.whereExists(\.comments) { $0.related(\.author) }.any) + == "EXISTS subqueries must not have related") + } + + /// `limit` on a many-to-many relationship (through a junction table), + /// such as the first label of each issue. JS throws `NotImplementedError` + /// ("Limit is not supported in junction relationships yet") from the + /// query builder; Swift's query is invalid, even when the limit is in an + /// `exists` inside a `related`. + @Test func limitOnAJunctionRelationship() async { + await #expect(processExitsWith: .success) { + let message = + "Limit is not supported in junction relationships yet. Junction relationship being limited: labels" + try await HardenCrashTests.invalidQueryScenario( + zql.issue.related(\.labels) { $0.limit(1) }.any, message) + try await HardenCrashTests.invalidQueryScenario( + zql.comment.related(\.issue) { $0.whereExists(\.labels) { $0.limit(1) } }.any, message) + } + } + } +#endif diff --git a/swift/Tests/ZeroTests/HardenMutationTests.swift b/swift/Tests/ZeroTests/HardenMutationTests.swift new file mode 100644 index 0000000000..ee9f8379e1 --- /dev/null +++ b/swift/Tests/ZeroTests/HardenMutationTests.swift @@ -0,0 +1,277 @@ +import Testing +import ZeroTesting + +@testable import Zero + +/// Mutation edge cases the client scenario harness can't express: a mutate +/// from inside a mutator or a view listener, and the Swift-only mutation +/// outcomes. Expected values come from the JS client +/// (`packages/zero-client/src/client`, `packages/replicache/src`) where it +/// has a counterpart. +@Suite @MainActor struct HardenMutationTests { + struct Seen: Codable, Sendable { + var mid: Int + var seen: [String] + } + + @MainActor enum Nested { + static var zero: Zero? + static var inner: MutatorResult? + } + + static func row(_ id: String) -> Issue { + Issue(id: id, title: id, description: "", closed: false, createdAt: 1) + } + + static func ids(_ tx: Transaction) throws -> [String] { + try tx.run(zql.issue).map(\.id) + } + + static let create = defineMutator("t.create") { (tx, id: String) -> Seen in + try tx.mutate.insert(row(id)) + return Seen(mid: tx.mutationID, seen: try ids(tx)) + } + static let createThenThrow = defineMutator("t.createThenThrow") { (tx, id: String) in + try tx.mutate.insert(row(id)) + throw ApplicationError("midway") + } + /// Writes, calls `zero.mutate` without waiting for it, and writes again. + static let outer = defineMutator("t.outer") { (tx, _: String) -> Seen in + try tx.mutate.insert(row("outer1")) + Nested.inner = Nested.zero?.mutate(create("inner")) + try tx.mutate.insert(row("outer2")) + return Seen(mid: tx.mutationID, seen: try ids(tx)) + } + + /// Closes the `Zero` it runs in, then writes again. + static let closing = defineMutator("t.closing") { (tx, _: String) -> String in + try tx.mutate.insert(row("a")) + Nested.zero?.close() + try tx.mutate.insert(row("b")) + return "done" + } + + static let all = defineQuery("issues.all") { () in zql.issue.orderBy(\.id, .asc) } + + let scheduler = ManualScheduler(now: 1_700_000_000_000) + let sockets = MockSocketFactory() + + func makeZero(storageKey: String = randomID()) throws -> Zero { + var options = ZeroOptions( + cacheURL: "http://zero.test", userID: "u", storageKey: storageKey, storage: .memory) + options.logSink = RecordingLogSink() + return try Zero( + schema: .test, mutators: [Self.create, Self.createThenThrow, Self.outer, Self.closing], + options: options, scheduler: scheduler, socketFactory: sockets) + } + + func connect() throws -> MockSocket { + scheduler.runDueTimers() + let socket = try #require(sockets.sockets.last) + socket.receive(text: #"["connected",{"wsid":"w"}]"#) + scheduler.runDueTimers() + return socket + } + + func poke(_ socket: MockSocket, from base: String?, to cookie: String, _ part: String) { + let base = base.map { "\"\($0)\"" } ?? "null" + socket.receive(text: #"["pokeStart",{"pokeID":"\#(cookie)","baseCookie":\#(base)}]"#) + socket.receive(text: #"["pokePart",{"pokeID":"\#(cookie)",\#(part)}]"#) + socket.receive(text: #"["pokeEnd",{"pokeID":"\#(cookie)","cookie":"\#(cookie)"}]"#) + scheduler.runDueTimers() + } + + static func serverRow(_ id: String) -> String { + #"{"op":"put","tableName":"issues","value":{"id":"\#(id)","title":"\#(id)","description":"","closed":false,"owner_id":null,"createdAt":1}}"# + } + + func pushed(on socket: MockSocket) -> [JSONValue] { + socket.messages.compactMap { try? JSONParser.parse(Array($0.utf8)) } + .filter { $0[0]?.stringValue == "push" } + .flatMap { $0[1]?["mutations"]?.arrayValue ?? [] } + } + + func pushedIDs(on socket: MockSocket) -> [Double] { + pushed(on: socket).compactMap { $0["id"]?.numberValue } + } + + func pushedNames(on socket: MockSocket) -> [String] { + pushed(on: socket).compactMap { $0["name"]?.stringValue } + } + + // MARK: - Reentrant mutate + + /// A mutator that calls `zero.mutate` without awaiting it. JS runs a + /// mutation under Replicache's write lock (`#mutate` in + /// `replicache-impl.ts` takes `withWriteNoImplicitCommit`), so the inner + /// mutation waits for the outer one to commit and gets the next mutation + /// ID. Checked with a throwaway TestZero test, which printed: outer + /// `{mid: 1, seen: [outer1, outer2]}`, inner `{mid: 2, seen: [inner, + /// outer1, outer2]}`, rows `[inner, outer1, outer2]`, and the next + /// mutation `mid: 3`. + @Test func mutateFromInsideAMutatorRunsAfterIt() async throws { + let zero = try makeZero() + Nested.zero = zero + defer { Nested.zero = nil } + let socket = try connect() + + let outer = zero.mutate(Self.outer("x")) + scheduler.runDueTimers() + #expect( + try await outer.client.value + == .success(data: ["mid": 1, "seen": ["outer1", "outer2"]])) + let inner = try #require(Nested.inner) + #expect( + try await inner.client.value + == .success(data: ["mid": 2, "seen": ["inner", "outer1", "outer2"]])) + #expect( + try await zero.run(zql.issue.orderBy(\.id, .asc)).map(\.id) == [ + "inner", "outer1", "outer2", + ]) + #expect(zero.engine.store.pending.map(\.id) == [1, 2]) + + let after = zero.mutate(Self.create("after")) + scheduler.runDueTimers() + #expect( + try await after.client.value + == .success(data: ["mid": 3, "seen": ["after", "inner", "outer1", "outer2"]])) + #expect(pushedIDs(on: socket) == [1, 2, 3]) + #expect(pushedNames(on: socket) == ["t.outer", "t.create", "t.create"]) + } + + /// A mutator that closes its `Zero`. JS (checked with a throwaway + /// TestZero test) commits it: `client` is `{type: 'success', data: + /// 'done'}`, and `server` is the zero error "Zero was explicitly closed by + /// calling zero.close()". + @Test func closeFromInsideAMutator() async throws { + let zero = try makeZero() + Nested.zero = zero + defer { Nested.zero = nil } + _ = try connect() + let result = zero.mutate(Self.closing("x")) + scheduler.runDueTimers() + #expect(zero.closed) + #expect(try await result.client.value == .success(data: "done")) + #expect( + try await result.server.value + == .zeroError(message: "Zero was explicitly closed by calling zero.close()")) + } + + /// A view listener that mutates while a poke rebases the pending + /// mutations: the new mutation goes on top of the rebased ones, is pushed + /// once after them, and the view ends with every row (in JS the listener's + /// mutate waits for the poke's write to finish). + @Test func mutateFromAViewListenerDuringARebase() throws { + let zero = try makeZero() + let socket = try connect() + let view = try zero.materialize(Self.all()) + poke( + socket, from: nil, to: "01", + #""gotQueriesPatch":[{"op":"put","hash":"\#(QueryHash.hashOfNameAndArgs("issues.all", []))"}]"# + ) + zero.mutate(Self.create("a")) + scheduler.runDueTimers() + var reentered = false + _ = view.addListener { data, _, _ in + let ids = data.json?.arrayValue?.compactMap { $0["id"]?.stringValue } ?? [] + if !reentered, ids.contains("s") { + reentered = true + zero.mutate(Self.create("fromListener")) + } + } + poke(socket, from: "01", to: "02", #""rowsPatch":[\#(Self.serverRow("s"))]"#) + scheduler.runDueTimers() + #expect(reentered) + #expect( + view.data.json?.arrayValue?.compactMap { $0["id"]?.stringValue } == [ + "a", "fromListener", "s", + ]) + #expect(zero.engine.store.pending.map(\.id) == [1, 2]) + #expect(pushedIDs(on: socket) == [1, 2]) + + poke( + socket, from: "02", to: "03", + #""lastMutationIDChanges":{"\#(zero.clientID)":2},"rowsPatch":[\#(Self.serverRow("a")),\#(Self.serverRow("fromListener"))]"# + ) + #expect(zero.engine.store.pending.isEmpty) + #expect( + view.data.json?.arrayValue?.compactMap { $0["id"]?.stringValue } == [ + "a", "fromListener", "s", + ]) + view.destroy() + } + + // MARK: - Mutation outcomes (Swift only) + + /// The outcome is reported once: by the lmid that confirms the mutation, + /// not again by a push response that arrives after it. + @Test func outcomeIsReportedOnceWhenAPushResponseFollowsTheLmid() throws { + let zero = try makeZero() + var outcomes: [MutationServerOutcome] = [] + _ = zero.addMutationOutcomeListener { outcomes.append($0) } + let socket = try connect() + zero.mutate(Self.create("a")) + scheduler.runDueTimers() + poke(socket, from: nil, to: "01", #""lastMutationIDChanges":{"\#(zero.clientID)":1}"#) + socket.receive( + text: + #"["pushResponse",{"mutations":[{"id":{"clientID":"\#(zero.clientID)","id":1},"result":{"error":"app","message":"late"}}]}]"# + ) + scheduler.runDueTimers() + #expect( + outcomes == [ + MutationServerOutcome( + mutationID: 1, name: "t.create", args: "a", outcome: .success(data: nil)) + ]) + } + + /// A mutation whose mutator threw on the client was never sent, so it + /// has no server outcome; the next mutation takes its ID and reports its + /// own name and arguments. + @Test func aMutationThatThrewOnTheClientHasNoOutcome() throws { + let zero = try makeZero() + var outcomes: [MutationServerOutcome] = [] + _ = zero.addMutationOutcomeListener { outcomes.append($0) } + let socket = try connect() + zero.mutate(Self.createThenThrow("x")) + zero.mutate(Self.create("y")) + scheduler.runDueTimers() + socket.receive( + text: + #"["pushResponse",{"mutations":[{"id":{"clientID":"\#(zero.clientID)","id":1},"result":{"data":1}}]}]"# + ) + poke(socket, from: nil, to: "01", #""lastMutationIDChanges":{"\#(zero.clientID)":1}"#) + #expect( + outcomes == [ + MutationServerOutcome( + mutationID: 1, name: "t.create", args: "y", outcome: .success(data: 1)) + ]) + } + + /// Listeners added before the server lost the client hear the outcomes + /// of the mutations of the client that replaces it. + @Test func outcomesOfTheReplacementClientAreReported() throws { + let zero = try makeZero() + var outcomes: [MutationServerOutcome] = [] + _ = zero.addMutationOutcomeListener { outcomes.append($0) } + let first = try connect() + first.receive( + text: #"["error",{"kind":"ClientNotFound","message":"gone","origin":"zeroCache"}]"#) + scheduler.runDueTimers() + let second = try connect() + #expect(second !== first) + zero.mutate(Self.create("a")) + scheduler.runDueTimers() + second.receive( + text: + #"["pushResponse",{"mutations":[{"id":{"clientID":"\#(zero.clientID)","id":1},"result":{"error":"app","message":"no","details":{"code":1}}}]}]"# + ) + scheduler.runDueTimers() + #expect( + outcomes == [ + MutationServerOutcome( + mutationID: 1, name: "t.create", args: "a", + outcome: .appError(message: "no", details: ["code": 1])) + ]) + } +} diff --git a/swift/Tests/ZeroTests/HardenStorageTests.swift b/swift/Tests/ZeroTests/HardenStorageTests.swift new file mode 100644 index 0000000000..73bbcc430c --- /dev/null +++ b/swift/Tests/ZeroTests/HardenStorageTests.swift @@ -0,0 +1,456 @@ +import CSQLite +import Foundation +import Synchronization +import Testing +import ZeroTesting + +@testable import Zero + +/// Storage and client-lifecycle edge cases, checked against the JS client +/// (`packages/replicache/src/persist`, `replicache-impl.ts`, +/// `packages/zero-client/src/client/zero.ts`) or, where JS has no +/// counterpart, against what DESIGN.md promises: no crash, no lost pending +/// mutations, and no silent divergence from the server. +@Suite(.serialized) @MainActor struct HardenStorageTests { + static let day: Double = 24 * 60 * 60_000 + static let create = defineMutator("issue.create") { (tx, id: String) in + try tx.mutate.insert( + Issue(id: id, title: "", description: "", closed: false, createdAt: 1)) + } + + /// In a directory with `SQLiteFaults.isolatedMarker` in its name, so the + /// faults these tests arm reach these files only, and those of other + /// suites don't. + let directory = NSTemporaryDirectory() + SQLiteFaults.isolatedMarker + UUID().uuidString + + final class Failures: @unchecked Sendable { var kinds: [StorageFailure.Kind] = [] } + + init() { + _ = SQLiteFaults.installed + SQLiteFaults.resetIsolated() + } + + func cleanUp() { + SQLiteFaults.resetIsolated() + try? FileManager.default.removeItem(atPath: directory) + } + + func makeZero( + storageKey: String = "k", storage: ZeroStorage? = nil, at now: Double = 0, + cacheURL: String? = nil, sockets: MockSocketFactory? = nil, + scheduler: ManualScheduler? = nil, + onStorageFailure: (@MainActor @Sendable (StorageFailure) -> Void)? = nil + ) throws -> Zero { + var options = ZeroOptions( + cacheURL: cacheURL, userID: "u", storageKey: storageKey, + storage: storage ?? .sqlite(directory: directory)) + options.onStorageFailure = onStorageFailure + options.logSink = RecordingLogSink() + return try Zero( + schema: .test, mutators: [Self.create], options: options, + scheduler: scheduler ?? ManualScheduler(now: now), socketFactory: sockets) + } + + func fileName(storageKey: String = "k") -> String { + ReplicaDatabase.fileName( + ReplicaDatabase.name( + userID: "u", storageKey: storageKey, mutateURL: nil, queryURL: nil, + schemaHash: ZeroSchema.test.clientSchema.hash)) + } + + func databasePath(storageKey: String = "k") -> String { + directory + "/" + fileName(storageKey: storageKey) + } + + /// Lets the main actor run the tasks that report write failures. + func settle(until done: () -> Bool = { false }) async throws { + for _ in 0..<50 { + if done() { return } + try await Task.sleep(for: .milliseconds(10)) + } + } + + /// Opens the newest socket and acknowledges it. + func connect(_ sockets: MockSocketFactory, _ scheduler: ManualScheduler) throws -> MockSocket { + scheduler.runDueTimers() + let socket = try #require(sockets.sockets.last) + socket.receive(text: #"["connected",{"wsid":"w"}]"#) + scheduler.runDueTimers() + return socket + } + + func poke( + _ socket: MockSocket, _ scheduler: ManualScheduler, _ id: String, from base: String?, + to cookie: String, rows: [String] + ) { + let base = base.map { "\"\($0)\"" } ?? "null" + socket.receive(text: #"["pokeStart",{"pokeID":"\#(id)","baseCookie":\#(base)}]"#) + socket.receive( + text: #"["pokePart",{"pokeID":"\#(id)","rowsPatch":[\#(rows.joined(separator: ","))]}]"#) + socket.receive(text: #"["pokeEnd",{"pokeID":"\#(id)","cookie":"\#(cookie)"}]"#) + scheduler.runDueTimers() + } + + static func row(_ id: String, title: String = "") -> String { + #"{"op":"put","tableName":"issues","value":{"id":"\#(id)","title":"\#(title)","description":"","closed":false,"owner_id":null,"createdAt":1}}"# + } + + /// The page offset of `table`'s root page in the file at `path`. + func rootPageOffset(of table: String, at path: String) throws -> Int64 { + let db = try SQLiteDatabase(path: path) + defer { db.close() } + var pageSize: Int64 = 0 + var root: Int64 = 0 + try db.query("PRAGMA page_size") { pageSize = $0.integer(0) } + try db.query("SELECT rootpage FROM sqlite_master WHERE name = ?", [.text(table)]) { + root = $0.integer(0) + } + #expect(root > 1) + return (root - 1) * pageSize + } + + // MARK: - Collecting databases + + /// The collector decides from a database's heartbeats and then deletes + /// its files, while the main actor may open that database in between: a + /// client that opens a database no client used for two days, at the + /// moment the collector of another database reads it, keeps writing to + /// files that are gone. Its pending mutation is lost at the next launch. + /// JS can't lose it this way: IndexedDB doesn't delete a database with + /// an open connection under it, and Replicache's store then fails over + /// to memory and says so (`idb-store.ts`, `#withReopen`). + @Test(.timeLimit(.minutes(1))) + func collectingADatabaseAClientJustOpenedKeepsItsMutations() async throws { + defer { cleanUp() } + let old = try makeZero(storageKey: "old", at: 0) + old.close() + let oldPath = databasePath(storageKey: "old") + let offset = try rootPageOffset(of: "clients", at: oldPath) + + let now = 2 * Self.day + 60_000 + let scheduler = ManualScheduler(now: now) + let current = try makeZero(storageKey: "current", scheduler: scheduler) + // The collector's read of the old database's clients waits. + SQLiteFaults.pauseRead(ofFile: fileName(storageKey: "old"), atOffset: offset) + scheduler.advance(by: 5 * 60_000) + for _ in 0..<500 where !SQLiteFaults.readPaused.load(ordering: .sequentiallyConsistent) { + try await Task.sleep(for: .milliseconds(10)) + } + let paused = SQLiteFaults.readPaused.load(ordering: .sequentiallyConsistent) + try #require(paused) + + // Meanwhile the app opens the old database and makes a mutation. + let reopened = try makeZero(storageKey: "old", at: now + 5 * 60_000) + // It opened the file, not a memory fallback. + #expect(reopened.clientGroupID == old.clientGroupID) + reopened.mutate(Self.create("i1")) + reopened.engine.database.flush() + SQLiteFaults.resumeRead() + current.engine.database.flush() + #expect(FileManager.default.fileExists(atPath: oldPath), "deleted while open") + reopened.close() + current.close() + + let next = try makeZero(storageKey: "old", at: now + 6 * 60_000) + #expect(next.engine.store.pending.map(\.id) == [1]) + #expect(next.clientGroupID == reopened.clientGroupID) + next.close() + } + + /// JS never collects a database of a newer format than it knows + /// (`gatherDatabaseInfoForCollect` in `collect-idb-databases.ts`: + /// `db.replicacheFormatVersion > FormatVersion.Latest` returns + /// `[false]`), so an older app binary sharing the directory, an app + /// extension or a downgrade, doesn't delete what a newer one wrote. Here + /// every `zero-*.sqlite` file whose `clients` look old is deleted, + /// whatever its layout version. + @Test(.timeLimit(.minutes(1))) + func doesNotCollectADatabaseOfANewerLayout() throws { + defer { cleanUp() } + let old = try makeZero(storageKey: "old", at: 0) + old.close() + // What a later layout version could write: the same tables, and more. + let newer = directory + "/zero-v\(ReplicaDatabase.sqliteLayoutVersion + 1)-abc.sqlite" + let db = try SQLiteDatabase(path: newer) + try db.execute( + """ + CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID; + INSERT INTO meta VALUES ('name', 'rep:zero-u-abc:8:53.x'); + CREATE TABLE clients (id TEXT, client_group_id TEXT, heartbeat REAL); + INSERT INTO clients VALUES ('c', 'g', 0); + CREATE TABLE pending (id INTEGER, mutation TEXT); + INSERT INTO pending VALUES (1, '{}'); + """) + db.close() + // A file of the app's that happens to match `zero-*.sqlite`. + let unrelated = directory + "/zero-notes.sqlite" + let notes = try SQLiteDatabase(path: unrelated) + try notes.execute("CREATE TABLE notes (body TEXT); INSERT INTO notes VALUES ('x');") + notes.close() + + let scheduler = ManualScheduler(now: 2 * Self.day + 60_000) + let current = try makeZero(storageKey: "current", scheduler: scheduler) + scheduler.advance(by: 5 * 60_000) + current.engine.database.flush() + // The collection ran: the old database of this layout is gone. + #expect(!FileManager.default.fileExists(atPath: databasePath(storageKey: "old"))) + #expect(FileManager.default.fileExists(atPath: newer)) + #expect(FileManager.default.fileExists(atPath: unrelated)) + current.close() + } + + // MARK: - Two clients of one group + + /// JS persists a client's snapshot only when its cookie is newer than the + /// stored one (`persistDD31` in `persist/persist.ts`: both + /// `compareCookiesForSnapshots(memdagBaseSnapshot, perdagBaseSnapshot) > 0` + /// checks), so a client that is behind never writes an older snapshot + /// over a newer one. Here every poke's changes and cookie are written + /// through: a client of the group that applies poke 01→02 after another + /// applied 02→03 leaves 03's rows with 02's version of the others and + /// cookie 02, a state the server never had, which the next launch shows + /// offline. + @Test(.timeLimit(.minutes(1)), arguments: [false, true]) + func aClientBehindDoesNotWriteAnOlderSnapshotOverANewerOne(inMemory: Bool) async throws { + defer { cleanUp() } + let key = randomID() + let storage: ZeroStorage = inMemory ? .memory : .sqlite(directory: directory) + let scheduler = ManualScheduler(now: 1_700_000_000_000) + let sockets = MockSocketFactory() + let first = try makeZero( + storageKey: key, storage: storage, cacheURL: "http://zero.test", sockets: sockets, + scheduler: scheduler) + let a = try connect(sockets, scheduler) + let second = try makeZero( + storageKey: key, storage: storage, cacheURL: "http://zero.test", sockets: sockets, + scheduler: scheduler) + let b = try connect(sockets, scheduler) + #expect(first.clientGroupID == second.clientGroupID) + + for s in [a, b] { + poke(s, scheduler, "p1", from: nil, to: "01", rows: [Self.row("i1", title: "v1")]) + } + poke(a, scheduler, "p2", from: "01", to: "02", rows: [Self.row("i1", title: "v2")]) + poke( + a, scheduler, "p3", from: "02", to: "03", rows: [Self.row("i1", title: "v3"), Self.row("i3")]) + // The second client is behind. + poke(b, scheduler, "p2", from: "01", to: "02", rows: [Self.row("i1", title: "v2")]) + #expect(try await first.run(zql.issue).map(\.title) == ["v3", ""]) + first.close() + second.close() + + let next = try makeZero(storageKey: key, storage: storage, at: 1_700_000_100_000) + #expect(next.engine.store.cookie == "03") + let rows = try await next.run(zql.issue.orderBy(\.id, .asc)) + #expect(rows.map(\.id) == ["i1", "i3"]) + #expect(rows.map(\.title) == ["v3", ""]) + next.close() + } + + // MARK: - Dropping databases + + /// JS drops the memory stores instances fell back to along with the + /// configured store (`dropTargets` in `collect-idb-databases.ts`, used by + /// `zero.delete()` through `dropMatchingDatabases` and by + /// `dropAllDatabases`). Here a client whose SQLite store failed to open + /// runs on a memory database that `delete()` leaves: the next client of + /// that user, falling back again, finds the deleted replica and pending + /// mutations, after the app logged the user out. + @Test(.timeLimit(.minutes(1)), arguments: [false, true]) + func deleteAlsoDropsTheMemoryFallback(dropAll: Bool) async throws { + let storage = ZeroStorage.sqlite(directory: "/dev/null/" + UUID().uuidString) + let key = randomID() + let first = try makeZero(storageKey: key, storage: storage) + first.mutate(Self.create("i1")) + if dropAll { + first.close() + await dropAllDatabases(storage) + } else { + await first.delete() + } + + let second = try makeZero(storageKey: key, storage: storage, at: 1000) + #expect(second.engine.store.pending.isEmpty) + #expect(try await second.run(zql.issue).isEmpty) + second.close() + } + + // MARK: - Bad data + + /// JS treats a store whose data is corrupt as lost client state: it drops + /// the database and calls `onClientStateNotFound`, so the next instance + /// starts on a fresh, persisted store (`#handleInvalidRefCount` and + /// `#dropDatabaseAndFireOnClientStateNotFound` in `replicache-impl.ts`). + /// A row that doesn't decode must not make every launch fall back to + /// memory, forever. + @Test(.timeLimit(.minutes(1)), arguments: ["mutations", "client_groups"]) + func aRowThatDoesNotDecodeDoesNotDisablePersistenceForever(table: String) async throws { + defer { cleanUp() } + let first = try makeZero() + first.mutate(Self.create("i1")) + first.close() + let db = try SQLiteDatabase(path: databasePath()) + try db.execute( + table == "mutations" + ? "UPDATE mutations SET args = '{not json'" + : "UPDATE client_groups SET mutator_names = '[not json'") + db.close() + + final class Calls: @unchecked Sendable { var notFound = 0 } + let calls = Calls() + let failures = Failures() + var options = ZeroOptions(userID: "u", storageKey: "k", storage: .sqlite(directory: directory)) + options.logSink = RecordingLogSink() + options.onStorageFailure = { failures.kinds.append($0.kind) } + options.onClientStateNotFound = { calls.notFound += 1 } + let second = try Zero( + schema: .test, mutators: [Self.create], options: options, + scheduler: ManualScheduler(now: 1000), socketFactory: nil) + try await settle(until: { calls.notFound > 0 }) + #expect(calls.notFound == 1) + #expect(failures.kinds == []) + #expect(second.engine.store.pending.isEmpty) + second.mutate(Self.create("i2")) + second.close() + + let third = try makeZero(at: 2000, onStorageFailure: { failures.kinds.append($0.kind) }) + try await settle() + #expect(failures.kinds == []) + #expect(third.clientGroupID == second.clientGroupID) + #expect(try await third.run(zql.issue).map(\.id) == ["i2"]) + third.close() + } + + /// A client whose database was dropped (`InvalidConnectionRequest*`) and + /// replaced: another client of the group, still on the dropped database, + /// gets the same error and drops it again. That must not delete the file + /// the replacement started, with its pending mutation. + @Test(.timeLimit(.minutes(1))) + func droppingADroppedDatabaseAgainKeepsTheNewOne() async throws { + defer { cleanUp() } + let scheduler = ManualScheduler(now: 1_700_000_000_000) + let sockets = MockSocketFactory() + let first = try makeZero(cacheURL: "http://zero.test", sockets: sockets, scheduler: scheduler) + let a = try connect(sockets, scheduler) + let second = try makeZero(cacheURL: "http://zero.test", sockets: sockets, scheduler: scheduler) + let b = try connect(sockets, scheduler) + #expect(first.clientGroupID == second.clientGroupID) + let error = + #"["error",{"kind":"InvalidConnectionRequestBaseCookie","message":"x","origin":"zeroCache"}]"# + + a.receive(text: error) + scheduler.runDueTimers() + let replacedGroup = first.clientGroupID + #expect(replacedGroup != second.clientGroupID) + first.mutate(Self.create("i1")) + first.engine.database.flush() + #expect(FileManager.default.fileExists(atPath: databasePath())) + + b.receive(text: error) + scheduler.runDueTimers() + first.engine.database.flush() + #expect(FileManager.default.fileExists(atPath: databasePath())) + first.close() + second.close() + + let next = try makeZero(at: 1_700_000_100_000) + #expect(next.clientGroupID == replacedGroup) + #expect(next.engine.store.pending.map(\.id) == [1]) + next.close() + } + + // MARK: - Writes that found the database busy + + /// The writes kept because the database was busy (`SQLiteStore.write`, + /// "written with the next") are dropped when the client closes before + /// another write, and `flush()`, which the app lifecycle calls before + /// suspension, returns without them. The mutation is gone at the next + /// launch, though no storage failure was reported. JS's SQLite store + /// doesn't count a busy database as a failure either + /// (`classifySQLiteError`), and keeps the mutation for the next persist. + @Test(.timeLimit(.minutes(1))) + func aBusyWriteIsNotLostWhenTheClientClosesBeforeTheNextWrite() async throws { + defer { cleanUp() } + let failures = Failures() + let first = try makeZero(onStorageFailure: { failures.kinds.append($0.kind) }) + first.engine.database.flush() + + let other = Box(try SQLiteDatabase(path: databasePath())) + try other.value.execute("BEGIN IMMEDIATE") + let release = Task.detached { + try? await Task.sleep(for: .seconds(5.5)) + try? other.value.execute("COMMIT") + other.value.close() + } + first.mutate(Self.create("i1")) + first.engine.database.flush() + await release.value + first.close() + try await settle() + #expect(failures.kinds == []) + + let second = try makeZero(at: 1000) + #expect(second.engine.store.pending.map(\.id) == [1]) + second.close() + } + + /// When the writes kept because the database was busy still can't be + /// written as the client closes, that is reported as a storage failure, + /// not dropped without a word. + @Test(.timeLimit(.minutes(1))) + func aBusyWriteThatStillFailsAtCloseIsReported() async throws { + defer { cleanUp() } + let failures = Failures() + let first = try makeZero(onStorageFailure: { failures.kinds.append($0.kind) }) + first.engine.database.flush() + + let other = try SQLiteDatabase(path: databasePath()) + try other.execute("BEGIN IMMEDIATE") + first.mutate(Self.create("i1")) + first.close() + try? other.execute("COMMIT") + other.close() + try await settle(until: { !failures.kinds.isEmpty }) + #expect(failures.kinds == [.ioError]) + } + + // MARK: - A full disk + + /// A disk that fills up while a large poke's snapshot is written: the + /// transaction rolls back, so the next launch has the previous snapshot, + /// cookie and rows together, and the pending mutation still to push. + @Test(.timeLimit(.minutes(1))) + func aFullDiskDuringALargePokeLeavesThePreviousSnapshotWhole() async throws { + defer { cleanUp() } + let scheduler = ManualScheduler(now: 1_700_000_000_000) + let sockets = MockSocketFactory() + let failures = Failures() + let first = try makeZero( + cacheURL: "http://zero.test", sockets: sockets, scheduler: scheduler, + onStorageFailure: { failures.kinds.append($0.kind) }) + let socket = try connect(sockets, scheduler) + poke(socket, scheduler, "p1", from: nil, to: "01", rows: [Self.row("i0")]) + first.mutate(Self.create("m1")) + scheduler.runDueTimers() + first.engine.database.flush() + + SQLiteFaults.isolatedWriteFault.store(SQLITE_FULL, ordering: .sequentiallyConsistent) + let big = String(repeating: "x", count: 2000) + poke( + socket, scheduler, "p2", from: "01", to: "02", + rows: (1...500).map { Self.row("r\($0)", title: big) }) + first.engine.database.flush() + try await settle(until: { !failures.kinds.isEmpty }) + #expect(failures.kinds == [.full]) + // The client keeps running on what it has. + #expect(try await first.run(zql.issue).count == 502) + SQLiteFaults.resetIsolated() + first.close() + + let next = try makeZero(at: 1_700_000_100_000) + #expect(next.engine.store.cookie == "01") + #expect(try await next.run(zql.issue).map(\.id).sorted() == ["i0", "m1"]) + #expect(next.engine.store.pending.map(\.id) == [1]) + next.close() + } +} diff --git a/swift/Tests/ZeroTests/SQLiteFaults.swift b/swift/Tests/ZeroTests/SQLiteFaults.swift new file mode 100644 index 0000000000..9605548aad --- /dev/null +++ b/swift/Tests/ZeroTests/SQLiteFaults.swift @@ -0,0 +1,220 @@ +import CSQLite +import Foundation +import Synchronization + +/// A SQLite VFS, made the default, that forwards to the system one and +/// fails writes or reads of the files whose path has `marker` on request. +enum SQLiteFaults { + static let marker = "sweep-storage-fault-" + /// The result code writes fail with, or 0. + static let writeFault = Atomic(0) + /// How many more writes fail; negative for all. + static let writeFaultsLeft = Atomic(0) + /// The offset in a main database file whose reads fail, or -1. + static let readFaultOffset = Atomic(-1) + /// How many reads failed. + static let readFaultHits = Atomic(0) + /// The offset in a main database file whose next read waits until + /// `resumeRead()`, or -1. Only files opened while `pausePathFragment` + /// was in their path are paused. + static let pauseReadOffset = Atomic(-1) + /// Set while a read waits. + static let readPaused = Atomic(false) + static let readResumed = Atomic(false) + nonisolated(unsafe) static var pausePathFragment: UnsafeMutablePointer? + + static func failWrites(_ code: Int32, times: Int = -1) { + writeFaultsLeft.store(times, ordering: .sequentiallyConsistent) + writeFault.store(code, ordering: .sequentiallyConsistent) + } + + static func failReads(atOffset offset: Int64) { + readFaultOffset.store(offset, ordering: .sequentiallyConsistent) + } + + static func reset() { + writeFault.store(0, ordering: .sequentiallyConsistent) + readFaultOffset.store(-1, ordering: .sequentiallyConsistent) + readFaultHits.store(0, ordering: .sequentiallyConsistent) + } + + /// The files of suites that run alongside the ones using `marker`, with + /// fault state of their own, so neither arms faults on the other's files. + static let isolatedMarker = "isolated-storage-fault-" + /// The result code every write of a file with `isolatedMarker` in its + /// path fails with, or 0. + static let isolatedWriteFault = Atomic(0) + + static func resetIsolated() { + isolatedWriteFault.store(0, ordering: .sequentiallyConsistent) + resumeRead() + } + + /// Makes the next read at `offset` of a main database file opened from + /// now on with `fragment` in its path wait until `resumeRead()`. + static func pauseRead(ofFile fragment: String, atOffset offset: Int64) { + readResumed.store(false, ordering: .sequentiallyConsistent) + readPaused.store(false, ordering: .sequentiallyConsistent) + pausePathFragment = strdup(fragment) + pauseReadOffset.store(offset, ordering: .sequentiallyConsistent) + } + + static func resumeRead() { + pauseReadOffset.store(-1, ordering: .sequentiallyConsistent) + readResumed.store(true, ordering: .sequentiallyConsistent) + } + + /// Whether the next write of a marked file fails, and with what. + static func takeWriteFault() -> Int32 { + let code = writeFault.load(ordering: .sequentiallyConsistent) + guard code != 0 else { return 0 } + let left = writeFaultsLeft.load(ordering: .sequentiallyConsistent) + if left == 0 { return 0 } + if left > 0 { + if writeFaultsLeft.subtract(1, ordering: .sequentiallyConsistent).newValue == 0 { + writeFault.store(0, ordering: .sequentiallyConsistent) + } + } + return code + } + + // A wrapped file: our methods, a tag, then the system VFS's file. + static let header = 16 + static let marked: Int64 = 1 + static let mainDB: Int64 = 2 + static let pauseTarget: Int64 = 4 + static let isolated: Int64 = 8 + + nonisolated(unsafe) static var system: UnsafeMutablePointer! + nonisolated(unsafe) static var methods: UnsafeMutablePointer! + + static let installed: Bool = { + system = sqlite3_vfs_find(nil) + let vfs = UnsafeMutablePointer.allocate(capacity: 1) + vfs.initialize(to: system.pointee) + vfs.pointee.pNext = nil + vfs.pointee.zName = UnsafePointer(strdup("sweep-faults")) + vfs.pointee.szOsFile = system.pointee.szOsFile + Int32(header) + vfs.pointee.xOpen = { _, name, file, flags, outFlags in + let inner = SQLiteFaults.inner(file!) + let rc = SQLiteFaults.system.pointee.xOpen!(SQLiteFaults.system, name, inner, flags, outFlags) + var tag: Int64 = 0 + if let name, strstr(name, SQLiteFaults.marker) != nil { tag |= SQLiteFaults.marked } + if flags & SQLITE_OPEN_MAIN_DB != 0 { tag |= SQLiteFaults.mainDB } + if let name, strstr(name, SQLiteFaults.isolatedMarker) != nil { tag |= SQLiteFaults.isolated } + if let name, let fragment = SQLiteFaults.pausePathFragment, strstr(name, fragment) != nil { + tag |= SQLiteFaults.pauseTarget + } + UnsafeMutableRawPointer(file!).advanced(by: 8).storeBytes(of: tag, as: Int64.self) + file!.pointee.pMethods = + inner.pointee.pMethods == nil ? nil : UnsafePointer(SQLiteFaults.methods) + return rc + } + + let m = UnsafeMutablePointer.allocate(capacity: 1) + m.initialize(to: sqlite3_io_methods()) + // Version 2: WAL, without memory-mapped reads, so reads reach xRead. + m.pointee.iVersion = 2 + m.pointee.xClose = { f in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xClose!(inner) + } + m.pointee.xRead = { f, buf, amt, off in + let inner = SQLiteFaults.inner(f!) + if SQLiteFaults.tag(f!) == SQLiteFaults.marked | SQLiteFaults.mainDB, + off == SQLiteFaults.readFaultOffset.load(ordering: .sequentiallyConsistent) + { + SQLiteFaults.readFaultHits.add(1, ordering: .sequentiallyConsistent) + return SQLITE_IOERR | (1 << 8) + } + if SQLiteFaults.tag(f!) & (SQLiteFaults.pauseTarget | SQLiteFaults.mainDB) + == SQLiteFaults.pauseTarget | SQLiteFaults.mainDB, + SQLiteFaults.pauseReadOffset.compareExchange( + expected: off, desired: -1, ordering: .sequentiallyConsistent + ).exchanged + { + SQLiteFaults.readPaused.store(true, ordering: .sequentiallyConsistent) + while !SQLiteFaults.readResumed.load(ordering: .sequentiallyConsistent) { usleep(1000) } + } + return SQLiteFaults.real(inner).xRead!(inner, buf, amt, off) + } + m.pointee.xWrite = { f, buf, amt, off in + let inner = SQLiteFaults.inner(f!) + if SQLiteFaults.tag(f!) & SQLiteFaults.marked != 0 { + let code = SQLiteFaults.takeWriteFault() + if code != 0 { return code } + } + if SQLiteFaults.tag(f!) & SQLiteFaults.isolated != 0 { + let code = SQLiteFaults.isolatedWriteFault.load(ordering: .sequentiallyConsistent) + if code != 0 { return code } + } + return SQLiteFaults.real(inner).xWrite!(inner, buf, amt, off) + } + m.pointee.xTruncate = { f, size in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xTruncate!(inner, size) + } + m.pointee.xSync = { f, flags in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xSync!(inner, flags) + } + m.pointee.xFileSize = { f, size in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xFileSize!(inner, size) + } + m.pointee.xLock = { f, lock in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xLock!(inner, lock) + } + m.pointee.xUnlock = { f, lock in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xUnlock!(inner, lock) + } + m.pointee.xCheckReservedLock = { f, out in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xCheckReservedLock!(inner, out) + } + m.pointee.xFileControl = { f, op, arg in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xFileControl!(inner, op, arg) + } + m.pointee.xSectorSize = { f in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xSectorSize!(inner) + } + m.pointee.xDeviceCharacteristics = { f in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xDeviceCharacteristics!(inner) + } + m.pointee.xShmMap = { f, region, size, extend, out in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xShmMap!(inner, region, size, extend, out) + } + m.pointee.xShmLock = { f, offset, n, flags in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xShmLock!(inner, offset, n, flags) + } + m.pointee.xShmBarrier = { f in + let inner = SQLiteFaults.inner(f!) + SQLiteFaults.real(inner).xShmBarrier!(inner) + } + m.pointee.xShmUnmap = { f, delete in + let inner = SQLiteFaults.inner(f!) + return SQLiteFaults.real(inner).xShmUnmap!(inner, delete) + } + methods = m + return sqlite3_vfs_register(vfs, 1) == SQLITE_OK + }() + + static func inner(_ f: UnsafeMutablePointer) -> UnsafeMutablePointer { + UnsafeMutableRawPointer(f).advanced(by: header).assumingMemoryBound(to: sqlite3_file.self) + } + + static func tag(_ f: UnsafeMutablePointer) -> Int64 { + UnsafeMutableRawPointer(f).advanced(by: 8).load(as: Int64.self) + } + + static func real(_ inner: UnsafeMutablePointer) -> sqlite3_io_methods { + inner.pointee.pMethods.pointee + } +} diff --git a/swift/Tests/ZeroTests/SweepStorageTests.swift b/swift/Tests/ZeroTests/SweepStorageTests.swift index 543ac45031..4844b839bc 100644 --- a/swift/Tests/ZeroTests/SweepStorageTests.swift +++ b/swift/Tests/ZeroTests/SweepStorageTests.swift @@ -340,169 +340,3 @@ final class Box: @unchecked Sendable { let value: T init(_ value: T) { self.value = value } } - -// MARK: - Fault injection - -/// A SQLite VFS, made the default, that forwards to the system one and -/// fails writes or reads of the files whose path has `marker` on request. -enum SQLiteFaults { - static let marker = "sweep-storage-fault-" - /// The result code writes fail with, or 0. - static let writeFault = Atomic(0) - /// How many more writes fail; negative for all. - static let writeFaultsLeft = Atomic(0) - /// The offset in a main database file whose reads fail, or -1. - static let readFaultOffset = Atomic(-1) - /// How many reads failed. - static let readFaultHits = Atomic(0) - - static func failWrites(_ code: Int32, times: Int = -1) { - writeFaultsLeft.store(times, ordering: .sequentiallyConsistent) - writeFault.store(code, ordering: .sequentiallyConsistent) - } - - static func failReads(atOffset offset: Int64) { - readFaultOffset.store(offset, ordering: .sequentiallyConsistent) - } - - static func reset() { - writeFault.store(0, ordering: .sequentiallyConsistent) - readFaultOffset.store(-1, ordering: .sequentiallyConsistent) - readFaultHits.store(0, ordering: .sequentiallyConsistent) - } - - /// Whether the next write of a marked file fails, and with what. - static func takeWriteFault() -> Int32 { - let code = writeFault.load(ordering: .sequentiallyConsistent) - guard code != 0 else { return 0 } - let left = writeFaultsLeft.load(ordering: .sequentiallyConsistent) - if left == 0 { return 0 } - if left > 0 { - if writeFaultsLeft.subtract(1, ordering: .sequentiallyConsistent).newValue == 0 { - writeFault.store(0, ordering: .sequentiallyConsistent) - } - } - return code - } - - // A wrapped file: our methods, a tag, then the system VFS's file. - static let header = 16 - static let marked: Int64 = 1 - static let mainDB: Int64 = 2 - - nonisolated(unsafe) static var system: UnsafeMutablePointer! - nonisolated(unsafe) static var methods: UnsafeMutablePointer! - - static let installed: Bool = { - system = sqlite3_vfs_find(nil) - let vfs = UnsafeMutablePointer.allocate(capacity: 1) - vfs.initialize(to: system.pointee) - vfs.pointee.pNext = nil - vfs.pointee.zName = UnsafePointer(strdup("sweep-faults")) - vfs.pointee.szOsFile = system.pointee.szOsFile + Int32(header) - vfs.pointee.xOpen = { _, name, file, flags, outFlags in - let inner = SQLiteFaults.inner(file!) - let rc = SQLiteFaults.system.pointee.xOpen!(SQLiteFaults.system, name, inner, flags, outFlags) - var tag: Int64 = 0 - if let name, strstr(name, SQLiteFaults.marker) != nil { tag |= SQLiteFaults.marked } - if flags & SQLITE_OPEN_MAIN_DB != 0 { tag |= SQLiteFaults.mainDB } - UnsafeMutableRawPointer(file!).advanced(by: 8).storeBytes(of: tag, as: Int64.self) - file!.pointee.pMethods = - inner.pointee.pMethods == nil ? nil : UnsafePointer(SQLiteFaults.methods) - return rc - } - - let m = UnsafeMutablePointer.allocate(capacity: 1) - m.initialize(to: sqlite3_io_methods()) - // Version 2: WAL, without memory-mapped reads, so reads reach xRead. - m.pointee.iVersion = 2 - m.pointee.xClose = { f in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xClose!(inner) - } - m.pointee.xRead = { f, buf, amt, off in - let inner = SQLiteFaults.inner(f!) - if SQLiteFaults.tag(f!) == SQLiteFaults.marked | SQLiteFaults.mainDB, - off == SQLiteFaults.readFaultOffset.load(ordering: .sequentiallyConsistent) - { - SQLiteFaults.readFaultHits.add(1, ordering: .sequentiallyConsistent) - return SQLITE_IOERR | (1 << 8) - } - return SQLiteFaults.real(inner).xRead!(inner, buf, amt, off) - } - m.pointee.xWrite = { f, buf, amt, off in - let inner = SQLiteFaults.inner(f!) - if SQLiteFaults.tag(f!) & SQLiteFaults.marked != 0 { - let code = SQLiteFaults.takeWriteFault() - if code != 0 { return code } - } - return SQLiteFaults.real(inner).xWrite!(inner, buf, amt, off) - } - m.pointee.xTruncate = { f, size in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xTruncate!(inner, size) - } - m.pointee.xSync = { f, flags in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xSync!(inner, flags) - } - m.pointee.xFileSize = { f, size in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xFileSize!(inner, size) - } - m.pointee.xLock = { f, lock in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xLock!(inner, lock) - } - m.pointee.xUnlock = { f, lock in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xUnlock!(inner, lock) - } - m.pointee.xCheckReservedLock = { f, out in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xCheckReservedLock!(inner, out) - } - m.pointee.xFileControl = { f, op, arg in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xFileControl!(inner, op, arg) - } - m.pointee.xSectorSize = { f in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xSectorSize!(inner) - } - m.pointee.xDeviceCharacteristics = { f in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xDeviceCharacteristics!(inner) - } - m.pointee.xShmMap = { f, region, size, extend, out in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xShmMap!(inner, region, size, extend, out) - } - m.pointee.xShmLock = { f, offset, n, flags in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xShmLock!(inner, offset, n, flags) - } - m.pointee.xShmBarrier = { f in - let inner = SQLiteFaults.inner(f!) - SQLiteFaults.real(inner).xShmBarrier!(inner) - } - m.pointee.xShmUnmap = { f, delete in - let inner = SQLiteFaults.inner(f!) - return SQLiteFaults.real(inner).xShmUnmap!(inner, delete) - } - methods = m - return sqlite3_vfs_register(vfs, 1) == SQLITE_OK - }() - - static func inner(_ f: UnsafeMutablePointer) -> UnsafeMutablePointer { - UnsafeMutableRawPointer(f).advanced(by: header).assumingMemoryBound(to: sqlite3_file.self) - } - - static func tag(_ f: UnsafeMutablePointer) -> Int64 { - UnsafeMutableRawPointer(f).advanced(by: 8).load(as: Int64.self) - } - - static func real(_ inner: UnsafeMutablePointer) -> sqlite3_io_methods { - inner.pointee.pMethods.pointee - } -} diff --git a/swift/Tests/ZeroVirtualTests/HardenVirtualTests.swift b/swift/Tests/ZeroVirtualTests/HardenVirtualTests.swift new file mode 100644 index 0000000000..d109edc84e --- /dev/null +++ b/swift/Tests/ZeroVirtualTests/HardenVirtualTests.swift @@ -0,0 +1,25 @@ +import Testing +import ZeroTesting + +@testable import ZeroVirtual + +// Exit tests run on macOS and Linux only, not in the iOS simulator. +#if os(macOS) || os(Linux) + /// Options that trap the virtualizer. Each runs in a child process (an exit + /// test), so a trap fails only its own test. + @Suite struct HardenVirtualTests { + /// `minPageSize: .max`, a way to say "one page holds everything". The + /// page size is rounded up to an even number with `n + 1`, which + /// overflows. + @Test func hugeMinPageSize() async { + await #expect(processExitsWith: .success) { + await MainActor.run { + var options = VirtualizerCore.Options( + estimatedRowHeight: 48, rowKey: \.id, toStartRow: { $0 }, listContext: "ctx") + options.minPageSize = .max + _ = VirtualizerCore(options, scheduler: ManualScheduler()) + } + } + } + } +#endif