@rotorsoft/act-tck 1.39.2 → 1.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/stability-tck.d.ts +11 -6
- package/dist/@types/stability-tck.d.ts.map +1 -1
- package/dist/@types/store-tck.d.ts.map +1 -1
- package/dist/index.cjs +187 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +188 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -364,8 +364,9 @@ async function load_surface(entry2) {
|
|
|
364
364
|
const content = await fs.readFile(abs, "utf8");
|
|
365
365
|
visited.set(abs, content);
|
|
366
366
|
const re_export = /^\s*export\b[^"';]*\bfrom\s+["'](\.[^"']+)["']/gm;
|
|
367
|
+
const ext = abs.endsWith(".d.ts") ? ".d.ts" : ".ts";
|
|
367
368
|
for (const m of content.matchAll(re_export)) {
|
|
368
|
-
const rel = m[1].replace(/\.js$/,
|
|
369
|
+
const rel = m[1].replace(/\.js$/, ext);
|
|
369
370
|
const target = path.resolve(path.dirname(abs), rel);
|
|
370
371
|
await walk(target);
|
|
371
372
|
}
|
|
@@ -1098,6 +1099,7 @@ import {
|
|
|
1098
1099
|
ConcurrencyError,
|
|
1099
1100
|
InMemoryCache,
|
|
1100
1101
|
SNAP_EVENT as SNAP_EVENT2,
|
|
1102
|
+
sensitive,
|
|
1101
1103
|
sleep,
|
|
1102
1104
|
state,
|
|
1103
1105
|
TOMBSTONE_EVENT,
|
|
@@ -4545,6 +4547,191 @@ var runStoreTck = (options) => {
|
|
|
4545
4547
|
expect8(positions).toEqual([s]);
|
|
4546
4548
|
});
|
|
4547
4549
|
});
|
|
4550
|
+
describe8("schema-driven dates on read", () => {
|
|
4551
|
+
const actor2 = { id: "reader", name: "Reader" };
|
|
4552
|
+
const reader = (shape) => {
|
|
4553
|
+
const Happened = z2.object(shape);
|
|
4554
|
+
return state({ Reader: z2.object({ n: z2.number() }) }).init(() => ({ n: 0 })).emits({ Happened }).patch({ Happened: (_, s) => ({ n: s.n + 1 }) }).on({ Do: Happened }).emit((p) => ["Happened", p]).build();
|
|
4555
|
+
};
|
|
4556
|
+
const over = async (S) => {
|
|
4557
|
+
const fresh = await options.factory();
|
|
4558
|
+
await fresh.seed();
|
|
4559
|
+
const cache = new InMemoryCache();
|
|
4560
|
+
const app = act().withState(S).build({ scoped: { store: fresh, cache } });
|
|
4561
|
+
const stream = `dt-${uid()}`;
|
|
4562
|
+
const read = async () => (await app.query_array({ stream, stream_exact: true }))[0].data;
|
|
4563
|
+
const done = async () => {
|
|
4564
|
+
await app.shutdown();
|
|
4565
|
+
await cache.dispose();
|
|
4566
|
+
await fresh.dispose();
|
|
4567
|
+
};
|
|
4568
|
+
return { app, fresh, cache, stream, read, done };
|
|
4569
|
+
};
|
|
4570
|
+
const raw = { correlation: "c", causation: {} };
|
|
4571
|
+
it7("types z.date() and leaves an ISO-shaped z.string() alone, warm and cold", async () => {
|
|
4572
|
+
const at = /* @__PURE__ */ new Date("2026-01-01T00:00:00.000Z");
|
|
4573
|
+
const label = "2026-06-06T00:00:00.000Z";
|
|
4574
|
+
const S = reader({ at: z2.date(), label: z2.string() });
|
|
4575
|
+
const t = await over(S);
|
|
4576
|
+
try {
|
|
4577
|
+
await t.app.do("Do", { stream: t.stream, actor: actor2 }, { at, label });
|
|
4578
|
+
const warm = await t.read();
|
|
4579
|
+
await t.cache.clear();
|
|
4580
|
+
const cold = await t.read();
|
|
4581
|
+
for (const d of [warm, cold]) {
|
|
4582
|
+
expect8(d.at).toBeInstanceOf(Date);
|
|
4583
|
+
expect8(d.at.getTime()).toBe(at.getTime());
|
|
4584
|
+
expect8(d.label).toBe(label);
|
|
4585
|
+
}
|
|
4586
|
+
} finally {
|
|
4587
|
+
await t.done();
|
|
4588
|
+
}
|
|
4589
|
+
});
|
|
4590
|
+
it7("revives a date through every wrapper the rebuild knows", async () => {
|
|
4591
|
+
const S = reader({
|
|
4592
|
+
a: z2.date(),
|
|
4593
|
+
b: z2.date().default(/* @__PURE__ */ new Date(0)),
|
|
4594
|
+
t: z2.tuple([z2.date(), z2.string()]),
|
|
4595
|
+
td: z2.tuple([z2.string(), z2.string()]),
|
|
4596
|
+
r: z2.date().readonly(),
|
|
4597
|
+
c: z2.date().catch(/* @__PURE__ */ new Date(0)),
|
|
4598
|
+
n: z2.date().optional().nonoptional(),
|
|
4599
|
+
p: z2.date().prefault(/* @__PURE__ */ new Date(0)),
|
|
4600
|
+
u: z2.union([z2.date(), z2.string()]),
|
|
4601
|
+
l: z2.array(z2.date()),
|
|
4602
|
+
m: z2.record(z2.string(), z2.date()),
|
|
4603
|
+
o: z2.object({ deep: z2.date() }),
|
|
4604
|
+
x: z2.date().nullable()
|
|
4605
|
+
});
|
|
4606
|
+
const t = await over(S);
|
|
4607
|
+
try {
|
|
4608
|
+
const when = /* @__PURE__ */ new Date("2022-01-01");
|
|
4609
|
+
await t.app.do(
|
|
4610
|
+
"Do",
|
|
4611
|
+
{ stream: t.stream, actor: actor2 },
|
|
4612
|
+
{
|
|
4613
|
+
a: /* @__PURE__ */ new Date("2020-01-01"),
|
|
4614
|
+
b: /* @__PURE__ */ new Date("2021-01-01"),
|
|
4615
|
+
t: [when, "x"],
|
|
4616
|
+
td: ["no", "dates"],
|
|
4617
|
+
r: when,
|
|
4618
|
+
c: when,
|
|
4619
|
+
n: when,
|
|
4620
|
+
p: when,
|
|
4621
|
+
u: when,
|
|
4622
|
+
l: [when],
|
|
4623
|
+
m: { k: when },
|
|
4624
|
+
o: { deep: when },
|
|
4625
|
+
x: null
|
|
4626
|
+
}
|
|
4627
|
+
);
|
|
4628
|
+
const d = await t.read();
|
|
4629
|
+
for (const key of ["a", "b", "r", "c", "n", "p", "u"])
|
|
4630
|
+
expect8(d[key], key).toBeInstanceOf(Date);
|
|
4631
|
+
expect8(d.t[0]).toBeInstanceOf(Date);
|
|
4632
|
+
expect8(d.t[1]).toBe("x");
|
|
4633
|
+
expect8(d.td).toEqual(["no", "dates"]);
|
|
4634
|
+
expect8(d.l[0]).toBeInstanceOf(Date);
|
|
4635
|
+
expect8(d.m.k).toBeInstanceOf(Date);
|
|
4636
|
+
expect8(d.o.deep).toBeInstanceOf(Date);
|
|
4637
|
+
expect8(d.x).toBeNull();
|
|
4638
|
+
} finally {
|
|
4639
|
+
await t.done();
|
|
4640
|
+
}
|
|
4641
|
+
});
|
|
4642
|
+
it7("revives dates in a payload that predates the declaration", async () => {
|
|
4643
|
+
const S = reader({ at: z2.date(), label: z2.string() });
|
|
4644
|
+
const t = await over(S);
|
|
4645
|
+
try {
|
|
4646
|
+
await t.fresh.commit(
|
|
4647
|
+
t.stream,
|
|
4648
|
+
[{ name: "Happened", data: { at: /* @__PURE__ */ new Date("2020-01-01") } }],
|
|
4649
|
+
raw,
|
|
4650
|
+
-1
|
|
4651
|
+
);
|
|
4652
|
+
const d = await t.read();
|
|
4653
|
+
expect8(d.label).toBeUndefined();
|
|
4654
|
+
expect8(d.at).toBeInstanceOf(Date);
|
|
4655
|
+
} finally {
|
|
4656
|
+
await t.done();
|
|
4657
|
+
}
|
|
4658
|
+
});
|
|
4659
|
+
it7("hands back what is stored when a date field holds something else", async () => {
|
|
4660
|
+
const S = reader({ at: z2.date() });
|
|
4661
|
+
const t = await over(S);
|
|
4662
|
+
try {
|
|
4663
|
+
await t.fresh.commit(
|
|
4664
|
+
t.stream,
|
|
4665
|
+
[{ name: "Happened", data: { at: "not a date" } }],
|
|
4666
|
+
raw,
|
|
4667
|
+
-1
|
|
4668
|
+
);
|
|
4669
|
+
expect8((await t.read()).at).toBe("not a date");
|
|
4670
|
+
} finally {
|
|
4671
|
+
await t.done();
|
|
4672
|
+
}
|
|
4673
|
+
});
|
|
4674
|
+
describe8.skipIf(!caps.pii_isolation)("with sensitive fields", () => {
|
|
4675
|
+
it7("reads a redacted payload whose required sensitive field lives in pii", async () => {
|
|
4676
|
+
const S = reader({ at: z2.date(), email: sensitive(z2.string()) });
|
|
4677
|
+
const t = await over(S);
|
|
4678
|
+
try {
|
|
4679
|
+
const target = { stream: t.stream, actor: actor2 };
|
|
4680
|
+
await t.app.do("Do", target, { at: /* @__PURE__ */ new Date(0), email: "u@x.io" });
|
|
4681
|
+
const d = await t.read();
|
|
4682
|
+
expect8(d.at).toBeInstanceOf(Date);
|
|
4683
|
+
expect8(d.email).toBe("[REDACTED]");
|
|
4684
|
+
expect8((await t.app.load(S, t.stream)).state.n).toBe(1);
|
|
4685
|
+
await t.app.do("Do", target, { at: /* @__PURE__ */ new Date(1), email: "v@x.io" });
|
|
4686
|
+
} finally {
|
|
4687
|
+
await t.done();
|
|
4688
|
+
}
|
|
4689
|
+
});
|
|
4690
|
+
it7("keeps a redacted sensitive date's sentinel", async () => {
|
|
4691
|
+
const S = reader({ at: z2.date(), born: sensitive(z2.date()) });
|
|
4692
|
+
const t = await over(S);
|
|
4693
|
+
try {
|
|
4694
|
+
await t.app.do(
|
|
4695
|
+
"Do",
|
|
4696
|
+
{ stream: t.stream, actor: actor2 },
|
|
4697
|
+
{ at: /* @__PURE__ */ new Date(0), born: /* @__PURE__ */ new Date(1) }
|
|
4698
|
+
);
|
|
4699
|
+
const d = await t.read();
|
|
4700
|
+
expect8(d.at).toBeInstanceOf(Date);
|
|
4701
|
+
expect8(d.born).toBe("[REDACTED]");
|
|
4702
|
+
} finally {
|
|
4703
|
+
await t.done();
|
|
4704
|
+
}
|
|
4705
|
+
});
|
|
4706
|
+
it7("hands a disclosed sensitive date to the reducer as a Date", async () => {
|
|
4707
|
+
const seen = {};
|
|
4708
|
+
const Happened = z2.object({
|
|
4709
|
+
at: z2.date(),
|
|
4710
|
+
born: sensitive(z2.date())
|
|
4711
|
+
});
|
|
4712
|
+
const S = state({ Disclosed: z2.object({ n: z2.number() }) }).init(() => ({ n: 0 })).emits({ Happened }).patch({
|
|
4713
|
+
Happened: ({ data }, s) => {
|
|
4714
|
+
seen.at = data.at instanceof Date;
|
|
4715
|
+
seen.born = data.born instanceof Date;
|
|
4716
|
+
return { n: s.n + 1 };
|
|
4717
|
+
}
|
|
4718
|
+
}).on({ Do: Happened }).emit((p) => ["Happened", p]).discloses(() => true).build();
|
|
4719
|
+
const t = await over(S);
|
|
4720
|
+
try {
|
|
4721
|
+
await t.app.do(
|
|
4722
|
+
"Do",
|
|
4723
|
+
{ stream: t.stream, actor: actor2 },
|
|
4724
|
+
{ at: /* @__PURE__ */ new Date(0), born: /* @__PURE__ */ new Date(1) }
|
|
4725
|
+
);
|
|
4726
|
+
await t.cache.clear();
|
|
4727
|
+
await t.app.load(S, { stream: t.stream, actor: actor2 });
|
|
4728
|
+
expect8(seen).toEqual({ at: true, born: true });
|
|
4729
|
+
} finally {
|
|
4730
|
+
await t.done();
|
|
4731
|
+
}
|
|
4732
|
+
});
|
|
4733
|
+
});
|
|
4734
|
+
});
|
|
4548
4735
|
describe8("autoclose end-to-end", () => {
|
|
4549
4736
|
const Ticket = state({ TckTicket: z2.object({ open: z2.boolean() }) }).init(() => ({ open: false })).emits({
|
|
4550
4737
|
TckTicketOpened: z2.object({ title: z2.string() }),
|