@pg-prime/events 0.0.0 → 0.2.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/LICENSE +373 -0
- package/README.md +42 -1
- package/dist/channel.d.ts +19 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +51 -0
- package/dist/channel.js.map +1 -0
- package/dist/define.d.ts +49 -0
- package/dist/define.d.ts.map +1 -0
- package/dist/define.js +39 -0
- package/dist/define.js.map +1 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +30 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +57 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +99 -0
- package/dist/events.js.map +1 -0
- package/dist/hooks.d.ts +40 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +27 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/log/cursor.d.ts +37 -0
- package/dist/log/cursor.d.ts.map +1 -0
- package/dist/log/cursor.js +58 -0
- package/dist/log/cursor.js.map +1 -0
- package/dist/log/migrations.d.ts +3 -0
- package/dist/log/migrations.d.ts.map +1 -0
- package/dist/log/migrations.js +55 -0
- package/dist/log/migrations.js.map +1 -0
- package/dist/log/sql.d.ts +68 -0
- package/dist/log/sql.d.ts.map +1 -0
- package/dist/log/sql.js +251 -0
- package/dist/log/sql.js.map +1 -0
- package/dist/log/subscription.d.ts +53 -0
- package/dist/log/subscription.d.ts.map +1 -0
- package/dist/log/subscription.js +272 -0
- package/dist/log/subscription.js.map +1 -0
- package/dist/log/topic.d.ts +48 -0
- package/dist/log/topic.d.ts.map +1 -0
- package/dist/log/topic.js +111 -0
- package/dist/log/topic.js.map +1 -0
- package/dist/payload.d.ts +40 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +53 -0
- package/dist/payload.js.map +1 -0
- package/dist/unsupported-typescript.d.ts +70 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +31 -0
- package/dist/util.js.map +1 -0
- package/dist/waker.d.ts +22 -0
- package/dist/waker.d.ts.map +1 -0
- package/dist/waker.js +86 -0
- package/dist/waker.js.map +1 -0
- package/package.json +49 -3
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Queryable, Subscription } from 'pg-prime';
|
|
2
|
+
import type { TopicDef } from '../define.js';
|
|
3
|
+
import type { Core } from '../events.js';
|
|
4
|
+
import type { EntryRow } from './sql.js';
|
|
5
|
+
export interface Entry<T> {
|
|
6
|
+
readonly id: bigint;
|
|
7
|
+
readonly topic: string;
|
|
8
|
+
readonly type: string | null;
|
|
9
|
+
readonly key: string | null;
|
|
10
|
+
readonly payload: T;
|
|
11
|
+
readonly headers: Record<string, string> | null;
|
|
12
|
+
readonly at: Date;
|
|
13
|
+
/** The opaque cursor state right after this entry: the only valid resume handle. */
|
|
14
|
+
readonly cursor: string;
|
|
15
|
+
}
|
|
16
|
+
export interface PublishOptions {
|
|
17
|
+
tx?: Queryable<any>;
|
|
18
|
+
type?: string;
|
|
19
|
+
key?: string;
|
|
20
|
+
headers?: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
export interface TopicHandle<T> {
|
|
23
|
+
publish(payload: T, opts?: PublishOptions): Promise<{
|
|
24
|
+
id: bigint;
|
|
25
|
+
}>;
|
|
26
|
+
publishMany(items: readonly ({
|
|
27
|
+
payload: T;
|
|
28
|
+
} & Omit<PublishOptions, 'tx'>)[], opts?: {
|
|
29
|
+
tx?: Queryable<any>;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
id: bigint;
|
|
32
|
+
}[]>;
|
|
33
|
+
tail(handler: (e: Entry<T>) => void, opts?: {
|
|
34
|
+
from?: 'now' | {
|
|
35
|
+
after: string;
|
|
36
|
+
};
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
pollMs?: number;
|
|
39
|
+
}): Promise<Subscription>;
|
|
40
|
+
/** Browse by id for admin tools; not a resume handle. */
|
|
41
|
+
read(opts?: {
|
|
42
|
+
afterId?: bigint;
|
|
43
|
+
limit?: number;
|
|
44
|
+
}): Promise<Omit<Entry<T>, 'cursor'>[]>;
|
|
45
|
+
}
|
|
46
|
+
export declare function entryOf<T>(def: TopicDef<T>, r: EntryRow): Promise<Omit<Entry<T>, 'cursor'>>;
|
|
47
|
+
export declare function topicHandle<T>(core: Core, def: TopicDef<T>): TopicHandle<T>;
|
|
48
|
+
//# sourceMappingURL=topic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topic.d.ts","sourceRoot":"","sources":["../../src/log/topic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAKxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,WAAW,KAAK,CAAC,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IAC/C,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;IACjB,oFAAoF;IACpF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACnE,WAAW,CACT,KAAK,EAAE,SAAS,CAAC;QAAE,OAAO,EAAE,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,EAAE,EAC/D,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,SAAS,CAAC,GAAG,CAAC,CAAA;KAAE,GAC7B,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAA;IAC5B,IAAI,CACF,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,EAC9B,IAAI,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,KAAK,GAAG;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACjF,OAAO,CAAC,YAAY,CAAC,CAAA;IACxB,yDAAyD;IACzD,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAA;CACvF;AAED,wBAAsB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAUjG;AAID,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAiG3E"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { decodePayload, encodePayload } from '../payload.js';
|
|
2
|
+
import { ASYNC_DISPOSE, emitAll } from '../util.js';
|
|
3
|
+
import { waker } from '../waker.js';
|
|
4
|
+
import { advance, advanceTo, decodeCursor, encodeCursor, seed } from './cursor.js';
|
|
5
|
+
export async function entryOf(def, r) {
|
|
6
|
+
return {
|
|
7
|
+
id: r.id,
|
|
8
|
+
topic: def.name,
|
|
9
|
+
type: r.type,
|
|
10
|
+
key: r.key,
|
|
11
|
+
payload: await decodePayload(def.payload, r.payload),
|
|
12
|
+
headers: r.headers,
|
|
13
|
+
at: r.at,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const TAIL_BATCH = 500;
|
|
17
|
+
export function topicHandle(core, def) {
|
|
18
|
+
const channel = core.topicChannel(def.name);
|
|
19
|
+
const handle = {
|
|
20
|
+
async publish(payload, opts = {}) {
|
|
21
|
+
const { tx, ...item } = opts;
|
|
22
|
+
const [r] = await handle.publishMany([{ payload, ...item }], tx === undefined ? {} : { tx });
|
|
23
|
+
return r;
|
|
24
|
+
},
|
|
25
|
+
async publishMany(items, opts = {}) {
|
|
26
|
+
if (opts.tx === undefined)
|
|
27
|
+
await core.ready(def);
|
|
28
|
+
else
|
|
29
|
+
core.assertStarted('publish');
|
|
30
|
+
if (items.length === 0)
|
|
31
|
+
return [];
|
|
32
|
+
const rows = await Promise.all(items.map(async (i) => ({
|
|
33
|
+
type: i.type ?? null,
|
|
34
|
+
key: i.key ?? null,
|
|
35
|
+
headers: i.headers ?? null,
|
|
36
|
+
payload: await encodePayload(def.payload, i.payload),
|
|
37
|
+
})));
|
|
38
|
+
const ids = await core.sql.publish(opts.tx ?? core.db, def.name, channel, rows);
|
|
39
|
+
core.hooks.emit('onPublish', {
|
|
40
|
+
topic: def.name,
|
|
41
|
+
count: ids.length,
|
|
42
|
+
inTx: opts.tx !== undefined,
|
|
43
|
+
});
|
|
44
|
+
return ids.map((id) => ({ id }));
|
|
45
|
+
},
|
|
46
|
+
async tail(handler, opts = {}) {
|
|
47
|
+
await core.ready(def);
|
|
48
|
+
const from = opts.from ?? 'now';
|
|
49
|
+
let cursor = from === 'now'
|
|
50
|
+
? await core.sql.seedNow(core.db).then((s) => seed(s.xmax, s.xip))
|
|
51
|
+
: decodeCursor(from.after);
|
|
52
|
+
const errors = new Set();
|
|
53
|
+
const fail = (e) => emitAll(errors, e);
|
|
54
|
+
const stop = new AbortController();
|
|
55
|
+
opts.signal?.addEventListener('abort', () => stop.abort(), { once: true });
|
|
56
|
+
if (opts.signal?.aborted === true)
|
|
57
|
+
stop.abort();
|
|
58
|
+
const wake = waker(core.db, channel, {
|
|
59
|
+
pollMs: opts.pollMs ?? 5000,
|
|
60
|
+
signal: stop.signal,
|
|
61
|
+
onInternal: (e) => core.hooks.emit('onInternal', e),
|
|
62
|
+
});
|
|
63
|
+
const loop = (async () => {
|
|
64
|
+
do {
|
|
65
|
+
try {
|
|
66
|
+
while (!stop.signal.aborted) {
|
|
67
|
+
const { view, rows } = await core.sql.read(core.db, def.name, cursor, TAIL_BATCH, null);
|
|
68
|
+
for (const r of rows) {
|
|
69
|
+
cursor = advance(cursor, view, r);
|
|
70
|
+
try {
|
|
71
|
+
handler({ ...(await entryOf(def, r)), cursor: encodeCursor(cursor) });
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
fail(e);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
cursor = advanceTo(cursor, view);
|
|
78
|
+
if (view.pos === null && view.late === null)
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
fail(e);
|
|
84
|
+
}
|
|
85
|
+
} while ((await wake.wait()) !== undefined);
|
|
86
|
+
})();
|
|
87
|
+
const close = async () => {
|
|
88
|
+
stop.abort();
|
|
89
|
+
await loop;
|
|
90
|
+
core.running.delete(sub);
|
|
91
|
+
};
|
|
92
|
+
// Tails heal gaps from the log, so only 'error' ever fires.
|
|
93
|
+
const on = (event, h) => {
|
|
94
|
+
if (event !== 'error')
|
|
95
|
+
return () => { };
|
|
96
|
+
errors.add(h);
|
|
97
|
+
return () => void errors.delete(h);
|
|
98
|
+
};
|
|
99
|
+
const sub = { channel, close, [ASYNC_DISPOSE]: close, on };
|
|
100
|
+
core.running.add(sub);
|
|
101
|
+
return sub;
|
|
102
|
+
},
|
|
103
|
+
async read(opts = {}) {
|
|
104
|
+
await core.ready(def);
|
|
105
|
+
const rows = await core.sql.browse(core.db, def.name, opts.afterId ?? 0n, opts.limit ?? 100);
|
|
106
|
+
return Promise.all(rows.map((r) => entryOf(def, r)));
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
return handle;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=topic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topic.js","sourceRoot":"","sources":["../../src/log/topic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** The Standard Schema v1 interface (standardschema.dev), copied as a type: no dependency. */
|
|
2
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
3
|
+
readonly '~standard': {
|
|
4
|
+
readonly version: 1;
|
|
5
|
+
readonly vendor: string;
|
|
6
|
+
readonly validate: (value: unknown) => StandardSchemaV1.Result<Output> | Promise<StandardSchemaV1.Result<Output>>;
|
|
7
|
+
readonly types?: {
|
|
8
|
+
readonly input: Input;
|
|
9
|
+
readonly output: Output;
|
|
10
|
+
} | undefined;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare namespace StandardSchemaV1 {
|
|
14
|
+
type Result<Output> = {
|
|
15
|
+
readonly value: Output;
|
|
16
|
+
readonly issues?: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
readonly issues: readonly Issue[];
|
|
19
|
+
};
|
|
20
|
+
interface Issue {
|
|
21
|
+
readonly message: string;
|
|
22
|
+
readonly path?: readonly (PropertyKey | {
|
|
23
|
+
readonly key: PropertyKey;
|
|
24
|
+
})[] | undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export interface PayloadOptions<T> {
|
|
28
|
+
/** Validates on both sides: before encoding and after decoding. */
|
|
29
|
+
schema?: StandardSchemaV1<unknown, T>;
|
|
30
|
+
/** Maps the value to and from its JSON form. */
|
|
31
|
+
codec?: {
|
|
32
|
+
encode(v: T): unknown;
|
|
33
|
+
decode(v: unknown): T;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Validates, encodes and serializes a payload; refuses NUL characters and oversize documents. */
|
|
37
|
+
export declare function encodePayload<T>(opts: PayloadOptions<T>, value: T, maxBytes?: number): Promise<string>;
|
|
38
|
+
/** Decodes a JSON value read back from the database and validates it. */
|
|
39
|
+
export declare function decodePayload<T>(opts: PayloadOptions<T>, wire: unknown): Promise<T>;
|
|
40
|
+
//# sourceMappingURL=payload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../src/payload.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK;IAC/D,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;QACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,QAAQ,EAAE,CACjB,KAAK,EAAE,OAAO,KACX,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;QAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,GAAG,SAAS,CAAA;KAChF,CAAA;CACF;AAED,MAAM,CAAC,OAAO,WAAW,gBAAgB,CAAC;IACxC,KAAK,MAAM,CAAC,MAAM,IACd;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAA;KAAE,GACvD;QAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAA;KAAE,CAAA;IACzC,UAAU,KAAK;QACb,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG;YAAE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;SAAE,CAAC,EAAE,GAAG,SAAS,CAAA;KACrF;CACF;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,mEAAmE;IACnE,MAAM,CAAC,EAAE,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IACrC,gDAAgD;IAChD,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC;QAAC,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAA;KAAE,CAAA;CACzD;AAID,kGAAkG;AAClG,wBAAsB,aAAa,CAAC,CAAC,EACnC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,KAAK,EAAE,CAAC,EACR,QAAQ,GAAE,MAAkB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF"}
|
package/dist/payload.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PayloadError } from './errors.js';
|
|
2
|
+
const MAX_BYTES = 1024 * 1024;
|
|
3
|
+
/** Validates, encodes and serializes a payload; refuses NUL characters and oversize documents. */
|
|
4
|
+
export async function encodePayload(opts, value, maxBytes = MAX_BYTES) {
|
|
5
|
+
const v = await validate(opts, value);
|
|
6
|
+
const wire = opts.codec === undefined ? v : opts.codec.encode(v);
|
|
7
|
+
const json = JSON.stringify(wire);
|
|
8
|
+
if (json === undefined)
|
|
9
|
+
throw new PayloadError('@pg-prime/events: a payload must be JSON.');
|
|
10
|
+
const path = nulPath(wire, []);
|
|
11
|
+
if (path !== undefined) {
|
|
12
|
+
throw new PayloadError(`@pg-prime/events: the payload has a NUL character at ${fmt(path)}; jsonb cannot store it.`, path);
|
|
13
|
+
}
|
|
14
|
+
const bytes = new TextEncoder().encode(json).length;
|
|
15
|
+
if (bytes > maxBytes) {
|
|
16
|
+
throw new PayloadError(`@pg-prime/events: the payload is ${bytes} bytes; the limit is ${maxBytes}. Store the ` +
|
|
17
|
+
`document in a table and publish its id.`);
|
|
18
|
+
}
|
|
19
|
+
return json;
|
|
20
|
+
}
|
|
21
|
+
/** Decodes a JSON value read back from the database and validates it. */
|
|
22
|
+
export function decodePayload(opts, wire) {
|
|
23
|
+
return validate(opts, opts.codec === undefined ? wire : opts.codec.decode(wire));
|
|
24
|
+
}
|
|
25
|
+
async function validate(opts, value) {
|
|
26
|
+
if (opts.schema === undefined)
|
|
27
|
+
return value;
|
|
28
|
+
const r = await opts.schema['~standard'].validate(value);
|
|
29
|
+
if (r.issues === undefined)
|
|
30
|
+
return r.value;
|
|
31
|
+
const issue = r.issues[0];
|
|
32
|
+
const path = (issue?.path ?? []).map((p) => (typeof p === 'object' ? p.key : p));
|
|
33
|
+
throw new PayloadError(`@pg-prime/events: the payload failed its schema at ${fmt(path)}: ${issue?.message ?? 'invalid'}`, path);
|
|
34
|
+
}
|
|
35
|
+
function nulPath(v, path) {
|
|
36
|
+
if (typeof v === 'string')
|
|
37
|
+
return v.includes('\0') ? path : undefined;
|
|
38
|
+
if (typeof v !== 'object' || v === null)
|
|
39
|
+
return undefined;
|
|
40
|
+
for (const [k, x] of Object.entries(v)) {
|
|
41
|
+
const key = Array.isArray(v) ? Number(k) : k;
|
|
42
|
+
if (k.includes('\0'))
|
|
43
|
+
return [...path, key];
|
|
44
|
+
const p = nulPath(x, [...path, key]);
|
|
45
|
+
if (p !== undefined)
|
|
46
|
+
return p;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function fmt(path) {
|
|
51
|
+
return path.length === 0 ? '(root)' : path.map(String).join('.');
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../src/payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// GENERATED by tools/api-snapshot.mjs — do not edit by hand.
|
|
2
|
+
//
|
|
3
|
+
// The `types@<5.9` gate of design/08 §2.2, first in every condition object of every subpath in
|
|
4
|
+
// package.json. A consumer on TypeScript < 5.9 resolves `@pg-prime/events` to THIS file instead of the
|
|
5
|
+
// real declarations, so every name still exists and every USE of one lands the message in the
|
|
6
|
+
// error text. Verified against TypeScript 5.8.3 by tools/pack-smoke.mjs on every run:
|
|
7
|
+
//
|
|
8
|
+
// consumer.ts(12,15): error TS2349: This expression is not callable.
|
|
9
|
+
// Type '{ ERROR: "@pg-prime/events requires TypeScript >= 5.9 — see https://pg-prime.dev/ts"; }' has no call signatures.
|
|
10
|
+
//
|
|
11
|
+
// Naming the key `ERROR` rather than a `__dunder__` is §2.2's own refinement over Kysely's
|
|
12
|
+
// version: it reads first in a truncated hover.
|
|
13
|
+
//
|
|
14
|
+
// ## `Unsupported<M = …> = M` is an identity alias ON PURPOSE — do not "simplify" it
|
|
15
|
+
//
|
|
16
|
+
// Written the obvious way, `type Unsupported = { ERROR: '…' }`, TypeScript prints the ALIAS NAME
|
|
17
|
+
// in the diagnostic — "Type 'Unsupported' has no call signatures" — and the message never
|
|
18
|
+
// reaches the user, which defeats the entire file. A generic alias whose body is just its own
|
|
19
|
+
// parameter has no alias symbol to print, so the checker falls back to the structural form and
|
|
20
|
+
// the sentence appears. Measured, both shapes, on 5.9.3 and 5.8.3.
|
|
21
|
+
//
|
|
22
|
+
// The lists are the root entry's value and type exports, taken from tools/api-snapshot/*.json.
|
|
23
|
+
// `node tools/api-snapshot.mjs --check` fails if this file and that golden disagree, which is
|
|
24
|
+
// what stops the stub from silently rotting into "this package has no exported member 'x'".
|
|
25
|
+
// The type parameters are optional and are only as many as the real declaration has, so that a
|
|
26
|
+
// generic use resolves to the brand instead of erroring with "is not generic".
|
|
27
|
+
|
|
28
|
+
type Unsupported<Message = { ERROR: '@pg-prime/events requires TypeScript >= 5.9 — see https://pg-prime.dev/ts' }> = Message
|
|
29
|
+
|
|
30
|
+
export declare const EventsLeaseLostError: Unsupported
|
|
31
|
+
export declare const NotStartedError: Unsupported
|
|
32
|
+
export declare const PayloadError: Unsupported
|
|
33
|
+
export declare const SchemaVersionError: Unsupported
|
|
34
|
+
export declare const defineChannel: Unsupported
|
|
35
|
+
export declare const defineSubscription: Unsupported
|
|
36
|
+
export declare const defineTopic: Unsupported
|
|
37
|
+
export declare const engineChannel: Unsupported
|
|
38
|
+
export declare const engineChannelSql: Unsupported
|
|
39
|
+
export declare const pgEvents: Unsupported
|
|
40
|
+
export declare const waker: Unsupported
|
|
41
|
+
|
|
42
|
+
export type BatchHandler<T0 = any> = Unsupported
|
|
43
|
+
export type ChannelDef<T0 = any> = Unsupported
|
|
44
|
+
export type ChannelHandle<T0 = any> = Unsupported
|
|
45
|
+
export type Entry<T0 = any> = Unsupported
|
|
46
|
+
export type EntryHandler<T0 = any> = Unsupported
|
|
47
|
+
export type Events<T0 = any> = Unsupported
|
|
48
|
+
export type EventsConfig<T0 = any> = Unsupported
|
|
49
|
+
export type EventsDefinitions = Unsupported
|
|
50
|
+
export type EventsHooks = Unsupported
|
|
51
|
+
export type EventsLeaseLostError = Unsupported
|
|
52
|
+
export type InternalKind = Unsupported
|
|
53
|
+
export type NotStartedError = Unsupported
|
|
54
|
+
export type PayloadError = Unsupported
|
|
55
|
+
export type PayloadOf<T0 = any> = Unsupported
|
|
56
|
+
export type PayloadOptions<T0 = any> = Unsupported
|
|
57
|
+
export type PublishOptions = Unsupported
|
|
58
|
+
export type ReadContext = Unsupported
|
|
59
|
+
export type Reader = Unsupported
|
|
60
|
+
export type RetryOptions = Unsupported
|
|
61
|
+
export type RunOptions = Unsupported
|
|
62
|
+
export type SchemaVersionError = Unsupported
|
|
63
|
+
export type StandardSchemaV1<T0 = any, T1 = any> = Unsupported
|
|
64
|
+
export type SubscriptionDef<T0 = any> = Unsupported
|
|
65
|
+
export type SubscriptionHandle<T0 = any> = Unsupported
|
|
66
|
+
export type TopicDef<T0 = any> = Unsupported
|
|
67
|
+
export type TopicHandle<T0 = any> = Unsupported
|
|
68
|
+
export type WakeReason = Unsupported
|
|
69
|
+
export type Waker = Unsupported
|
|
70
|
+
export type WakerOptions = Unsupported
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const ASYNC_DISPOSE: symbol;
|
|
2
|
+
/** Calls every observer; a throwing one neither stops the rest nor escapes. */
|
|
3
|
+
export declare function emitAll(observers: Iterable<(e: unknown) => void>, e: unknown): void;
|
|
4
|
+
/** Resolves after `ms`, or early when `signal` aborts. */
|
|
5
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
6
|
+
export declare function backoffMs(p: {
|
|
7
|
+
baseDelayMs: number;
|
|
8
|
+
maxDelayMs: number;
|
|
9
|
+
jitter: 'full' | 'equal' | 'none';
|
|
10
|
+
}, attempt: number, random?: () => number): number;
|
|
11
|
+
//# sourceMappingURL=util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,EAAE,MAC6D,CAAA;AAEzF,+EAA+E;AAC/E,wBAAgB,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC,EAAE,OAAO,GAAG,IAAI,CAMnF;AAED,0DAA0D;AAC1D,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrE;AAED,wBAAgB,SAAS,CACvB,CAAC,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;CAAE,EACjF,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,MAAoB,GACjC,MAAM,CAIR"}
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const ASYNC_DISPOSE = Symbol.asyncDispose ?? Symbol.for('Symbol.asyncDispose');
|
|
2
|
+
/** Calls every observer; a throwing one neither stops the rest nor escapes. */
|
|
3
|
+
export function emitAll(observers, e) {
|
|
4
|
+
for (const h of observers) {
|
|
5
|
+
try {
|
|
6
|
+
h(e);
|
|
7
|
+
}
|
|
8
|
+
catch { }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Resolves after `ms`, or early when `signal` aborts. */
|
|
12
|
+
export function sleep(ms, signal) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
if (signal?.aborted === true)
|
|
15
|
+
return resolve();
|
|
16
|
+
const done = () => {
|
|
17
|
+
clearTimeout(t);
|
|
18
|
+
signal?.removeEventListener('abort', done);
|
|
19
|
+
resolve();
|
|
20
|
+
};
|
|
21
|
+
const t = setTimeout(done, ms);
|
|
22
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function backoffMs(p, attempt, random = Math.random) {
|
|
26
|
+
const ceiling = Math.min(p.maxDelayMs, p.baseDelayMs * 2 ** Math.max(0, attempt - 1));
|
|
27
|
+
if (p.jitter === 'none')
|
|
28
|
+
return ceiling;
|
|
29
|
+
return p.jitter === 'equal' ? ceiling / 2 + random() * (ceiling / 2) : random() * ceiling;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GACvB,MAAoC,CAAC,YAAY,IAAI,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA;AAEzF,+EAA+E;AAC/E,MAAM,UAAU,OAAO,CAAC,SAAyC,EAAE,CAAU;IAC3E,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,CAAC,CAAC,CAAC,CAAC,CAAA;QACN,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC;AACH,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI;YAAE,OAAO,OAAO,EAAE,CAAA;QAC9C,MAAM,IAAI,GAAG,GAAS,EAAE;YACtB,YAAY,CAAC,CAAC,CAAC,CAAA;YACf,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YAC1C,OAAO,EAAE,CAAA;QACX,CAAC,CAAA;QACD,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAC9B,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACzD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,CAAiF,EACjF,OAAe,EACf,MAAM,GAAiB,IAAI,CAAC,MAAM;IAElC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,WAAW,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAA;IACrF,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,OAAO,CAAA;IACvC,OAAO,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,OAAO,CAAA;AAC3F,CAAC"}
|
package/dist/waker.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Db } from 'pg-prime';
|
|
2
|
+
import type { EventsHooks } from './hooks.js';
|
|
3
|
+
export type WakeReason = 'notify' | 'gap' | 'poll';
|
|
4
|
+
export interface WakerOptions {
|
|
5
|
+
pollMs: number;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
/** Receives `listen-unavailable` when the waker falls back to polling. */
|
|
8
|
+
onInternal?: EventsHooks['onInternal'];
|
|
9
|
+
}
|
|
10
|
+
/** A single-use async iterable of wake reasons, plus `wait(ms)` for loops with their own timers. */
|
|
11
|
+
export interface Waker extends AsyncIterable<WakeReason> {
|
|
12
|
+
/** The next reason, waiting at most `ms` (default `pollMs`; a timeout is `'poll'`); `undefined` once closed. */
|
|
13
|
+
wait(ms?: number): Promise<WakeReason | undefined>;
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Wakes on a NOTIFY on `channel`, a LISTEN `gap`, or `pollMs` of silence. LISTEN starts here; once
|
|
18
|
+
* it is established the first reason is `'gap'`, since anything sent before it was missed. Behind
|
|
19
|
+
* a transaction pooler without `directConnection` it only polls.
|
|
20
|
+
*/
|
|
21
|
+
export declare function waker(db: Db, channel: string, opts: WakerOptions): Waker;
|
|
22
|
+
//# sourceMappingURL=waker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waker.d.ts","sourceRoot":"","sources":["../src/waker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAgB,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAA;AAElD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAA;CACvC;AAED,oGAAoG;AACpG,MAAM,WAAW,KAAM,SAAQ,aAAa,CAAC,UAAU,CAAC;IACtD,gHAAgH;IAChH,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAA;IAClD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,KAAK,CA0ExE"}
|
package/dist/waker.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { UnsupportedInPoolerModeError } from 'pg-prime';
|
|
2
|
+
/**
|
|
3
|
+
* Wakes on a NOTIFY on `channel`, a LISTEN `gap`, or `pollMs` of silence. LISTEN starts here; once
|
|
4
|
+
* it is established the first reason is `'gap'`, since anything sent before it was missed. Behind
|
|
5
|
+
* a transaction pooler without `directConnection` it only polls.
|
|
6
|
+
*/
|
|
7
|
+
export function waker(db, channel, opts) {
|
|
8
|
+
let sub;
|
|
9
|
+
let listening;
|
|
10
|
+
let unsupported = false;
|
|
11
|
+
let reported = false;
|
|
12
|
+
let flag;
|
|
13
|
+
let wake;
|
|
14
|
+
let closed = opts.signal?.aborted === true;
|
|
15
|
+
const raise = (kind) => {
|
|
16
|
+
if (kind === 'gap' || flag === undefined)
|
|
17
|
+
flag = kind;
|
|
18
|
+
wake?.();
|
|
19
|
+
};
|
|
20
|
+
const listen = async () => {
|
|
21
|
+
try {
|
|
22
|
+
const s = await db.listen(channel, () => raise('notify'));
|
|
23
|
+
if (closed)
|
|
24
|
+
return void (await s.close());
|
|
25
|
+
s.on('gap', () => raise('gap'));
|
|
26
|
+
sub = s;
|
|
27
|
+
reported = false;
|
|
28
|
+
raise('gap');
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
unsupported = e instanceof UnsupportedInPoolerModeError;
|
|
32
|
+
if (reported)
|
|
33
|
+
return;
|
|
34
|
+
reported = true;
|
|
35
|
+
opts.onInternal?.({
|
|
36
|
+
kind: 'listen-unavailable',
|
|
37
|
+
message: `@pg-prime/events: cannot LISTEN on ${channel}; polling every ${opts.pollMs} ms.`,
|
|
38
|
+
cause: e,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const close = async () => {
|
|
43
|
+
closed = true;
|
|
44
|
+
wake?.();
|
|
45
|
+
const s = sub;
|
|
46
|
+
sub = undefined;
|
|
47
|
+
await s?.close();
|
|
48
|
+
};
|
|
49
|
+
const wait = async (ms = opts.pollMs) => {
|
|
50
|
+
if (sub === undefined && !unsupported && !closed) {
|
|
51
|
+
listening ??= listen();
|
|
52
|
+
await listening;
|
|
53
|
+
listening = undefined;
|
|
54
|
+
}
|
|
55
|
+
if (flag === undefined && !closed) {
|
|
56
|
+
await new Promise((resolve) => {
|
|
57
|
+
const t = setTimeout(resolve, ms);
|
|
58
|
+
wake = () => {
|
|
59
|
+
clearTimeout(t);
|
|
60
|
+
resolve();
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
wake = undefined;
|
|
64
|
+
}
|
|
65
|
+
if (closed)
|
|
66
|
+
return undefined;
|
|
67
|
+
const reason = flag ?? 'poll';
|
|
68
|
+
flag = undefined;
|
|
69
|
+
return reason;
|
|
70
|
+
};
|
|
71
|
+
opts.signal?.addEventListener('abort', () => void close(), { once: true });
|
|
72
|
+
if (!closed)
|
|
73
|
+
listening = listen();
|
|
74
|
+
const it = {
|
|
75
|
+
next: async () => {
|
|
76
|
+
const value = await wait();
|
|
77
|
+
return value === undefined ? { done: true, value: undefined } : { done: false, value };
|
|
78
|
+
},
|
|
79
|
+
return: async () => {
|
|
80
|
+
await close();
|
|
81
|
+
return { done: true, value: undefined };
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
return { wait, close, [Symbol.asyncIterator]: () => it };
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=waker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waker.js","sourceRoot":"","sources":["../src/waker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pg-prime/events",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Typed LISTEN/NOTIFY channels, a durable event log with subscriptions, and tails for pg-prime. Pre-alpha — the API will change.
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Typed LISTEN/NOTIFY channels, a durable event log with subscriptions, and tails for pg-prime. Pre-alpha — the API will change.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"event-log",
|
|
7
|
+
"events",
|
|
8
|
+
"listen-notify",
|
|
9
|
+
"pg-prime",
|
|
10
|
+
"postgres",
|
|
11
|
+
"postgresql",
|
|
12
|
+
"pubsub"
|
|
13
|
+
],
|
|
5
14
|
"license": "MPL-2.0",
|
|
6
15
|
"repository": {
|
|
7
16
|
"type": "git",
|
|
8
17
|
"url": "git+https://github.com/pg-prime/pg-prime.git"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types@<5.9": "./dist/unsupported-typescript.d.ts",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public",
|
|
34
|
+
"provenance": true
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"fast-check": "^4.10.2",
|
|
38
|
+
"vitest": "^4.1.10",
|
|
39
|
+
"@pg-prime/testing": "0.1.1",
|
|
40
|
+
"pg-prime": "0.2.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"pg-prime": ">=0.2.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=22.12"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "node ../../tools/build-package.mjs",
|
|
50
|
+
"clean": "node ../../tools/build-package.mjs --clean",
|
|
51
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
52
|
+
"test": "vitest run --project unit",
|
|
53
|
+
"test:live": "vitest run --project unit --project live",
|
|
54
|
+
"test:pg": "vitest run"
|
|
9
55
|
}
|
|
10
|
-
}
|
|
56
|
+
}
|