@fougere/testing 0.9.2-alpha.0 → 0.10.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +1 -1
  2. package/dist/{comparison.d.ts → DoorContractCase.d.ts} +5 -13
  3. package/dist/DoorContractCase.d.ts.map +1 -0
  4. package/dist/{comparison.js → DoorContractCase.js} +31 -31
  5. package/dist/DoorContractCase.js.map +1 -0
  6. package/dist/DoorInput.d.ts +5 -0
  7. package/dist/DoorInput.d.ts.map +1 -0
  8. package/dist/DoorInput.js +2 -0
  9. package/dist/DoorInput.js.map +1 -0
  10. package/dist/DoorOptions.d.ts +7 -0
  11. package/dist/DoorOptions.d.ts.map +1 -0
  12. package/dist/DoorOptions.js +2 -0
  13. package/dist/DoorOptions.js.map +1 -0
  14. package/dist/all.d.ts +4 -4
  15. package/dist/all.d.ts.map +1 -1
  16. package/dist/all.js +3 -3
  17. package/dist/all.js.map +1 -1
  18. package/dist/app/TestApp.d.ts +15 -0
  19. package/dist/app/TestApp.d.ts.map +1 -0
  20. package/dist/{app.js → app/TestApp.js} +3 -3
  21. package/dist/app/TestApp.js.map +1 -0
  22. package/dist/{app.d.ts → app/TestAppOptions.d.ts} +2 -14
  23. package/dist/app/TestAppOptions.d.ts.map +1 -0
  24. package/dist/app/TestAppOptions.js +2 -0
  25. package/dist/app/TestAppOptions.js.map +1 -0
  26. package/dist/facades/CheckOptions.d.ts +6 -0
  27. package/dist/facades/CheckOptions.d.ts.map +1 -0
  28. package/dist/facades/CheckOptions.js +2 -0
  29. package/dist/facades/CheckOptions.js.map +1 -0
  30. package/dist/{doors.d.ts → facades/Verdict.d.ts} +4 -8
  31. package/dist/facades/Verdict.d.ts.map +1 -0
  32. package/dist/{doors.js → facades/Verdict.js} +4 -4
  33. package/dist/facades/Verdict.js.map +1 -0
  34. package/dist/index.d.ts +8 -5
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +7 -5
  37. package/dist/index.js.map +1 -1
  38. package/dist/load.d.ts +19 -5
  39. package/dist/load.d.ts.map +1 -1
  40. package/dist/load.js +29 -9
  41. package/dist/load.js.map +1 -1
  42. package/dist/spans.d.ts +18 -0
  43. package/dist/spans.d.ts.map +1 -0
  44. package/dist/spans.js +56 -0
  45. package/dist/spans.js.map +1 -0
  46. package/dist/statements.d.ts +17 -0
  47. package/dist/statements.d.ts.map +1 -0
  48. package/dist/statements.js +37 -0
  49. package/dist/statements.js.map +1 -0
  50. package/dist/stub/Port.d.ts +3 -0
  51. package/dist/stub/Port.d.ts.map +1 -0
  52. package/dist/stub/Port.js +2 -0
  53. package/dist/stub/Port.js.map +1 -0
  54. package/dist/{stub.d.ts → stub/Stub.d.ts} +2 -3
  55. package/dist/stub/Stub.d.ts.map +1 -0
  56. package/dist/{stub.js → stub/Stub.js} +1 -1
  57. package/dist/stub/Stub.js.map +1 -0
  58. package/dist/{sync.d.ts → sync/SyncDrift.d.ts} +2 -10
  59. package/dist/sync/SyncDrift.d.ts.map +1 -0
  60. package/dist/{sync.js → sync/SyncDrift.js} +5 -18
  61. package/dist/sync/SyncDrift.js.map +1 -0
  62. package/dist/sync/SyncedRemote.d.ts +10 -0
  63. package/dist/sync/SyncedRemote.d.ts.map +1 -0
  64. package/dist/sync/SyncedRemote.js +15 -0
  65. package/dist/sync/SyncedRemote.js.map +1 -0
  66. package/dist/vitest.d.ts.map +1 -1
  67. package/dist/vitest.js +4 -0
  68. package/dist/vitest.js.map +1 -1
  69. package/package.json +20 -10
  70. package/src/{comparison.ts → DoorContractCase.ts} +34 -40
  71. package/src/DoorInput.ts +1 -0
  72. package/src/DoorOptions.ts +7 -0
  73. package/src/all.ts +7 -5
  74. package/src/{app.ts → app/TestApp.ts} +4 -26
  75. package/src/app/TestAppOptions.ts +25 -0
  76. package/src/facades/CheckOptions.ts +6 -0
  77. package/src/{doors.ts → facades/Verdict.ts} +5 -9
  78. package/src/index.ts +8 -8
  79. package/src/load.ts +52 -13
  80. package/src/spans.ts +66 -0
  81. package/src/statements.ts +41 -0
  82. package/src/stub/Port.ts +2 -0
  83. package/src/{stub.ts → stub/Stub.ts} +1 -3
  84. package/src/{sync.ts → sync/SyncDrift.ts} +3 -23
  85. package/src/sync/SyncedRemote.ts +22 -0
  86. package/src/vitest.ts +4 -0
  87. package/dist/app.d.ts.map +0 -1
  88. package/dist/app.js.map +0 -1
  89. package/dist/comparison.d.ts.map +0 -1
  90. package/dist/comparison.js.map +0 -1
  91. package/dist/doors.d.ts.map +0 -1
  92. package/dist/doors.js.map +0 -1
  93. package/dist/stub.d.ts.map +0 -1
  94. package/dist/stub.js.map +0 -1
  95. package/dist/sync.d.ts.map +0 -1
  96. package/dist/sync.js.map +0 -1
