@lunora/testing 1.0.0-alpha.4 → 1.0.0-alpha.40
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/LICENSE.md +6 -0
- package/README.md +12 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{lunoraTest-BUg4Ebv8.mjs → lunoraTest-C0DZz2W9.mjs} +104 -46
- package/package.json +6 -5
package/dist/index.d.mts
CHANGED
|
@@ -141,6 +141,19 @@ type FunctionRegistry = Record<string, RegisteredAction<any, any> | RegisteredMu
|
|
|
141
141
|
* clearly-throwing stubs for unsupported surfaces.
|
|
142
142
|
*/
|
|
143
143
|
interface LunoraTestOptions {
|
|
144
|
+
/**
|
|
145
|
+
* Injectable `ctx.env` for every context (query / mutation / action). When
|
|
146
|
+
* provided, handlers that read `ctx.env.SOME_KEY` (the validated `defineEnv`
|
|
147
|
+
* surface) see this object. Left unset it stays `undefined` — matching the
|
|
148
|
+
* optional `ctx.env?` field, so graceful `ctx.env?.KEY` access still yields
|
|
149
|
+
* `undefined` rather than throwing. Not a throwing stub for exactly that
|
|
150
|
+
* reason: `env` is designed to be legitimately absent.
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* const t = lunoraTest(schema, { env: { STRIPE_KEY: "sk_test_…" } });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
env?: Record<string, unknown>;
|
|
144
157
|
/**
|
|
145
158
|
* Injectable `fetch` implementation for action contexts. When provided,
|
|
146
159
|
* `ctx.fetch` in every `action` (and `withIdentity` views) resolves to this
|
|
@@ -171,6 +184,16 @@ interface LunoraTestOptions {
|
|
|
171
184
|
* ```
|
|
172
185
|
*/
|
|
173
186
|
functions?: FunctionRegistry;
|
|
187
|
+
/**
|
|
188
|
+
* Fixed value for `ctx.now` (epoch ms) in every context. Production captures
|
|
189
|
+
* `Date.now()` once per execution; in tests a fixed `now` makes time-dependent
|
|
190
|
+
* handlers deterministic. Defaults to the wall clock at harness creation.
|
|
191
|
+
* @example
|
|
192
|
+
* ```ts
|
|
193
|
+
* const t = lunoraTest(schema, { now: 1_700_000_000_000 });
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
now?: number;
|
|
174
197
|
}
|
|
175
198
|
/**
|
|
176
199
|
* The in-memory test harness returned by {@link lunoraTest}. Mirrors the first
|
|
@@ -253,6 +276,8 @@ interface TestHarness {
|
|
|
253
276
|
*
|
|
254
277
|
* **v1 surfaces now supported:**
|
|
255
278
|
*
|
|
279
|
+
* - `ctx.env` (all contexts): inject the validated env via `options.env`; unset it
|
|
280
|
+
* stays `undefined`, matching the optional `ctx.env?` field.
|
|
256
281
|
* - `ctx.fetch` (actions): inject a custom `fetch` via `options.fetch`.
|
|
257
282
|
* - `ctx.scheduler` (mutations + actions): fully functional fake with virtual clock;
|
|
258
283
|
* control via `harness.scheduler.advance(ms)` / `runPending()` / `list()`.
|
package/dist/index.d.ts
CHANGED
|
@@ -141,6 +141,19 @@ type FunctionRegistry = Record<string, RegisteredAction<any, any> | RegisteredMu
|
|
|
141
141
|
* clearly-throwing stubs for unsupported surfaces.
|
|
142
142
|
*/
|
|
143
143
|
interface LunoraTestOptions {
|
|
144
|
+
/**
|
|
145
|
+
* Injectable `ctx.env` for every context (query / mutation / action). When
|
|
146
|
+
* provided, handlers that read `ctx.env.SOME_KEY` (the validated `defineEnv`
|
|
147
|
+
* surface) see this object. Left unset it stays `undefined` — matching the
|
|
148
|
+
* optional `ctx.env?` field, so graceful `ctx.env?.KEY` access still yields
|
|
149
|
+
* `undefined` rather than throwing. Not a throwing stub for exactly that
|
|
150
|
+
* reason: `env` is designed to be legitimately absent.
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* const t = lunoraTest(schema, { env: { STRIPE_KEY: "sk_test_…" } });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
env?: Record<string, unknown>;
|
|
144
157
|
/**
|
|
145
158
|
* Injectable `fetch` implementation for action contexts. When provided,
|
|
146
159
|
* `ctx.fetch` in every `action` (and `withIdentity` views) resolves to this
|
|
@@ -171,6 +184,16 @@ interface LunoraTestOptions {
|
|
|
171
184
|
* ```
|
|
172
185
|
*/
|
|
173
186
|
functions?: FunctionRegistry;
|
|
187
|
+
/**
|
|
188
|
+
* Fixed value for `ctx.now` (epoch ms) in every context. Production captures
|
|
189
|
+
* `Date.now()` once per execution; in tests a fixed `now` makes time-dependent
|
|
190
|
+
* handlers deterministic. Defaults to the wall clock at harness creation.
|
|
191
|
+
* @example
|
|
192
|
+
* ```ts
|
|
193
|
+
* const t = lunoraTest(schema, { now: 1_700_000_000_000 });
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
now?: number;
|
|
174
197
|
}
|
|
175
198
|
/**
|
|
176
199
|
* The in-memory test harness returned by {@link lunoraTest}. Mirrors the first
|
|
@@ -253,6 +276,8 @@ interface TestHarness {
|
|
|
253
276
|
*
|
|
254
277
|
* **v1 surfaces now supported:**
|
|
255
278
|
*
|
|
279
|
+
* - `ctx.env` (all contexts): inject the validated env via `options.env`; unset it
|
|
280
|
+
* stays `undefined`, matching the optional `ctx.env?` field.
|
|
256
281
|
* - `ctx.fetch` (actions): inject a custom `fetch` via `options.fetch`.
|
|
257
282
|
* - `ctx.scheduler` (mutations + actions): fully functional fake with virtual clock;
|
|
258
283
|
* control via `harness.scheduler.advance(ms)` / `runPending()` / `list()`.
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { lunoraTest } from './packem_shared/lunoraTest-
|
|
1
|
+
export { lunoraTest } from './packem_shared/lunoraTest-C0DZz2W9.mjs';
|
|
2
2
|
export { extractLink, listCapturedMail, waitForMail } from '@lunora/mail/testing';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { runShardMigrations, createShardCtxDb } from '@lunora/do';
|
|
2
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
3
|
import { DatabaseSync } from 'node:sqlite';
|
|
3
4
|
|
|
4
|
-
const createFakeScheduler = (getDispatch, getMutationContext, getFunctionRegistry) => {
|
|
5
|
-
let nowMs =
|
|
5
|
+
const createFakeScheduler = (getDispatch, getMutationContext, getFunctionRegistry, now) => {
|
|
6
|
+
let nowMs = now;
|
|
6
7
|
let nextId = 1;
|
|
7
8
|
const pending = /* @__PURE__ */ new Map();
|
|
8
9
|
const recordedFailures = [];
|
|
@@ -57,7 +58,12 @@ const createFakeScheduler = (getDispatch, getMutationContext, getFunctionRegistr
|
|
|
57
58
|
const executeDue = async (cutoff) => {
|
|
58
59
|
const due = [...pending.values()].filter((j) => j.scheduledFor <= cutoff).toSorted((a, b) => a.scheduledFor - b.scheduledFor);
|
|
59
60
|
const failed = [];
|
|
61
|
+
let executed = 0;
|
|
60
62
|
for (const job of due) {
|
|
63
|
+
if (!pending.has(job.id)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
executed += 1;
|
|
61
67
|
try {
|
|
62
68
|
await dispatchJob(job);
|
|
63
69
|
} catch (error) {
|
|
@@ -66,7 +72,7 @@ const createFakeScheduler = (getDispatch, getMutationContext, getFunctionRegistr
|
|
|
66
72
|
recordedFailures.push(failure);
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
|
-
return { executed
|
|
75
|
+
return { executed, failed };
|
|
70
76
|
};
|
|
71
77
|
const runSweep = async (cutoff, options) => {
|
|
72
78
|
const { executed, failed } = await executeDue(cutoff);
|
|
@@ -100,7 +106,7 @@ const createSqlExec = () => {
|
|
|
100
106
|
return {
|
|
101
107
|
one() {
|
|
102
108
|
if (rows.length !== 1) {
|
|
103
|
-
throw new
|
|
109
|
+
throw new LunoraError("INTERNAL", `expected exactly one row, received ${String(rows.length)}`);
|
|
104
110
|
}
|
|
105
111
|
const [only] = rows;
|
|
106
112
|
return only;
|
|
@@ -138,7 +144,7 @@ const registeredFunctionKind = (value) => {
|
|
|
138
144
|
};
|
|
139
145
|
const registeredFunctionVisibility = (value) => typeof value === "object" && value !== null && value.visibility === "internal" ? "internal" : "public";
|
|
140
146
|
const unavailable = (surface) => {
|
|
141
|
-
throw new
|
|
147
|
+
throw new LunoraError("INTERNAL", `ctx.${surface} is not available in the in-memory @lunora/testing harness (v1)`);
|
|
142
148
|
};
|
|
143
149
|
const stubProxy = (surface) => /* @__PURE__ */ new Proxy((..._args) => unavailable(surface), {
|
|
144
150
|
apply: () => unavailable(surface),
|
|
@@ -154,8 +160,9 @@ const noopLog = {
|
|
|
154
160
|
const buildSubscribe = (runRegistered, queryContext, mutationListeners) => {
|
|
155
161
|
const factory = (referenceOrInline, args) => {
|
|
156
162
|
let done = false;
|
|
157
|
-
|
|
163
|
+
const pendingWaiters = [];
|
|
158
164
|
let pendingResult;
|
|
165
|
+
let pendingError;
|
|
159
166
|
let latestSeq = 0;
|
|
160
167
|
let appliedSeq = 0;
|
|
161
168
|
const runQuery = () => {
|
|
@@ -170,25 +177,46 @@ const buildSubscribe = (runRegistered, queryContext, mutationListeners) => {
|
|
|
170
177
|
}
|
|
171
178
|
appliedSeq = seq;
|
|
172
179
|
const iterResult = { done: false, value };
|
|
173
|
-
if (
|
|
180
|
+
if (pendingWaiters.length === 0) {
|
|
174
181
|
pendingResult = iterResult;
|
|
182
|
+
pendingError = void 0;
|
|
175
183
|
} else {
|
|
176
|
-
const resolve = pendingResolve;
|
|
177
|
-
pendingResolve = void 0;
|
|
178
184
|
pendingResult = void 0;
|
|
179
|
-
|
|
185
|
+
pendingError = void 0;
|
|
186
|
+
for (const waiter of pendingWaiters.splice(0)) {
|
|
187
|
+
waiter.resolve(iterResult);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const emitError = (seq, error) => {
|
|
192
|
+
if (seq < appliedSeq) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
appliedSeq = seq;
|
|
196
|
+
if (pendingWaiters.length === 0) {
|
|
197
|
+
pendingError = { error };
|
|
198
|
+
pendingResult = void 0;
|
|
199
|
+
} else {
|
|
200
|
+
pendingResult = void 0;
|
|
201
|
+
pendingError = void 0;
|
|
202
|
+
for (const waiter of pendingWaiters.splice(0)) {
|
|
203
|
+
waiter.reject(error);
|
|
204
|
+
}
|
|
180
205
|
}
|
|
181
206
|
};
|
|
182
207
|
const emitAt = (seq) => (value) => {
|
|
183
208
|
emit(seq, value);
|
|
184
209
|
};
|
|
210
|
+
const emitErrorAt = (seq) => (error) => {
|
|
211
|
+
emitError(seq, error);
|
|
212
|
+
};
|
|
185
213
|
const listener = () => {
|
|
186
214
|
if (done) {
|
|
187
215
|
return;
|
|
188
216
|
}
|
|
189
217
|
latestSeq += 1;
|
|
190
218
|
const seq = latestSeq;
|
|
191
|
-
runQuery().then(emitAt(seq)).catch(()
|
|
219
|
+
runQuery().then(emitAt(seq)).catch(emitErrorAt(seq));
|
|
192
220
|
};
|
|
193
221
|
mutationListeners.add(listener);
|
|
194
222
|
const iterator = {
|
|
@@ -199,17 +227,29 @@ const buildSubscribe = (runRegistered, queryContext, mutationListeners) => {
|
|
|
199
227
|
if (done) {
|
|
200
228
|
return Promise.resolve({ done: true, value: void 0 });
|
|
201
229
|
}
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
230
|
+
if (appliedSeq === latestSeq) {
|
|
231
|
+
if (pendingError !== void 0) {
|
|
232
|
+
const { error } = pendingError;
|
|
233
|
+
pendingError = void 0;
|
|
234
|
+
return Promise.reject(error);
|
|
235
|
+
}
|
|
236
|
+
if (pendingResult !== void 0) {
|
|
237
|
+
const result = pendingResult;
|
|
238
|
+
pendingResult = void 0;
|
|
239
|
+
return Promise.resolve(result);
|
|
240
|
+
}
|
|
206
241
|
}
|
|
207
242
|
if (appliedSeq < latestSeq) {
|
|
208
|
-
return new Promise((resolve) => {
|
|
209
|
-
|
|
243
|
+
return new Promise((resolve, reject) => {
|
|
244
|
+
pendingWaiters.push({ reject, resolve });
|
|
210
245
|
});
|
|
211
246
|
}
|
|
212
247
|
return runQuery().then((value) => {
|
|
248
|
+
if (pendingError !== void 0) {
|
|
249
|
+
const { error } = pendingError;
|
|
250
|
+
pendingError = void 0;
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
213
253
|
if (pendingResult !== void 0) {
|
|
214
254
|
const result = pendingResult;
|
|
215
255
|
pendingResult = void 0;
|
|
@@ -221,15 +261,13 @@ const buildSubscribe = (runRegistered, queryContext, mutationListeners) => {
|
|
|
221
261
|
return: () => {
|
|
222
262
|
done = true;
|
|
223
263
|
mutationListeners.delete(listener);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
pendingResolve = void 0;
|
|
227
|
-
resolve({ done: true, value: void 0 });
|
|
264
|
+
for (const waiter of pendingWaiters.splice(0)) {
|
|
265
|
+
waiter.resolve({ done: true, value: void 0 });
|
|
228
266
|
}
|
|
229
267
|
return Promise.resolve({ done: true, value: void 0 });
|
|
230
268
|
}
|
|
231
269
|
};
|
|
232
|
-
runQuery().then(emitAt(0)).catch((
|
|
270
|
+
runQuery().then(emitAt(0)).catch(emitErrorAt(0));
|
|
233
271
|
return iterator;
|
|
234
272
|
};
|
|
235
273
|
return factory;
|
|
@@ -243,26 +281,28 @@ const lunoraTest = (schema, options) => {
|
|
|
243
281
|
const runner = sql.exec;
|
|
244
282
|
runner.call(sql, statement);
|
|
245
283
|
};
|
|
246
|
-
let
|
|
247
|
-
const runInMutationTransaction =
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
transactionDepth = 1;
|
|
252
|
-
execStatement("BEGIN");
|
|
253
|
-
try {
|
|
254
|
-
const result = await function_();
|
|
255
|
-
execStatement("COMMIT");
|
|
256
|
-
return result;
|
|
257
|
-
} catch (error) {
|
|
284
|
+
let mutationQueue = Promise.resolve();
|
|
285
|
+
const runInMutationTransaction = (function_) => {
|
|
286
|
+
const runTransaction = async () => {
|
|
287
|
+
execStatement("BEGIN");
|
|
258
288
|
try {
|
|
259
|
-
|
|
260
|
-
|
|
289
|
+
const result2 = await function_();
|
|
290
|
+
execStatement("COMMIT");
|
|
291
|
+
return result2;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
try {
|
|
294
|
+
execStatement("ROLLBACK");
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
throw error;
|
|
261
298
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
299
|
+
};
|
|
300
|
+
const result = mutationQueue.then(runTransaction);
|
|
301
|
+
mutationQueue = result.then(
|
|
302
|
+
() => void 0,
|
|
303
|
+
() => void 0
|
|
304
|
+
);
|
|
305
|
+
return result;
|
|
266
306
|
};
|
|
267
307
|
let closed = false;
|
|
268
308
|
const closeDatabase = () => {
|
|
@@ -283,20 +323,28 @@ const lunoraTest = (schema, options) => {
|
|
|
283
323
|
};
|
|
284
324
|
let scheduledDispatchRef;
|
|
285
325
|
let mutationContextRef;
|
|
326
|
+
const harnessNow = options?.now ?? Date.now();
|
|
286
327
|
const { controls: schedulerControls, scheduler: fakeScheduler } = createFakeScheduler(
|
|
287
328
|
() => {
|
|
288
329
|
if (scheduledDispatchRef === void 0) {
|
|
289
|
-
throw new
|
|
330
|
+
throw new LunoraError(
|
|
331
|
+
"INTERNAL",
|
|
332
|
+
"[fake-scheduler] dispatch not yet available — scheduler.advance called before harness construction completed"
|
|
333
|
+
);
|
|
290
334
|
}
|
|
291
335
|
return scheduledDispatchRef;
|
|
292
336
|
},
|
|
293
337
|
() => {
|
|
294
338
|
if (mutationContextRef === void 0) {
|
|
295
|
-
throw new
|
|
339
|
+
throw new LunoraError(
|
|
340
|
+
"INTERNAL",
|
|
341
|
+
"[fake-scheduler] mutationContext not yet available — scheduler.advance called before harness construction completed"
|
|
342
|
+
);
|
|
296
343
|
}
|
|
297
344
|
return mutationContextRef;
|
|
298
345
|
},
|
|
299
|
-
() => functionRegistryMap
|
|
346
|
+
() => functionRegistryMap,
|
|
347
|
+
harnessNow
|
|
300
348
|
);
|
|
301
349
|
const makeHarness = (identity) => {
|
|
302
350
|
const auth = {
|
|
@@ -308,21 +356,27 @@ const lunoraTest = (schema, options) => {
|
|
|
308
356
|
const queryContext = {
|
|
309
357
|
auth,
|
|
310
358
|
db: database,
|
|
359
|
+
env: options?.env,
|
|
311
360
|
log: noopLog,
|
|
361
|
+
now: harnessNow,
|
|
312
362
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- lazy closure: `runInternal` is invoked only when a handler calls ctx.runQuery, after construction completes
|
|
313
363
|
runQuery: (reference, args) => runInternal("query", reference, queryContext, args),
|
|
364
|
+
secrets: stubProxy("secrets"),
|
|
314
365
|
storage: stubProxy("storage"),
|
|
315
366
|
vectors: stubProxy("vectors")
|
|
316
367
|
};
|
|
317
368
|
const mutationContext = {
|
|
318
369
|
auth,
|
|
319
370
|
db: database,
|
|
371
|
+
env: options?.env,
|
|
320
372
|
log: noopLog,
|
|
373
|
+
now: harnessNow,
|
|
321
374
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- lazy closure: `runInternal` is invoked only when a handler calls ctx.runMutation, after construction completes
|
|
322
375
|
runMutation: (reference, args) => runInternal("mutation", reference, mutationContext, args),
|
|
323
376
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- lazy closure: `runInternal` is invoked only when a handler calls ctx.runQuery, after construction completes
|
|
324
377
|
runQuery: (reference, args) => runInternal("query", reference, queryContext, args),
|
|
325
378
|
scheduler: fakeScheduler,
|
|
379
|
+
secrets: stubProxy("secrets"),
|
|
326
380
|
storage: stubProxy("storage"),
|
|
327
381
|
vectors: stubProxy("vectors"),
|
|
328
382
|
workflows: stubProxy("workflows")
|
|
@@ -331,9 +385,11 @@ const lunoraTest = (schema, options) => {
|
|
|
331
385
|
const actionContext = {
|
|
332
386
|
auth,
|
|
333
387
|
db: database,
|
|
388
|
+
env: options?.env,
|
|
334
389
|
// Use the injected fetch when provided; fall back to the v1 stub otherwise.
|
|
335
390
|
fetch: options?.fetch ?? stubProxy("fetch"),
|
|
336
391
|
log: noopLog,
|
|
392
|
+
now: harnessNow,
|
|
337
393
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- lazy closure: `runInternal` is invoked only when a handler calls ctx.runAction, after construction completes
|
|
338
394
|
runAction: (reference, args) => runInternal("action", reference, actionContext, args),
|
|
339
395
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- lazy closure: `runInternal` is invoked only when a handler calls ctx.runMutation, after construction completes
|
|
@@ -341,6 +397,7 @@ const lunoraTest = (schema, options) => {
|
|
|
341
397
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- lazy closure: `runInternal` is invoked only when a handler calls ctx.runQuery, after construction completes
|
|
342
398
|
runQuery: (reference, args) => runInternal("query", reference, queryContext, args),
|
|
343
399
|
scheduler: fakeScheduler,
|
|
400
|
+
secrets: stubProxy("secrets"),
|
|
344
401
|
storage: stubProxy("storage"),
|
|
345
402
|
vectors: stubProxy("vectors"),
|
|
346
403
|
workflows: stubProxy("workflows")
|
|
@@ -348,11 +405,12 @@ const lunoraTest = (schema, options) => {
|
|
|
348
405
|
const runRegistered = (expected, reference, context, args, allowInternal) => {
|
|
349
406
|
const kind = registeredFunctionKind(reference);
|
|
350
407
|
if (kind !== expected) {
|
|
351
|
-
throw new
|
|
408
|
+
throw new LunoraError("INTERNAL", `expected a registered ${expected}, received a ${kind ?? "non-function"} reference`);
|
|
352
409
|
}
|
|
353
410
|
if (!allowInternal && registeredFunctionVisibility(reference) === "internal") {
|
|
354
|
-
throw new
|
|
355
|
-
|
|
411
|
+
throw new LunoraError(
|
|
412
|
+
"INTERNAL",
|
|
413
|
+
`This ${expected} is an internal function — it is unreachable from the external RPC boundary in production. Call it through ctx.run${expected.charAt(0).toUpperCase()}${expected.slice(1)} from another function instead.`
|
|
356
414
|
);
|
|
357
415
|
}
|
|
358
416
|
return Promise.resolve(reference.handler(context, args ?? {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/testing",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.40",
|
|
4
4
|
"description": "Testing toolkit for Lunora: an in-memory harness for queries, mutations, and actions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"directory": "packages/testing"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
-
"dist",
|
|
28
|
+
"./dist",
|
|
29
29
|
"README.md",
|
|
30
30
|
"LICENSE.md",
|
|
31
31
|
"__assets__"
|
|
@@ -46,9 +46,10 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lunora/do": "1.0.0-alpha.
|
|
50
|
-
"@lunora/
|
|
51
|
-
"@lunora/
|
|
49
|
+
"@lunora/do": "1.0.0-alpha.28",
|
|
50
|
+
"@lunora/errors": "1.0.0-alpha.4",
|
|
51
|
+
"@lunora/mail": "1.0.0-alpha.14",
|
|
52
|
+
"@lunora/server": "1.0.0-alpha.23"
|
|
52
53
|
},
|
|
53
54
|
"engines": {
|
|
54
55
|
"node": "^22.15.0 || >=24.11.0"
|