@syncular/testkit 0.0.6-168 → 0.0.6-177

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/faults.d.ts CHANGED
@@ -1,4 +1,15 @@
1
1
  import type { SyncCombinedRequest, SyncPullResponse, SyncPushResponse, SyncTransport } from '@syncular/core';
2
+ export type FaultTransportOperation = 'push' | 'pull' | 'fetch';
3
+ export type FaultTransportAction = 'pass' | 'fail';
4
+ export type FaultTransportPhase = 'before' | 'after';
5
+ export interface FaultPlanStep {
6
+ operation: FaultTransportOperation | 'any';
7
+ action?: FaultTransportAction;
8
+ phase?: FaultTransportPhase;
9
+ repeat?: number;
10
+ failWith?: Error;
11
+ latencyMs?: number;
12
+ }
2
13
  export interface FaultTransportOptions {
3
14
  failAfter?: number;
4
15
  failWith?: Error;
@@ -7,6 +18,7 @@ export interface FaultTransportOptions {
7
18
  failOnPush?: boolean;
8
19
  failOnPull?: boolean;
9
20
  failOnFetch?: boolean;
21
+ plan?: FaultPlanStep[];
10
22
  onFail?: (operation: 'push' | 'pull' | 'fetch', error: Error) => void;
11
23
  onSuccess?: (operation: 'push' | 'pull' | 'fetch') => void;
12
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"faults.d.ts","sourceRoot":"","sources":["../src/faults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EAEd,MAAM,gBAAgB,CAAC;AAExB,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACtE,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;CAC5D;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,MAAM,mBAAmB,CAAC;IACpC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,qBAAqB,CAAC,KAAK,IAAI,CAAC;CAC/D;AAED,wBAAgB,UAAU,CACxB,aAAa,EAAE,aAAa,EAC5B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CAgHtB;AAED,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE;IAC5C,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB,GAAG,aAAa,CAkChB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,aAAa,CAAC;IACzB,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,aAAa,CAC3B,aAAa,EAAE,aAAa,GAC3B,wBAAwB,CAyB1B"}
1
+ {"version":3,"file":"faults.d.ts","sourceRoot":"","sources":["../src/faults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EAEd,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,uBAAuB,GAAG,KAAK,CAAC;IAC3C,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,aAAa,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACtE,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,KAAK,IAAI,CAAC;CAC5D;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,MAAM,mBAAmB,CAAC;IACpC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,qBAAqB,CAAC,KAAK,IAAI,CAAC;CAC/D;AAkBD,wBAAgB,UAAU,CACxB,aAAa,EAAE,aAAa,EAC5B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CA6KtB;AA4BD,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE;IAC5C,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,SAAS,CAAC,EAAE,UAAU,CAAC;CACxB,GAAG,aAAa,CAkChB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,aAAa,CAAC;IACzB,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED,wBAAgB,aAAa,CAC3B,aAAa,EAAE,aAAa,GAC3B,wBAAwB,CAyB1B"}
package/dist/faults.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export function withFaults(baseTransport, options = {}) {
2
2
  let currentOptions = { ...options };
3
+ let currentPlan = createActivePlan(currentOptions.plan);
3
4
  const state = {
4
5
  pushCount: 0,
5
6
  pullCount: 0,
@@ -7,22 +8,30 @@ export function withFaults(baseTransport, options = {}) {
7
8
  failureCount: 0,
8
9
  };
9
10
  const defaultError = new Error('Simulated transport error');
10
- const maybeDelay = async () => {
11
- if (currentOptions.latencyMs && currentOptions.latencyMs > 0) {
12
- await new Promise((resolve) => setTimeout(resolve, currentOptions.latencyMs));
11
+ const maybeDelay = async (extraLatencyMs = 0) => {
12
+ const latencyMs = (currentOptions.latencyMs ?? 0) + extraLatencyMs;
13
+ if (latencyMs > 0) {
14
+ await new Promise((resolve) => setTimeout(resolve, latencyMs));
13
15
  }
14
16
  };
15
- const shouldFail = (operation, count) => {
16
- if (operation === 'push' && currentOptions.failOnPull) {
17
- return false;
17
+ const shouldTargetOperation = (operation) => {
18
+ const hasTargets = currentOptions.failOnPush === true ||
19
+ currentOptions.failOnPull === true ||
20
+ currentOptions.failOnFetch === true;
21
+ if (!hasTargets) {
22
+ return true;
18
23
  }
19
- if (operation === 'pull' && currentOptions.failOnPush) {
20
- return false;
24
+ if (operation === 'push') {
25
+ return currentOptions.failOnPush === true;
21
26
  }
22
- if (operation === 'fetch' && !currentOptions.failOnFetch) {
23
- if (currentOptions.failOnPush || currentOptions.failOnPull) {
24
- return false;
25
- }
27
+ if (operation === 'pull') {
28
+ return currentOptions.failOnPull === true;
29
+ }
30
+ return currentOptions.failOnFetch === true;
31
+ };
32
+ const shouldFail = (operation, count) => {
33
+ if (!shouldTargetOperation(operation)) {
34
+ return false;
26
35
  }
27
36
  if (currentOptions.failAfter !== undefined &&
28
37
  count >= currentOptions.failAfter) {
@@ -34,39 +43,63 @@ export function withFaults(baseTransport, options = {}) {
34
43
  return false;
35
44
  };
36
45
  const getError = () => currentOptions.failWith ?? defaultError;
46
+ const fail = (operation, error) => {
47
+ state.failureCount++;
48
+ currentOptions.onFail?.(operation, error);
49
+ throw error;
50
+ };
51
+ const recordSuccess = (operation) => {
52
+ if (operation === 'push') {
53
+ state.pushCount++;
54
+ return;
55
+ }
56
+ if (operation === 'pull') {
57
+ state.pullCount++;
58
+ return;
59
+ }
60
+ state.fetchCount++;
61
+ };
62
+ const takePlannedDecision = (operation) => {
63
+ const step = currentPlan.find((candidate) => candidate.remaining > 0 &&
64
+ (candidate.operation === 'any' || candidate.operation === operation));
65
+ if (!step) {
66
+ return null;
67
+ }
68
+ step.remaining -= 1;
69
+ return {
70
+ action: step.action,
71
+ phase: step.phase,
72
+ error: step.failWith ?? getError(),
73
+ latencyMs: step.latencyMs ?? 0,
74
+ };
75
+ };
76
+ const runWithFaults = async (operation, count, run) => {
77
+ const plannedDecision = takePlannedDecision(operation);
78
+ await maybeDelay(plannedDecision?.latencyMs ?? 0);
79
+ if (plannedDecision?.action === 'fail' &&
80
+ plannedDecision.phase === 'before') {
81
+ return fail(operation, plannedDecision.error);
82
+ }
83
+ if (!plannedDecision && shouldFail(operation, count)) {
84
+ return fail(operation, getError());
85
+ }
86
+ const result = await run();
87
+ recordSuccess(operation);
88
+ if (plannedDecision?.action === 'fail' &&
89
+ plannedDecision.phase === 'after') {
90
+ return fail(operation, plannedDecision.error);
91
+ }
92
+ currentOptions.onSuccess?.(operation);
93
+ return result;
94
+ };
37
95
  const transport = {
38
96
  async sync(request, transportOptions) {
39
- await maybeDelay();
40
97
  const operation = request.push ? 'push' : 'pull';
41
98
  const count = operation === 'push' ? state.pushCount : state.pullCount;
42
- if (shouldFail(operation, count)) {
43
- const error = getError();
44
- state.failureCount++;
45
- currentOptions.onFail?.(operation, error);
46
- throw error;
47
- }
48
- if (operation === 'push') {
49
- state.pushCount++;
50
- }
51
- else {
52
- state.pullCount++;
53
- }
54
- const result = await baseTransport.sync(request, transportOptions);
55
- currentOptions.onSuccess?.(operation);
56
- return result;
99
+ return runWithFaults(operation, count, () => baseTransport.sync(request, transportOptions));
57
100
  },
58
101
  async fetchSnapshotChunk(request, transportOptions) {
59
- await maybeDelay();
60
- if (shouldFail('fetch', state.fetchCount)) {
61
- const error = getError();
62
- state.failureCount++;
63
- currentOptions.onFail?.('fetch', error);
64
- throw error;
65
- }
66
- state.fetchCount++;
67
- const result = await baseTransport.fetchSnapshotChunk(request, transportOptions);
68
- currentOptions.onSuccess?.('fetch');
69
- return result;
102
+ return runWithFaults('fetch', state.fetchCount, () => baseTransport.fetchSnapshotChunk(request, transportOptions));
70
103
  },
71
104
  };
72
105
  return {
@@ -77,12 +110,37 @@ export function withFaults(baseTransport, options = {}) {
77
110
  state.pullCount = 0;
78
111
  state.fetchCount = 0;
79
112
  state.failureCount = 0;
113
+ currentPlan = createActivePlan(currentOptions.plan);
80
114
  },
81
115
  setOptions: (newOptions) => {
82
116
  currentOptions = { ...currentOptions, ...newOptions };
117
+ if ('plan' in newOptions) {
118
+ currentPlan = createActivePlan(currentOptions.plan);
119
+ }
83
120
  },
84
121
  };
85
122
  }
123
+ function createActivePlan(plan) {
124
+ if (!plan || plan.length === 0) {
125
+ return [];
126
+ }
127
+ return plan.flatMap((step) => {
128
+ const remaining = step.repeat ?? 1;
129
+ if (remaining <= 0) {
130
+ return [];
131
+ }
132
+ return [
133
+ {
134
+ operation: step.operation,
135
+ action: step.action ?? 'fail',
136
+ phase: step.phase ?? 'before',
137
+ remaining,
138
+ failWith: step.failWith,
139
+ latencyMs: step.latencyMs,
140
+ },
141
+ ];
142
+ });
143
+ }
86
144
  export function createMockTransport(options) {
87
145
  return {
88
146
  async sync(request) {
@@ -1 +1 @@
1
- {"version":3,"file":"faults.js","sourceRoot":"","sources":["../src/faults.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"faults.js","sourceRoot":"","sources":["../src/faults.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ export function createNodeHonoServer(app, options) {
4
4
  const corsEnabled = options?.cors ?? true;
5
5
  const corsAllowMethods = options?.corsAllowMethods ?? 'GET, POST, PUT, DELETE, OPTIONS';
6
6
  const corsAllowHeaders = options?.corsAllowHeaders ??
7
- 'content-type, x-actor-id, x-syncular-transport-path, x-user-id';
7
+ 'content-type, x-actor-id, x-syncular-snapshot-scopes, x-syncular-transport-path, x-user-id';
8
8
  const corsMaxAgeSeconds = options?.corsMaxAgeSeconds ?? 86400;
9
9
  return createServer(async (req, res) => {
10
10
  const url = `http://localhost${req.url ?? '/'}`;
@@ -1 +1 @@
1
- {"version":3,"file":"hono-node-server.js","sourceRoot":"","sources":["../src/hono-node-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hono-node-server.js","sourceRoot":"","sources":["../src/hono-node-server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/testkit",
3
- "version": "0.0.6-168",
3
+ "version": "0.0.6-177",
4
4
  "description": "Testing fixtures and utilities for Syncular",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
@@ -41,17 +41,17 @@
41
41
  "release": "bunx syncular-publish"
42
42
  },
43
43
  "dependencies": {
44
- "@syncular/client": "0.0.6-168",
45
- "@syncular/core": "0.0.6-168",
46
- "@syncular/dialect-bun-sqlite": "0.0.6-168",
47
- "@syncular/dialect-libsql": "0.0.6-168",
48
- "@syncular/dialect-pglite": "0.0.6-168",
49
- "@syncular/dialect-sqlite3": "0.0.6-168",
50
- "@syncular/server": "0.0.6-168",
51
- "@syncular/server-dialect-postgres": "0.0.6-168",
52
- "@syncular/server-dialect-sqlite": "0.0.6-168",
53
- "@syncular/server-hono": "0.0.6-168",
54
- "@syncular/transport-http": "0.0.6-168",
44
+ "@syncular/client": "0.0.6-177",
45
+ "@syncular/core": "0.0.6-177",
46
+ "@syncular/dialect-bun-sqlite": "0.0.6-177",
47
+ "@syncular/dialect-libsql": "0.0.6-177",
48
+ "@syncular/dialect-pglite": "0.0.6-177",
49
+ "@syncular/dialect-sqlite3": "0.0.6-177",
50
+ "@syncular/server": "0.0.6-177",
51
+ "@syncular/server-dialect-postgres": "0.0.6-177",
52
+ "@syncular/server-dialect-sqlite": "0.0.6-177",
53
+ "@syncular/server-hono": "0.0.6-177",
54
+ "@syncular/transport-http": "0.0.6-177",
55
55
  "hono": "^4.12.3"
56
56
  },
57
57
  "devDependencies": {
@@ -0,0 +1,123 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import type { SyncCombinedRequest } from '@syncular/core';
3
+ import { createMockTransport, withFaults } from './faults';
4
+
5
+ function createPushRequest(
6
+ clientCommitId: string
7
+ ): Pick<SyncCombinedRequest, 'clientId' | 'push'> {
8
+ return {
9
+ clientId: 'client-1',
10
+ push: {
11
+ clientCommitId,
12
+ schemaVersion: 1,
13
+ operations: [
14
+ {
15
+ table: 'tasks',
16
+ row_id: `row-${clientCommitId}`,
17
+ op: 'upsert',
18
+ payload: { title: clientCommitId, project_id: 'p1' },
19
+ base_version: null,
20
+ },
21
+ ],
22
+ },
23
+ };
24
+ }
25
+
26
+ function createPullRequest(): Pick<SyncCombinedRequest, 'clientId' | 'pull'> {
27
+ return {
28
+ clientId: 'client-1',
29
+ pull: {
30
+ limitCommits: 10,
31
+ subscriptions: [
32
+ {
33
+ id: 'tasks-p1',
34
+ table: 'tasks',
35
+ scopes: { project_id: 'p1', user_id: 'u1' },
36
+ cursor: 0,
37
+ },
38
+ ],
39
+ },
40
+ };
41
+ }
42
+
43
+ describe('withFaults', () => {
44
+ it('supports deterministic post-success failures for ack-loss scenarios', async () => {
45
+ const wrapped = withFaults(createMockTransport(), {
46
+ plan: [
47
+ {
48
+ operation: 'push',
49
+ phase: 'after',
50
+ failWith: new Error('SIMULATED_ACK_LOSS'),
51
+ },
52
+ ],
53
+ });
54
+
55
+ await expect(
56
+ wrapped.transport.sync(createPushRequest('push-1'))
57
+ ).rejects.toThrow('SIMULATED_ACK_LOSS');
58
+
59
+ const retry = await wrapped.transport.sync(createPushRequest('push-2'));
60
+ expect(retry.push?.status).toBe('applied');
61
+ expect(wrapped.getState()).toEqual({
62
+ pushCount: 2,
63
+ pullCount: 0,
64
+ fetchCount: 0,
65
+ failureCount: 1,
66
+ });
67
+ });
68
+
69
+ it('consumes pass and fail steps in order for matching operations', async () => {
70
+ const wrapped = withFaults(createMockTransport(), {
71
+ plan: [
72
+ { operation: 'push', action: 'pass' },
73
+ {
74
+ operation: 'pull',
75
+ phase: 'before',
76
+ failWith: new Error('SIMULATED_PULL_DROP'),
77
+ },
78
+ ],
79
+ });
80
+
81
+ const pushResult = await wrapped.transport.sync(
82
+ createPushRequest('push-1')
83
+ );
84
+ expect(pushResult.push?.status).toBe('applied');
85
+
86
+ await expect(wrapped.transport.sync(createPullRequest())).rejects.toThrow(
87
+ 'SIMULATED_PULL_DROP'
88
+ );
89
+
90
+ const retry = await wrapped.transport.sync(createPullRequest());
91
+ expect(retry.pull?.ok).toBe(true);
92
+ expect(wrapped.getState()).toEqual({
93
+ pushCount: 1,
94
+ pullCount: 1,
95
+ fetchCount: 0,
96
+ failureCount: 1,
97
+ });
98
+ });
99
+
100
+ it('reset restores the original plan for deterministic reruns', async () => {
101
+ const wrapped = withFaults(createMockTransport(), {
102
+ plan: [
103
+ {
104
+ operation: 'fetch',
105
+ phase: 'before',
106
+ failWith: new Error('SIMULATED_FETCH_FAILURE'),
107
+ },
108
+ ],
109
+ });
110
+
111
+ await expect(
112
+ wrapped.transport.fetchSnapshotChunk({ chunkId: 'chunk-1' })
113
+ ).rejects.toThrow('SIMULATED_FETCH_FAILURE');
114
+
115
+ await wrapped.transport.fetchSnapshotChunk({ chunkId: 'chunk-1' });
116
+
117
+ wrapped.reset();
118
+
119
+ await expect(
120
+ wrapped.transport.fetchSnapshotChunk({ chunkId: 'chunk-1' })
121
+ ).rejects.toThrow('SIMULATED_FETCH_FAILURE');
122
+ });
123
+ });
package/src/faults.ts CHANGED
@@ -6,6 +6,21 @@ import type {
6
6
  SyncTransportOptions,
7
7
  } from '@syncular/core';
8
8
 
9
+ export type FaultTransportOperation = 'push' | 'pull' | 'fetch';
10
+
11
+ export type FaultTransportAction = 'pass' | 'fail';
12
+
13
+ export type FaultTransportPhase = 'before' | 'after';
14
+
15
+ export interface FaultPlanStep {
16
+ operation: FaultTransportOperation | 'any';
17
+ action?: FaultTransportAction;
18
+ phase?: FaultTransportPhase;
19
+ repeat?: number;
20
+ failWith?: Error;
21
+ latencyMs?: number;
22
+ }
23
+
9
24
  export interface FaultTransportOptions {
10
25
  failAfter?: number;
11
26
  failWith?: Error;
@@ -14,6 +29,7 @@ export interface FaultTransportOptions {
14
29
  failOnPush?: boolean;
15
30
  failOnPull?: boolean;
16
31
  failOnFetch?: boolean;
32
+ plan?: FaultPlanStep[];
17
33
  onFail?: (operation: 'push' | 'pull' | 'fetch', error: Error) => void;
18
34
  onSuccess?: (operation: 'push' | 'pull' | 'fetch') => void;
19
35
  }
@@ -32,11 +48,28 @@ export interface FaultTransportResult {
32
48
  setOptions: (options: Partial<FaultTransportOptions>) => void;
33
49
  }
34
50
 
51
+ interface ActiveFaultPlanStep {
52
+ operation: FaultPlanStep['operation'];
53
+ action: FaultTransportAction;
54
+ phase: FaultTransportPhase;
55
+ remaining: number;
56
+ failWith?: Error;
57
+ latencyMs?: number;
58
+ }
59
+
60
+ interface PlannedFaultDecision {
61
+ action: FaultTransportAction;
62
+ phase: FaultTransportPhase;
63
+ error: Error;
64
+ latencyMs: number;
65
+ }
66
+
35
67
  export function withFaults(
36
68
  baseTransport: SyncTransport,
37
69
  options: FaultTransportOptions = {}
38
70
  ): FaultTransportResult {
39
71
  let currentOptions = { ...options };
72
+ let currentPlan = createActivePlan(currentOptions.plan);
40
73
  const state: FaultTransportState = {
41
74
  pushCount: 0,
42
75
  pullCount: 0,
@@ -46,29 +79,41 @@ export function withFaults(
46
79
 
47
80
  const defaultError = new Error('Simulated transport error');
48
81
 
49
- const maybeDelay = async (): Promise<void> => {
50
- if (currentOptions.latencyMs && currentOptions.latencyMs > 0) {
51
- await new Promise((resolve) =>
52
- setTimeout(resolve, currentOptions.latencyMs)
53
- );
82
+ const maybeDelay = async (extraLatencyMs = 0): Promise<void> => {
83
+ const latencyMs = (currentOptions.latencyMs ?? 0) + extraLatencyMs;
84
+ if (latencyMs > 0) {
85
+ await new Promise((resolve) => setTimeout(resolve, latencyMs));
86
+ }
87
+ };
88
+
89
+ const shouldTargetOperation = (
90
+ operation: FaultTransportOperation
91
+ ): boolean => {
92
+ const hasTargets =
93
+ currentOptions.failOnPush === true ||
94
+ currentOptions.failOnPull === true ||
95
+ currentOptions.failOnFetch === true;
96
+
97
+ if (!hasTargets) {
98
+ return true;
99
+ }
100
+
101
+ if (operation === 'push') {
102
+ return currentOptions.failOnPush === true;
54
103
  }
104
+ if (operation === 'pull') {
105
+ return currentOptions.failOnPull === true;
106
+ }
107
+ return currentOptions.failOnFetch === true;
55
108
  };
56
109
 
57
110
  const shouldFail = (
58
- operation: 'push' | 'pull' | 'fetch',
111
+ operation: FaultTransportOperation,
59
112
  count: number
60
113
  ): boolean => {
61
- if (operation === 'push' && currentOptions.failOnPull) {
62
- return false;
63
- }
64
- if (operation === 'pull' && currentOptions.failOnPush) {
114
+ if (!shouldTargetOperation(operation)) {
65
115
  return false;
66
116
  }
67
- if (operation === 'fetch' && !currentOptions.failOnFetch) {
68
- if (currentOptions.failOnPush || currentOptions.failOnPull) {
69
- return false;
70
- }
71
- }
72
117
 
73
118
  if (
74
119
  currentOptions.failAfter !== undefined &&
@@ -86,51 +131,95 @@ export function withFaults(
86
131
 
87
132
  const getError = (): Error => currentOptions.failWith ?? defaultError;
88
133
 
89
- const transport: SyncTransport = {
90
- async sync(request, transportOptions) {
91
- await maybeDelay();
134
+ const fail = (operation: FaultTransportOperation, error: Error): never => {
135
+ state.failureCount++;
136
+ currentOptions.onFail?.(operation, error);
137
+ throw error;
138
+ };
92
139
 
93
- const operation = request.push ? 'push' : 'pull';
94
- const count = operation === 'push' ? state.pushCount : state.pullCount;
140
+ const recordSuccess = (operation: FaultTransportOperation): void => {
141
+ if (operation === 'push') {
142
+ state.pushCount++;
143
+ return;
144
+ }
145
+ if (operation === 'pull') {
146
+ state.pullCount++;
147
+ return;
148
+ }
149
+ state.fetchCount++;
150
+ };
95
151
 
96
- if (shouldFail(operation, count)) {
97
- const error = getError();
98
- state.failureCount++;
99
- currentOptions.onFail?.(operation, error);
100
- throw error;
101
- }
152
+ const takePlannedDecision = (
153
+ operation: FaultTransportOperation
154
+ ): PlannedFaultDecision | null => {
155
+ const step = currentPlan.find(
156
+ (candidate) =>
157
+ candidate.remaining > 0 &&
158
+ (candidate.operation === 'any' || candidate.operation === operation)
159
+ );
160
+ if (!step) {
161
+ return null;
162
+ }
102
163
 
103
- if (operation === 'push') {
104
- state.pushCount++;
105
- } else {
106
- state.pullCount++;
107
- }
164
+ step.remaining -= 1;
165
+ return {
166
+ action: step.action,
167
+ phase: step.phase,
168
+ error: step.failWith ?? getError(),
169
+ latencyMs: step.latencyMs ?? 0,
170
+ };
171
+ };
108
172
 
109
- const result = await baseTransport.sync(request, transportOptions);
110
- currentOptions.onSuccess?.(operation);
111
- return result;
173
+ const runWithFaults = async <Result>(
174
+ operation: FaultTransportOperation,
175
+ count: number,
176
+ run: () => Promise<Result>
177
+ ): Promise<Result> => {
178
+ const plannedDecision = takePlannedDecision(operation);
179
+ await maybeDelay(plannedDecision?.latencyMs ?? 0);
180
+
181
+ if (
182
+ plannedDecision?.action === 'fail' &&
183
+ plannedDecision.phase === 'before'
184
+ ) {
185
+ return fail(operation, plannedDecision.error);
186
+ }
187
+
188
+ if (!plannedDecision && shouldFail(operation, count)) {
189
+ return fail(operation, getError());
190
+ }
191
+
192
+ const result = await run();
193
+ recordSuccess(operation);
194
+
195
+ if (
196
+ plannedDecision?.action === 'fail' &&
197
+ plannedDecision.phase === 'after'
198
+ ) {
199
+ return fail(operation, plannedDecision.error);
200
+ }
201
+
202
+ currentOptions.onSuccess?.(operation);
203
+ return result;
204
+ };
205
+
206
+ const transport: SyncTransport = {
207
+ async sync(request, transportOptions) {
208
+ const operation: FaultTransportOperation = request.push ? 'push' : 'pull';
209
+ const count = operation === 'push' ? state.pushCount : state.pullCount;
210
+
211
+ return runWithFaults(operation, count, () =>
212
+ baseTransport.sync(request, transportOptions)
213
+ );
112
214
  },
113
215
 
114
216
  async fetchSnapshotChunk(
115
217
  request: { chunkId: string },
116
218
  transportOptions?: SyncTransportOptions
117
219
  ): Promise<Uint8Array> {
118
- await maybeDelay();
119
-
120
- if (shouldFail('fetch', state.fetchCount)) {
121
- const error = getError();
122
- state.failureCount++;
123
- currentOptions.onFail?.('fetch', error);
124
- throw error;
125
- }
126
-
127
- state.fetchCount++;
128
- const result = await baseTransport.fetchSnapshotChunk(
129
- request,
130
- transportOptions
220
+ return runWithFaults('fetch', state.fetchCount, () =>
221
+ baseTransport.fetchSnapshotChunk(request, transportOptions)
131
222
  );
132
- currentOptions.onSuccess?.('fetch');
133
- return result;
134
223
  },
135
224
  };
136
225
 
@@ -142,13 +231,43 @@ export function withFaults(
142
231
  state.pullCount = 0;
143
232
  state.fetchCount = 0;
144
233
  state.failureCount = 0;
234
+ currentPlan = createActivePlan(currentOptions.plan);
145
235
  },
146
236
  setOptions: (newOptions) => {
147
237
  currentOptions = { ...currentOptions, ...newOptions };
238
+ if ('plan' in newOptions) {
239
+ currentPlan = createActivePlan(currentOptions.plan);
240
+ }
148
241
  },
149
242
  };
150
243
  }
151
244
 
245
+ function createActivePlan(
246
+ plan: FaultPlanStep[] | undefined
247
+ ): ActiveFaultPlanStep[] {
248
+ if (!plan || plan.length === 0) {
249
+ return [];
250
+ }
251
+
252
+ return plan.flatMap((step) => {
253
+ const remaining = step.repeat ?? 1;
254
+ if (remaining <= 0) {
255
+ return [];
256
+ }
257
+
258
+ return [
259
+ {
260
+ operation: step.operation,
261
+ action: step.action ?? 'fail',
262
+ phase: step.phase ?? 'before',
263
+ remaining,
264
+ failWith: step.failWith,
265
+ latencyMs: step.latencyMs,
266
+ },
267
+ ];
268
+ });
269
+ }
270
+
152
271
  export function createMockTransport(options?: {
153
272
  pullResponse?: SyncPullResponse;
154
273
  pushResponse?: SyncPushResponse;
@@ -0,0 +1,53 @@
1
+ import { afterEach, describe, expect, it } from 'bun:test';
2
+ import { Hono } from 'hono';
3
+ import { closeNodeServer, createNodeHonoServer } from './hono-node-server';
4
+
5
+ const servers: Array<ReturnType<typeof createNodeHonoServer>> = [];
6
+
7
+ afterEach(async () => {
8
+ while (servers.length > 0) {
9
+ const server = servers.pop();
10
+ if (!server) continue;
11
+ await closeNodeServer(server);
12
+ }
13
+ });
14
+
15
+ describe('createNodeHonoServer', () => {
16
+ it('allows snapshot scope headers in CORS preflight responses', async () => {
17
+ const app = new Hono();
18
+ app.get('/sync/snapshot-chunks/:chunkId', (c) =>
19
+ c.body(new Uint8Array([1, 2, 3]))
20
+ );
21
+
22
+ const server = createNodeHonoServer(app);
23
+ servers.push(server);
24
+
25
+ await new Promise<void>((resolve) => {
26
+ server.listen(0, '127.0.0.1', resolve);
27
+ });
28
+
29
+ const address = server.address();
30
+ if (typeof address !== 'object' || !address) {
31
+ throw new Error('Failed to resolve test server address');
32
+ }
33
+
34
+ const response = await fetch(
35
+ `http://127.0.0.1:${address.port}/sync/snapshot-chunks/chunk-1`,
36
+ {
37
+ method: 'OPTIONS',
38
+ headers: {
39
+ origin: 'http://127.0.0.1:4173',
40
+ 'access-control-request-method': 'GET',
41
+ 'access-control-request-headers':
42
+ 'x-actor-id, x-syncular-snapshot-scopes',
43
+ },
44
+ }
45
+ );
46
+
47
+ expect(response.status).toBe(204);
48
+ expect(response.headers.get('access-control-allow-origin')).toBe('*');
49
+ expect(response.headers.get('access-control-allow-headers')).toContain(
50
+ 'x-syncular-snapshot-scopes'
51
+ );
52
+ });
53
+ });
@@ -30,7 +30,7 @@ export function createNodeHonoServer(
30
30
  options?.corsAllowMethods ?? 'GET, POST, PUT, DELETE, OPTIONS';
31
31
  const corsAllowHeaders =
32
32
  options?.corsAllowHeaders ??
33
- 'content-type, x-actor-id, x-syncular-transport-path, x-user-id';
33
+ 'content-type, x-actor-id, x-syncular-snapshot-scopes, x-syncular-transport-path, x-user-id';
34
34
  const corsMaxAgeSeconds = options?.corsMaxAgeSeconds ?? 86400;
35
35
 
36
36
  return createServer(async (req, res) => {