package/dist/load.js CHANGED
@@ -1,19 +1,24 @@
1
1
  import { frameCall } from '@fougere/transport-http';
2
+ import { resolveEffectiveOperations } from '@fougere/core';
2
3
  import { sampleInput } from './sample.js';
3
4
  /** Every operation the app answers, with a body for those that take one. */
4
- export function reachableOps(app, given = {}) {
5
+ export function reachableOps(app, given = {}, remotes = {}) {
6
+ const { operations } = resolveEffectiveOperations(app.fronds, { remotes });
7
+ const hops = new Map(operations.map((op) => [`${op.handler.address}.${op.name}`, op.reach.hops]));
5
8
  const found = [];
6
9
  for (const frond of app.fronds) {
7
10
  for (const handler of frond.handlers) {
8
- // A named surface is a restricted door; the load of an app is what its default
9
- // door answers, so a surface would count the same operation twice.
11
+ // A named surface is a restricted facade; the load of an app is what its default
12
+ // facade answers, so a surface would count the same operation twice.
10
13
  if (handler.surface)
11
14
  continue;
12
15
  for (const [op, contract] of handler.operations ?? []) {
13
16
  const schema = contract.input;
17
+ const method = `${handler.address}.${op}`;
14
18
  found.push({
15
- method: `${handler.address}.${op}`,
19
+ method,
16
20
  input: schema ? sampleInput(schema, given[handler.address] ?? {}) : undefined,
21
+ hops: hops.get(method) ?? 0,
17
22
  });
18
23
  }
19
24
  }
@@ -22,8 +27,8 @@ export function reachableOps(app, given = {}) {
22
27
  }
23
28
  /** A k6 scenario, written from what the app answers. */
24
29
  export function loadScript(app, options = {}) {
25
- const door = options.door ?? 'http://127.0.0.1:3000/_fougere/call';
26
- const ops = reachableOps(app, options.given);
30
+ const facade = options.facade ?? 'http://127.0.0.1:3000/_fougere/call';
31
+ const ops = reachableOps(app, options.given, options.remotes ?? {});
27
32
  // The shape, from the one function that states it. `body` is replaced per iteration.
28
33
  const envelope = frameCall({ entity: 'ENTITY', op: 'OP' }, { params: {}, query: {}, input: undefined, state: {} }, 0);
29
34
  // What the envelope carries that an iteration does not fill in itself. Keeping
@@ -35,11 +40,17 @@ export function loadScript(app, options = {}) {
35
40
  import http from 'k6/http';
36
41
  import { check } from 'k6';
37
42
 
38
- const DOOR = ${JSON.stringify(door)};
43
+ const DOOR = ${JSON.stringify(facade)};
39
44
 
40
45
  // Every operation the app serves. A weight of 0 takes one out, visibly.
41
46
  const OPS = ${JSON.stringify(ops.map((op) => ({ ...op, weight: 1 })), null, 2)};
42
47
 
48
+ // Yours: how long an op may take here, and what one process boundary is allowed to add. Two
49
+ // numbers instead of one, because an op that crosses nothing and an op that crosses twice were
50
+ // never the same subject — hops above is read from the code, these two are facts about your
51
+ // network.
52
+ const BUDGET = { base: 300, perHop: 200 };
53
+
43
54
  export const options = {
44
55
  // Yours: a flat rate draws flat lines and there is nothing to read in them.
45
56
  stages: [
@@ -47,8 +58,17 @@ export const options = {
47
58
  { duration: '45s', target: 5 },
48
59
  { duration: '30s', target: 0 },
49
60
  ],
50
- // Yours: what counts as too slow is a fact about your users.
51
- thresholds: { http_req_failed: ['rate<0.01'], http_req_duration: ['p(95)<500'] },
61
+ // Yours: the shape of the run.
62
+ // Derived from BUDGET and each op's hops — an op that crosses two processes is not held to
63
+ // the same figure as one that never leaves. k6 reads a threshold per tag, and every call
64
+ // below is tagged with the op it made.
65
+ thresholds: {
66
+ http_req_failed: ['rate<0.01'],
67
+ ...Object.fromEntries(OPS.map((op) => [
68
+ \`http_req_duration{op:\${op.method}}\`,
69
+ [\`p(95)<\${BUDGET.base + op.hops * BUDGET.perHop}\`],
70
+ ])),
71
+ },
52
72
  };
53
73
 
54
74
  const TOTAL = OPS.reduce((sum, op) => sum + op.weight, 0);
package/dist/load.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"load.js","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAc1C,4EAA4E;AAC5E,MAAM,UAAU,YAAY,CAAC,GAAQ,EAAE,KAAK,GAAyB,EAAE;IACrE,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QAC/B,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACrC,+EAA+E;YAC/E,mEAAmE;YACnE,IAAI,OAAO,CAAC,OAAO;gBAAE,SAAS;YAC9B,KAAK,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;gBACtD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAA+B,CAAC;gBACxD,KAAK,CAAC,IAAI,CAAC;oBACT,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,EAAE;oBAClC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;iBAC9E,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,UAAU,CAAC,GAAQ,EAAE,OAAO,GAAgB,EAAE;IAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,qCAAqC,CAAC;IACnE,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7C,qFAAqF;IACrF,MAAM,QAAQ,GAAG,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAW,EAAE,CAAC,CAAC,CAAC;IAC/H,+EAA+E;IAC/E,+EAA+E;IAC/E,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAEtE,OAAO;;;;;eAKM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;;;cAGrB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;oBAwB1D,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,GAA4B,CAAC,CAAC,CAAC,CAAC,CAAC;;;;;;;;CAQvH,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { App } from '@fougere/core';
2
+ import type { FinishedSpan } from '@fougere/observability';
3
+ /**
4
+ * The steps one call opened — the operation, then whatever ran under it.
5
+ *
6
+ * The dual of [statementsOf]: that one counts what a call asked the database, this one holds
7
+ * the shape of the call itself. What it is FOR is a test about structure rather than speed —
8
+ * an op that delegates reports almost no self time, whatever the machine it runs on:
9
+ *
10
+ * ```ts
11
+ * const [op] = await spansOf(app, () => facade.list());
12
+ * expect(op.selfMs).toBeLessThan(0.1 * op.ms); // it delegates, it does not work
13
+ * ```
14
+ *
15
+ * A threshold in milliseconds tests the machine. A ratio tests the code.
16
+ */
17
+ export declare function spansOf(app: App, run: () => Promise<unknown>): Promise<FinishedSpan[]>;
18
+ //# sourceMappingURL=spans.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spans.d.ts","sourceRoot":"","sources":["../src/spans.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAK3D;;;;;;;;;;;;;GAaG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAY5F"}
package/dist/spans.js ADDED
@@ -0,0 +1,56 @@
1
+ /** One tracer per app: `app.use` has no undo, so a second call would stack a middleware. */
2
+ const installed = new WeakMap();
3
+ /**
4
+ * The steps one call opened — the operation, then whatever ran under it.
5
+ *
6
+ * The dual of [statementsOf]: that one counts what a call asked the database, this one holds
7
+ * the shape of the call itself. What it is FOR is a test about structure rather than speed —
8
+ * an op that delegates reports almost no self time, whatever the machine it runs on:
9
+ *
10
+ * ```ts
11
+ * const [op] = await spansOf(app, () => facade.list());
12
+ * expect(op.selfMs).toBeLessThan(0.1 * op.ms); // it delegates, it does not work
13
+ * ```
14
+ *
15
+ * A threshold in milliseconds tests the machine. A ratio tests the code.
16
+ */
17
+ export async function spansOf(app, run) {
18
+ const collecting = await tracerOf(app);
19
+ const spans = [];
20
+ collecting.push(spans);
21
+ try {
22
+ await run();
23
+ }
24
+ finally {
25
+ collecting.splice(collecting.indexOf(spans), 1);
26
+ }
27
+ return spans;
28
+ }
29
+ async function tracerOf(app) {
30
+ const held = installed.get(app);
31
+ if (held)
32
+ return held;
33
+ const { statementsUnder, tracing } = await observability();
34
+ const collecting = [];
35
+ installed.set(app, collecting);
36
+ // A test IS the diagnosis, so it asks for the detail a process running continuously does not.
37
+ const tracer = tracing([(span) => { for (const into of collecting)
38
+ into.push(span); }], { spanPerStatement: true });
39
+ app.use(tracer.middleware);
40
+ // Both halves, or `selfMs` would read as if the handler did the waiting itself. The
41
+ // subscription lives as long as the test process: `app.use` has no undo either, and one
42
+ // app per file is what a test file has.
43
+ await statementsUnder(tracer);
44
+ return collecting;
45
+ }
46
+ /** Optional, and dynamic for the reason `@fougere/calls` states: this package depends on none. */
47
+ async function observability() {
48
+ try {
49
+ return await import('@fougere/observability');
50
+ }
51
+ catch {
52
+ throw new Error('[spansOf] reading what a call cost needs @fougere/observability — install it, '
53
+ + 'or there is no span to read.');
54
+ }
55
+ }
56
+ //# sourceMappingURL=spans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spans.js","sourceRoot":"","sources":["../src/spans.ts"],"names":[],"mappings":"AAGA,4FAA4F;AAC5F,MAAM,SAAS,GAAG,IAAI,OAAO,EAAyB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAQ,EAAE,GAA2B;IACjE,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEvB,IAAI,CAAC;QACH,MAAM,GAAG,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACT,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,GAAQ;IAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAEtB,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC;IAC3D,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC/B,8FAA8F;IAC9F,MAAM,MAAM,GAAG,OAAO,CACpB,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,KAAK,MAAM,IAAI,IAAI,UAAU;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAC/D,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAC3B,CAAC;IACF,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC3B,oFAAoF;IACpF,wFAAwF;IACxF,wCAAwC;IACxC,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;IAE9B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,kGAAkG;AAClG,KAAK,UAAU,aAAa;IAC1B,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,gFAAgF;cAC9E,8BAA8B,CACjC,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { QueryEvent } from '@fougere/adapter-sql';
2
+ /**
3
+ * Every statement a call ran, in order.
4
+ *
5
+ * `testApp` boots on real SQLite, so this is what the handler actually asked the database —
6
+ * one per row is the shape of an N+1, and it is a number a test can refuse. A computed field
7
+ * that reads is the case: the façade hands the presenter the whole PAGE, so one query is
8
+ * possible, and nothing refuses `Promise.all(rows.map(…))` inside the field body.
9
+ *
10
+ * It REFUSES when `@fougere/adapter-sql` is absent, where the tracer degrades quietly: zero
11
+ * is what a passing assertion looks like, so an app observing nothing would turn this into a
12
+ * test that cannot fail.
13
+ *
14
+ * What it counts is the PROCESS, not the call — everything running while the block runs.
15
+ */
16
+ export declare function statementsOf(run: () => Promise<unknown>): Promise<QueryEvent[]>;
17
+ //# sourceMappingURL=statements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statements.d.ts","sourceRoot":"","sources":["../src/statements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAYrF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Every statement a call ran, in order.
3
+ *
4
+ * `testApp` boots on real SQLite, so this is what the handler actually asked the database —
5
+ * one per row is the shape of an N+1, and it is a number a test can refuse. A computed field
6
+ * that reads is the case: the façade hands the presenter the whole PAGE, so one query is
7
+ * possible, and nothing refuses `Promise.all(rows.map(…))` inside the field body.
8
+ *
9
+ * It REFUSES when `@fougere/adapter-sql` is absent, where the tracer degrades quietly: zero
10
+ * is what a passing assertion looks like, so an app observing nothing would turn this into a
11
+ * test that cannot fail.
12
+ *
13
+ * What it counts is the PROCESS, not the call — everything running while the block runs.
14
+ */
15
+ export async function statementsOf(run) {
16
+ const { onQuery } = await sql();
17
+ const ran = [];
18
+ const stop = onQuery((event) => ran.push(event));
19
+ try {
20
+ await run();
21
+ }
22
+ finally {
23
+ stop();
24
+ }
25
+ return ran;
26
+ }
27
+ /** Optional, and dynamic for the reason `@fougere/calls` states: this package depends on none. */
28
+ async function sql() {
29
+ try {
30
+ return await import('@fougere/adapter-sql');
31
+ }
32
+ catch {
33
+ throw new Error('[statementsOf] counting statements needs @fougere/adapter-sql — install it, '
34
+ + 'or the count is zero whatever the handler ran.');
35
+ }
36
+ }
37
+ //# sourceMappingURL=statements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statements.js","sourceRoot":"","sources":["../src/statements.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAA2B;IAC5D,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC;IAChC,MAAM,GAAG,GAAiB,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEjD,IAAI,CAAC;QACH,MAAM,GAAG,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACT,IAAI,EAAE,CAAC;IACT,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kGAAkG;AAClG,KAAK,UAAU,GAAG;IAChB,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,8EAA8E;cAC5E,gDAAgD,CACnD,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Anything a provider can be declared as: a class the container knows how to build. */
2
+ export type Port = abstract new (...args: never[]) => unknown;
3
+ //# sourceMappingURL=Port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Port.d.ts","sourceRoot":"","sources":["../../src/stub/Port.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,MAAM,MAAM,IAAI,GAAG,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Port.js","sourceRoot":"","sources":["../../src/stub/Port.ts"],"names":[],"mappings":""}
@@ -1,7 +1,6 @@
1
1
  import { type Mock } from 'vitest';
2
2
  import type { App } from '@fougere/core';
3
- /** Anything a provider can be declared as: a class the container knows how to build. */
4
- export type Port = abstract new (...args: never[]) => unknown;
3
+ import type { Port } from './Port.js';
5
4
  /** The double handed in place of a port — one spy per method the port declares. */
6
5
  export type Stub<T> = {
7
6
  [K in keyof T]: T[K] extends (...args: never[]) => unknown ? Mock : T[K];
@@ -12,4 +11,4 @@ export declare function methodsOf(port: Port): string[];
12
11
  export declare function stubOf<T>(port: Port): Stub<T>;
13
12
  /** Put doubles in front of ports, in every frond scope that answers under their name. */
14
13
  export declare function installStubs(app: App, ports: Port[]): Map<Port, Stub<unknown>>;
15
- //# sourceMappingURL=stub.d.ts.map
14
+ //# sourceMappingURL=Stub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stub.d.ts","sourceRoot":"","sources":["../../src/stub/Stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAM,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,mFAAmF;AACnF,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AAEnG,6EAA6E;AAC7E,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAY9C;AAED,2BAA2B;AAC3B,wBAAgB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAI7C;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAiB9E"}
@@ -39,4 +39,4 @@ export function installStubs(app, ports) {
39
39
  }
40
40
  return doubles;
41
41
  }
42
- //# sourceMappingURL=stub.js.map
42
+ //# sourceMappingURL=Stub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Stub.js","sourceRoot":"","sources":["../../src/stub/Stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAa,MAAM,QAAQ,CAAC;AAQvC,6EAA6E;AAC7E,MAAM,UAAU,SAAS,CAAC,IAAU;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,IAAI,KAAK,GAAkB,IAAI,CAAC,SAAmB,CAAC;IACpD,OAAO,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,EAAE,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,IAAI,IAAI,KAAK,aAAa;gBAAE,SAAS;YACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC9D,IAAI,OAAO,QAAQ,EAAE,KAAK,KAAK,UAAU;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC;QACD,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAkB,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AACpB,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,MAAM,CAAI,IAAU;IAClC,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC;QAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IAC/D,OAAO,MAAiB,CAAC;AAC3B,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,GAAQ,EAAE,KAAa;IAClD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAY,SAAS,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAExF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,UAAU,IAAI,CAAC,IAAI,mDAAmD;kBACpE,sFAAsF,CACzF,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,SAAS;YAAE,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1,12 +1,6 @@
1
1
  import { type Change, type SchemaDescriptor } from '@fougere/schema';
2
2
  import type { IdentityCard } from '@fougere/core';
3
- /** One entry of `.fougere/remotes.json`, written by `fougere sync`. */
4
- export interface SyncedRemote {
5
- name: string;
6
- url: string;
7
- /** Where the synced classes were written. */
8
- path: string;
9
- }
3
+ import type { SyncedRemote } from './SyncedRemote.js';
10
4
  /** What separates a consumer's synced copy from what the producer serves today. */
11
5
  export interface SyncDrift {
12
6
  frond: string;
@@ -18,12 +12,10 @@ export interface SyncDrift {
18
12
  changes: Change[];
19
13
  }[];
20
14
  }
21
- /** The remotes a project synced, read from the file `fougere sync` writes. */
22
- export declare function syncedRemotes(root: string): Promise<SyncedRemote[]>;
23
15
  /** The shapes a consumer holds for one remote frond. */
24
16
  export declare function heldShapes(remote: SyncedRemote): Promise<Map<string, SchemaDescriptor>>;
25
17
  /** What the consumer's copy no longer matches. */
26
18
  export declare function syncDriftOf(held: Map<string, SchemaDescriptor>, card: IdentityCard, frond: string): SyncDrift;
27
19
  /** Whether the copy still matches. */
28
20
  export declare function inSync(drift: SyncDrift): boolean;
29
- //# sourceMappingURL=sync.d.ts.map
21
+ //# sourceMappingURL=SyncDrift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SyncDrift.d.ts","sourceRoot":"","sources":["../../src/sync/SyncDrift.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,KAAK,MAAM,EAAE,KAAK,gBAAgB,EAAmB,MAAM,iBAAiB,CAAC;AAC5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,mFAAmF;AACnF,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gEAAgE;IAChE,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CAChD;AAED,wDAAwD;AACxD,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAW7F;AAYD,kDAAkD;AAClD,wBAAgB,WAAW,CACzB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACnC,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,MAAM,GACZ,SAAS,CAWX;AAED,sCAAsC;AACtC,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAEhD"}
@@ -1,19 +1,6 @@
1
- import { readFile } from 'node:fs/promises';
2
1
  import { join } from 'node:path';
3
2
  import { pathToFileURL } from 'node:url';
4
3
  import { Card } from '@fougere/schema';
5
- /** The remotes a project synced, read from the file `fougere sync` writes. */
6
- export async function syncedRemotes(root) {
7
- try {
8
- const raw = await readFile(join(root, '.fougere', 'remotes.json'), 'utf8');
9
- const parsed = JSON.parse(raw);
10
- return Object.entries(parsed).map(([name, one]) => ({ name, ...one }));
11
- }
12
- catch {
13
- // No file is the ordinary case: an app with no remote synced nothing.
14
- return [];
15
- }
16
- }
17
4
  /** The shapes a consumer holds for one remote frond. */
18
5
  export async function heldShapes(remote) {
19
6
  const cards = new Map();
@@ -27,15 +14,15 @@ export async function heldShapes(remote) {
27
14
  }
28
15
  return cards;
29
16
  }
30
- /** The shapes a card announces, by the name a door carries. */
17
+ /** The shapes a card announces, by the name a facade carries. */
31
18
  function servedShapes(card, frond) {
32
19
  const served = new Map();
33
20
  for (const one of card.fronds) {
34
21
  if (one.name !== frond)
35
22
  continue;
36
- for (const door of one.doors)
37
- if (door.schema)
38
- served.set(door.schema.title ?? door.name, door.schema);
23
+ for (const facade of one.facades)
24
+ if (facade.schema)
25
+ served.set(facade.schema.title ?? facade.name, facade.schema);
39
26
  }
40
27
  return served;
41
28
  }
@@ -59,4 +46,4 @@ export function syncDriftOf(held, card, frond) {
59
46
  export function inSync(drift) {
60
47
  return drift.gone.length === 0 && drift.moved.length === 0;
61
48
  }
62
- //# sourceMappingURL=sync.js.map
49
+ //# sourceMappingURL=SyncDrift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SyncDrift.js","sourceRoot":"","sources":["../../src/sync/SyncDrift.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,IAAI,EAAuD,MAAM,iBAAiB,CAAC;AAa5F,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAoB;IACnD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;IAClD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAA4B,CAAC;IAE9D,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACtD,MAAM,MAAM,GAAG,QAAkC,CAAC;QAClD,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,OAAQ,MAAqB,CAAC,SAAS,KAAK,UAAU;YAAE,SAAS;QACrG,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,iEAAiE;AACjE,SAAS,YAAY,CAAC,IAAkB,EAAE,KAAa;IACrD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAmB,CAAC;IAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC9B,IAAI,GAAG,CAAC,IAAI,KAAK,KAAK;YAAE,SAAS;QACjC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO;YAAE,IAAI,MAAM,CAAC,MAAM;gBAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IACrH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,WAAW,CACzB,IAAmC,EACnC,IAAkB,EAClB,KAAa;IAEb,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,MAAM,KAAK,GAAc,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAExD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,EAAE,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAA0B,CAAC,CAAC,CAAC;QAC9F,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,MAAM,CAAC,KAAgB;IACrC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,10 @@
1
+ /** One entry of `.fougere/remotes.json`, written by `fougere sync`. */
2
+ export interface SyncedRemote {
3
+ name: string;
4
+ url: string;
5
+ /** Where the synced classes were written. */
6
+ path: string;
7
+ }
8
+ /** The remotes a project synced, read from the file `fougere sync` writes. */
9
+ export declare function syncedRemotes(root: string): Promise<SyncedRemote[]>;
10
+ //# sourceMappingURL=SyncedRemote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SyncedRemote.d.ts","sourceRoot":"","sources":["../../src/sync/SyncedRemote.ts"],"names":[],"mappings":"AAGA,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8EAA8E;AAC9E,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CASzE"}
@@ -0,0 +1,15 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ /** The remotes a project synced, read from the file `fougere sync` writes. */
4
+ export async function syncedRemotes(root) {
5
+ try {
6
+ const raw = await readFile(join(root, '.fougere', 'remotes.json'), 'utf8');
7
+ const parsed = JSON.parse(raw);
8
+ return Object.entries(parsed).map(([name, one]) => ({ name, ...one }));
9
+ }
10
+ catch {
11
+ // No file is the ordinary case: an app with no remote synced nothing.
12
+ return [];
13
+ }
14
+ }
15
+ //# sourceMappingURL=SyncedRemote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SyncedRemote.js","sourceRoot":"","sources":["../../src/sync/SyncedRemote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAUjC,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;QAC3E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAkD,CAAC;QAChF,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyB5F"}
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,SAAS,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6B5F"}
package/dist/vitest.js CHANGED
@@ -16,6 +16,10 @@ export function fougereTest(overrides = {}) {
16
16
  // the fronds when it is about several of them.
17
17
  include: ['tests/**/*.test.ts', 'fronds/*/tests/**/*.test.ts'],
18
18
  server: { deps: { inline: [/@fougere\//, '@pothos/core'] } },
19
+ // A Fougere test boots an app, and a boot runs the scan — a tsc program over the
20
+ // project. Vitest's 5s default times that out on a cold machine, which three
21
+ // packages had each discovered and patched on their own.
22
+ testTimeout: 20_000,
19
23
  },
20
24
  };
21
25
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,SAAS,GAA4B,EAAE;IACjE,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;IAED,MAAM,IAAI,GAAG;QACX,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,IAAI,EAAE;YACJ,gFAAgF;YAChF,+CAA+C;YAC/C,OAAO,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,CAAC;YAC9D,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,EAAE;SAC7D;KACF,CAAC;IAEF,OAAO;QACL,GAAG,IAAI;QACP,GAAG,SAAS;QACZ,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,OAAiB,IAAI,EAAE,CAAC,EAAE;QACpE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,IAAc,IAAI,EAAE,CAAC,EAAE;KAC5D,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,SAAS,GAA4B,EAAE;IACjE,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,+CAA+C;IACjD,CAAC;IAED,MAAM,IAAI,GAAG;QACX,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,IAAI,EAAE;YACJ,gFAAgF;YAChF,+CAA+C;YAC/C,OAAO,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,CAAC;YAC9D,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,EAAE;YAC5D,iFAAiF;YACjF,6EAA6E;YAC7E,yDAAyD;YACzD,WAAW,EAAE,MAAM;SACpB;KACF,CAAC;IAEF,OAAO;QACL,GAAG,IAAI;QACP,GAAG,SAAS;QACZ,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,OAAiB,IAAI,EAAE,CAAC,EAAE;QACpE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,IAAc,IAAI,EAAE,CAAC,EAAE;KAC5D,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fougere/testing",
3
- "version": "0.9.2-alpha.0",
3
+ "version": "0.10.0-alpha.0",
4
4
  "description": "Tests derived from the declaration — cases, stubs and a gradient of realities.",
5
5
  "keywords": [
6
6
  "fougere",
@@ -34,20 +34,22 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "json-schema-faker": "^0.6.3",
37
- "@fougere/core": "0.9.2-alpha.0",
38
- "@fougere/container": "0.9.2-alpha.0",
39
- "@fougere/schema": "0.9.2-alpha.0",
40
- "@fougere/app": "0.9.2-alpha.0",
41
- "@fougere/compiler": "0.9.2-alpha.0",
42
- "@fougere/transport-http": "0.9.2-alpha.0",
43
- "@fougere/defaults": "0.9.2-alpha.0"
37
+ "@fougere/core": "0.10.0-alpha.0",
38
+ "@fougere/transport-http": "0.10.0-alpha.0",
39
+ "@fougere/schema": "0.10.0-alpha.0",
40
+ "@fougere/app": "0.10.0-alpha.0",
41
+ "@fougere/container": "0.10.0-alpha.0",
42
+ "@fougere/defaults": "0.10.0-alpha.0",
43
+ "@fougere/compiler": "0.10.0-alpha.0"
44
44
  },
45
45
  "publishConfig": {
46
46
  "access": "public"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "vitest": ">=2",
50
- "@fougere/adapter-graphql": "0.9.2-alpha.0"
50
+ "@fougere/adapter-graphql": "0.10.0-alpha.0",
51
+ "@fougere/observability": "0.10.0-alpha.0",
52
+ "@fougere/adapter-sql": "0.10.0-alpha.0"
51
53
  },
52
54
  "peerDependenciesMeta": {
53
55
  "vitest": {
@@ -55,11 +57,19 @@
55
57
  },
56
58
  "@fougere/adapter-graphql": {
57
59
  "optional": true
60
+ },
61
+ "@fougere/adapter-sql": {
62
+ "optional": true
63
+ },
64
+ "@fougere/observability": {
65
+ "optional": true
58
66
  }
59
67
  },
60
68
  "devDependencies": {
61
69
  "vitest": "^4.1.0",
62
- "@fougere/adapter-graphql": "0.9.2-alpha.0"
70
+ "@fougere/adapter-sql": "0.10.0-alpha.0",
71
+ "@fougere/adapter-graphql": "0.10.0-alpha.0",
72
+ "@fougere/observability": "0.10.0-alpha.0"
63
73
  },
64
74
  "scripts": {
65
75
  "build": "rm -rf dist && tsc",