@codenotch/process 0.0.0-dev
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 +51 -0
- package/dist/activity.mjs +14 -0
- package/dist/template.mjs +21 -0
- package/dist/types/activity/index.d.ts +52 -0
- package/dist/types/lib/bindings.d.ts +48 -0
- package/dist/types/lib/canonical-json.d.ts +10 -0
- package/dist/types/lib/collections.d.ts +12 -0
- package/dist/types/lib/connectors.d.ts +299 -0
- package/dist/types/lib/context.d.ts +307 -0
- package/dist/types/lib/credits.d.ts +71 -0
- package/dist/types/lib/decimal.d.ts +72 -0
- package/dist/types/lib/duration.d.ts +40 -0
- package/dist/types/lib/encoding.d.ts +27 -0
- package/dist/types/lib/errors.d.ts +57 -0
- package/dist/types/lib/hash.d.ts +18 -0
- package/dist/types/lib/index.d.ts +25 -0
- package/dist/types/lib/instant.d.ts +41 -0
- package/dist/types/lib/meters.d.ts +61 -0
- package/dist/types/lib/registry.d.ts +131 -0
- package/dist/types/lib/renderings.d.ts +75 -0
- package/dist/types/lib/subscriptions.d.ts +61 -0
- package/dist/types/lib/types.d.ts +89 -0
- package/dist/types/lib/urls.d.ts +36 -0
- package/dist/types/lib/userTasks.d.ts +119 -0
- package/dist/types/lib/validators.d.ts +22 -0
- package/dist/types/lib/webPush.d.ts +30 -0
- package/dist/types/template/index.d.ts +17 -0
- package/dist/types/testing/api.d.ts +175 -0
- package/dist/types/testing/index.d.ts +2 -0
- package/dist/types/testing/main.d.ts +1 -0
- package/dist/types/testing/stdlib.d.ts +79 -0
- package/package.json +43 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Json, Template, TemplateKind } from '../lib/types';
|
|
2
|
+
import type { EmailTemplatePayload, PdfPageOptions, PdfTemplatePayload } from '../lib/renderings';
|
|
3
|
+
export type { EmailTemplatePayload, PdfPageOptions, PdfTemplatePayload, Template, TemplateKind, };
|
|
4
|
+
type MaybePromise<T> = T | PromiseLike<T>;
|
|
5
|
+
export declare const template: Readonly<{
|
|
6
|
+
/** Declares an email template: render(data) returns { subject, html, text? }. */
|
|
7
|
+
email: <D = Json>(render: (data: D) => MaybePromise<EmailTemplatePayload>) => Template<D, "email">;
|
|
8
|
+
/**
|
|
9
|
+
* Declares a pdf template: render(data) returns { html } (print CSS; Chromium paged
|
|
10
|
+
* media — @page margin boxes, counter(page/pages) — works natively). The optional
|
|
11
|
+
* first argument sets the page: template.pdf({ format: 'A4' }, render).
|
|
12
|
+
*/
|
|
13
|
+
pdf: {
|
|
14
|
+
<D = Json>(render: (data: D) => MaybePromise<PdfTemplatePayload>): Template<D, "pdf">;
|
|
15
|
+
<D = Json>(page: PdfPageOptions, render: (data: D) => MaybePromise<PdfTemplatePayload>): Template<D, "pdf">;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { Activity, DurationLike, DurationParts, Json, Serializable } from '../lib/types';
|
|
2
|
+
/** The body of one test. It receives the environment; throwing fails the test. */
|
|
3
|
+
export type TestFn = (env: TestEnv) => void | PromiseLike<void>;
|
|
4
|
+
export interface TestOptions {
|
|
5
|
+
/** Per-test budget in milliseconds. Default: two minutes. */
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}
|
|
8
|
+
/** The simulated caller of env.start — reaches the process as ctx.user. */
|
|
9
|
+
export interface TestUser {
|
|
10
|
+
/** Any stable string; generated when omitted. */
|
|
11
|
+
id?: string;
|
|
12
|
+
email?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
roles?: string[];
|
|
15
|
+
/** Defaults to 'internal'. The process's declared auth is enforced against it. */
|
|
16
|
+
group?: 'internal' | 'external';
|
|
17
|
+
language?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface StartOptions {
|
|
20
|
+
input?: Serializable;
|
|
21
|
+
/** Omitted: the process starts as the system user. */
|
|
22
|
+
user?: TestUser;
|
|
23
|
+
/** Overrides applied on top of the project's resolved parameters. */
|
|
24
|
+
params?: Record<string, Serializable>;
|
|
25
|
+
}
|
|
26
|
+
/** Where an instance stands. Terminal: SUCCESS, ERROR, STOPPED, TERMINATED, FAILED_RECOVERY. */
|
|
27
|
+
export type RunState = 'SUCCESS' | 'ERROR' | 'WAITING' | 'EXECUTING' | 'READY' | 'IDLE' | 'PAUSED' | 'STOPPED' | 'STOPPING' | 'TERMINATED' | 'FAILED_RECOVERY';
|
|
28
|
+
/** The failure of an ERROR run, as journaled. */
|
|
29
|
+
export interface RunFailure {
|
|
30
|
+
name: string;
|
|
31
|
+
message: string;
|
|
32
|
+
/** The code of a ProcessError; null/absent otherwise. */
|
|
33
|
+
code?: string | null;
|
|
34
|
+
details?: Json;
|
|
35
|
+
}
|
|
36
|
+
/** What an api-triggered process answered with. */
|
|
37
|
+
export interface RunResponse {
|
|
38
|
+
status: number;
|
|
39
|
+
body?: Json;
|
|
40
|
+
headers?: Record<string, string> | null;
|
|
41
|
+
}
|
|
42
|
+
/** One signal a run sent: its ref and its payload. */
|
|
43
|
+
export interface SentSignal {
|
|
44
|
+
ref: string;
|
|
45
|
+
data: Json;
|
|
46
|
+
}
|
|
47
|
+
/** Handle on a process instance the test started. */
|
|
48
|
+
export interface TestRun<Out = Json> {
|
|
49
|
+
readonly instanceId: string;
|
|
50
|
+
/** 'SUCCESS', 'ERROR', 'WAITING', … Reading is safe in any state. */
|
|
51
|
+
state(): Promise<RunState>;
|
|
52
|
+
/** What the process returned; reading it from a failed run throws that failure. */
|
|
53
|
+
output(): Promise<Out>;
|
|
54
|
+
/** The failure of an ERROR run; reading it from a run that did not fail throws. */
|
|
55
|
+
failure(): Promise<RunFailure>;
|
|
56
|
+
/** What it answered with — how a test tells a 403 from a 200. Null when it never responded. */
|
|
57
|
+
response(): Promise<RunResponse | null>;
|
|
58
|
+
/** The arguments of each call of that step, in order. Takes the imported activity handle or a name. */
|
|
59
|
+
activityCalls<In>(activity: Activity<In, unknown>): Promise<In[]>;
|
|
60
|
+
activityCalls(activity: string): Promise<Json[]>;
|
|
61
|
+
/** The signals it sent; a trailing '*' matches by prefix ('contact.*'). */
|
|
62
|
+
sentSignals(pattern?: string): Promise<SentSignal[]>;
|
|
63
|
+
/** The payloads of the declared events it sent to its caller (every event when no id is given). */
|
|
64
|
+
sentEvents(eventId?: string): Promise<Json[]>;
|
|
65
|
+
}
|
|
66
|
+
/** Handle on one datastore of the deployed project. */
|
|
67
|
+
export interface TestStore<Row = Record<string, Json>> {
|
|
68
|
+
/** Empties this store and inserts the rows (each may carry its Id). Nothing else is touched. */
|
|
69
|
+
seed(rows: ReadonlyArray<Partial<Row> & {
|
|
70
|
+
Id?: string;
|
|
71
|
+
}>): Promise<void>;
|
|
72
|
+
/** Every row, as stored. */
|
|
73
|
+
rows(): Promise<Row[]>;
|
|
74
|
+
/** One record by id, or null. */
|
|
75
|
+
get(id: string): Promise<Row | null>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A duration for env.advance: everything a DurationLike accepts ('PT5M', 180000,
|
|
79
|
+
* { days: 3 }), plus the ms shorthand ({ ms: 500 }). Calendar components
|
|
80
|
+
* (years/months) have no fixed length and are rejected, as ctx.wait rejects them.
|
|
81
|
+
*/
|
|
82
|
+
export type AdvanceDuration = DurationLike | (DurationParts & {
|
|
83
|
+
ms?: number;
|
|
84
|
+
});
|
|
85
|
+
export interface TestActivityOptions {
|
|
86
|
+
/** Per-call budget; unbounded when absent (the test's own budget still applies). */
|
|
87
|
+
timeout?: DurationLike;
|
|
88
|
+
}
|
|
89
|
+
/** The environment a test drives: seed data, install mocks, start processes, move the clock, assert. */
|
|
90
|
+
export interface TestEnv {
|
|
91
|
+
/** Handle on one of the project's tables. */
|
|
92
|
+
table<Row = Record<string, Json>>(name: string): TestStore<Row>;
|
|
93
|
+
/** Handle on one of the project's document stores. */
|
|
94
|
+
doc<Doc = Record<string, Json>>(store: string): TestStore<Doc>;
|
|
95
|
+
/**
|
|
96
|
+
* Replaces a step for this test. Pass the imported activity handle for a typed
|
|
97
|
+
* mock, or name a built-in step by its activity name ('table.write', 'node.run',
|
|
98
|
+
* 'http.fetch', 'email.send', …) / a project activity by its export. The mock
|
|
99
|
+
* receives the arguments the process passed; throwing fails the step exactly
|
|
100
|
+
* like a real failure.
|
|
101
|
+
*/
|
|
102
|
+
mock<In, Out>(activity: Activity<In, Out>, fn: (args: In) => Out | PromiseLike<Out>): void;
|
|
103
|
+
mock(activity: string, fn: (args: any) => Serializable | PromiseLike<Serializable>): void;
|
|
104
|
+
/** Stubs the one CNQL query matching this ref; every other query still runs for real. */
|
|
105
|
+
onQuery(queryRef: string, fn: (args: Record<string, Json> | null) => Serializable | PromiseLike<Serializable>): void;
|
|
106
|
+
/**
|
|
107
|
+
* Executes one of the project's DEPLOYED activities as a black box — the test-side
|
|
108
|
+
* mirror of ctx.activity. The real artifact runs on the real Node runtime with a
|
|
109
|
+
* real io; mocks do not apply (a mock replaces a step inside a process run — this
|
|
110
|
+
* IS the activity), and it is a single attempt: retries are a calling process's
|
|
111
|
+
* policy, not the activity's behavior. A failure throws exactly what a process
|
|
112
|
+
* would catch (ProcessError for coded failures).
|
|
113
|
+
*/
|
|
114
|
+
activity<In, Out>(activity: Activity<In, Out>, input: In, opts?: TestActivityOptions): Promise<Out>;
|
|
115
|
+
activity<T = Json>(reference: string, input?: Serializable, opts?: TestActivityOptions): Promise<T>;
|
|
116
|
+
/** Starts a process and settles the world: when this resolves, the run parked or finished. */
|
|
117
|
+
start<Out = Json>(processId: string, options?: StartOptions): Promise<TestRun<Out>>;
|
|
118
|
+
/** Moves the test's clock and runs everything that came due. */
|
|
119
|
+
advance(duration: AdvanceDuration): Promise<void>;
|
|
120
|
+
/** Emits a signal, exactly as a process would. */
|
|
121
|
+
sendSignal(ref: string, data?: Serializable): Promise<void>;
|
|
122
|
+
/** The test's current time, ISO-8601 UTC. It moves only when the test advances it. */
|
|
123
|
+
now(): string;
|
|
124
|
+
}
|
|
125
|
+
/** The assertion vocabulary. All object matching compares numbers by value and dates normalized to UTC. */
|
|
126
|
+
export interface Matchers {
|
|
127
|
+
/** The value matches exactly, with no extra fields anywhere. */
|
|
128
|
+
toEqual(expected: unknown): void;
|
|
129
|
+
/** Every field the subset lists matches; unlisted fields are ignored (recursively). */
|
|
130
|
+
toMatch(subset: unknown): void;
|
|
131
|
+
/** Some element of the array matches the subset. */
|
|
132
|
+
toContainMatch(subset: unknown): void;
|
|
133
|
+
toHaveLength(n: number): void;
|
|
134
|
+
/** Scalar equality. */
|
|
135
|
+
toBe(expected: unknown): void;
|
|
136
|
+
toBeNull(): void;
|
|
137
|
+
toBeDefined(): void;
|
|
138
|
+
toBeUndefined(): void;
|
|
139
|
+
}
|
|
140
|
+
/** Declares one test. Files declare with `test(...)`; the platform runs them in declaration order. */
|
|
141
|
+
export declare function test(name: string, fn: TestFn): void;
|
|
142
|
+
export declare function test(name: string, options: TestOptions, fn: TestFn): void;
|
|
143
|
+
/** Wraps a value for assertion. A failed expectation throws with the path it differed at. */
|
|
144
|
+
export declare const expect: (actual: unknown) => Matchers;
|
|
145
|
+
/**
|
|
146
|
+
* The platform surface behind env (ScriptTestEnvironment on the C# side). The names
|
|
147
|
+
* and signatures here are the stable contract between the engine and this bundle.
|
|
148
|
+
* Task-returning members surface as promises in the test host.
|
|
149
|
+
*/
|
|
150
|
+
export interface TestHostBridge {
|
|
151
|
+
Seed(datastore: string, isDocument: boolean, rowsJson: string | null): PromiseLike<void>;
|
|
152
|
+
Rows(datastore: string, isDocument: boolean): PromiseLike<string | null>;
|
|
153
|
+
Get(datastore: string, isDocument: boolean, id: string): PromiseLike<string | null>;
|
|
154
|
+
/** Routes the named step to this engine's __invokeMock while the test runs. */
|
|
155
|
+
Mock(activityName: string): void;
|
|
156
|
+
Now(): string;
|
|
157
|
+
/** Returns the outcome envelope: {ok:true, result} or {ok:false, error:{name,code,message}}. */
|
|
158
|
+
Activity(reference: string, inputJson: string | null, timeoutMsJson: string | null): PromiseLike<string>;
|
|
159
|
+
Start(processId: string, optionsJson: string | null): PromiseLike<string>;
|
|
160
|
+
Advance(durationJson: string | null): PromiseLike<void>;
|
|
161
|
+
SendSignal(signalRef: string, dataJson: string | null): PromiseLike<void>;
|
|
162
|
+
RunState(instanceId: string): PromiseLike<string>;
|
|
163
|
+
RunOutput(instanceId: string): PromiseLike<string | null>;
|
|
164
|
+
RunFailure(instanceId: string): PromiseLike<string | null>;
|
|
165
|
+
RunResponse(instanceId: string): PromiseLike<string | null>;
|
|
166
|
+
RunActivityCalls(instanceId: string, activityName: string): PromiseLike<string | null>;
|
|
167
|
+
RunSentSignals(instanceId: string, refPattern: string | null): PromiseLike<string | null>;
|
|
168
|
+
RunSentEvents(instanceId: string, eventId: string | null): PromiseLike<string | null>;
|
|
169
|
+
}
|
|
170
|
+
/** @internal The declared tests, for the runner: name + timeout, in declaration order. */
|
|
171
|
+
export declare const testNamesJson: () => string;
|
|
172
|
+
/** @internal Runs one declared test against the host bridge the runner passes. */
|
|
173
|
+
export declare const runTest: (index: number, host: TestHostBridge) => Promise<void>;
|
|
174
|
+
/** @internal Invokes the mock the running test installed for an activity. */
|
|
175
|
+
export declare const invokeMock: (activityName: string, inputJson: string | null) => Promise<string | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as lib from '../lib/index';
|
|
2
|
+
/**
|
|
3
|
+
* The crypto backing ScriptTestHost injects as __host — the same pure functions the
|
|
4
|
+
* workflow runtime provides, so hash.* behaves identically in tests and processes.
|
|
5
|
+
*/
|
|
6
|
+
export interface TestCryptoHost {
|
|
7
|
+
HashSha256(dataBase64: string): string;
|
|
8
|
+
HashHmacSha256(keyBase64: string, dataBase64: string): string;
|
|
9
|
+
TimingSafeEqual(aBase64: string, bBase64: string): boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Binds the library's engine seam to what the test host actually provides: the pure
|
|
13
|
+
* crypto backing. Everything else on the seam belongs to a running instance and is
|
|
14
|
+
* unreachable through the public surface — reaching it anyway names the gap.
|
|
15
|
+
*/
|
|
16
|
+
export declare const installTestBindings: (host: TestCryptoHost | undefined) => void;
|
|
17
|
+
/**
|
|
18
|
+
* What require('@codenotch/process') returns in the test host: the real library, with
|
|
19
|
+
* the process-definition constructs replaced by refusals that say what to do instead.
|
|
20
|
+
*/
|
|
21
|
+
export declare const stdlibModule: Readonly<{
|
|
22
|
+
process: () => never;
|
|
23
|
+
api: Readonly<{
|
|
24
|
+
get: () => never;
|
|
25
|
+
post: () => never;
|
|
26
|
+
put: () => never;
|
|
27
|
+
delete: () => never;
|
|
28
|
+
}>;
|
|
29
|
+
signal: () => never;
|
|
30
|
+
timer: () => never;
|
|
31
|
+
connectorEvent: () => never;
|
|
32
|
+
install: () => never;
|
|
33
|
+
manual: () => never;
|
|
34
|
+
events: () => never;
|
|
35
|
+
ProcessError: typeof lib.ProcessError;
|
|
36
|
+
CallFailedError: typeof lib.CallFailedError;
|
|
37
|
+
TimeoutError: typeof lib.TimeoutError;
|
|
38
|
+
CancelledError: typeof lib.CancelledError;
|
|
39
|
+
CallCompletedError: typeof lib.CallCompletedError;
|
|
40
|
+
NotFoundError: typeof lib.NotFoundError;
|
|
41
|
+
Instant: typeof lib.Instant;
|
|
42
|
+
Duration: typeof lib.Duration;
|
|
43
|
+
Decimal: typeof lib.Decimal;
|
|
44
|
+
base64: {
|
|
45
|
+
encode(data: string | Uint8Array, options?: {
|
|
46
|
+
url?: boolean;
|
|
47
|
+
}): string;
|
|
48
|
+
decode(text: string): Uint8Array;
|
|
49
|
+
decodeToText(text: string): string;
|
|
50
|
+
};
|
|
51
|
+
hex: {
|
|
52
|
+
encode(data: string | Uint8Array): string;
|
|
53
|
+
decode(text: string): Uint8Array;
|
|
54
|
+
decodeToText(text: string): string;
|
|
55
|
+
};
|
|
56
|
+
utf8: {
|
|
57
|
+
encode(text: string): Uint8Array;
|
|
58
|
+
decode(bytes: Uint8Array): string;
|
|
59
|
+
};
|
|
60
|
+
url: typeof lib.url;
|
|
61
|
+
canonicalJson: (value: lib.Serializable) => string;
|
|
62
|
+
chunk: <T>(items: readonly T[], size: number) => T[][];
|
|
63
|
+
uniqueBy: <T>(items: readonly T[], key: (item: T) => unknown) => T[];
|
|
64
|
+
is: {
|
|
65
|
+
email: (value: unknown) => boolean;
|
|
66
|
+
uuid: (value: unknown) => boolean;
|
|
67
|
+
url: (value: unknown) => boolean;
|
|
68
|
+
isoDate: (value: unknown) => boolean;
|
|
69
|
+
isoDateTime: (value: unknown) => boolean;
|
|
70
|
+
isoDuration: (value: unknown) => boolean;
|
|
71
|
+
ipv4: (value: unknown) => boolean;
|
|
72
|
+
ipv6: (value: unknown) => boolean;
|
|
73
|
+
};
|
|
74
|
+
hash: {
|
|
75
|
+
sha256(data: string | Uint8Array, output?: lib.HashOutput): string;
|
|
76
|
+
hmacSha256(key: string | Uint8Array, data: string | Uint8Array, output?: lib.HashOutput): string;
|
|
77
|
+
timingSafeEqual(a: string | Uint8Array, b: string | Uint8Array): boolean;
|
|
78
|
+
};
|
|
79
|
+
}>;
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codenotch/process",
|
|
3
|
+
"version": "0.0.0-dev",
|
|
4
|
+
"description": "Types and standard library for Codenotch script processes. Processes execute on the server only: the engine embeds the compiled runtime (dist/prelude.js); this package distributes the type declarations.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/EchinoHub/service-core.git",
|
|
8
|
+
"directory": "src/codenotch-process"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"types": "./dist/types/lib/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/types/lib/index.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"./activity": {
|
|
19
|
+
"types": "./dist/types/activity/index.d.ts",
|
|
20
|
+
"default": "./dist/activity.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./template": {
|
|
23
|
+
"types": "./dist/types/template/index.d.ts",
|
|
24
|
+
"default": "./dist/template.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./testing": {
|
|
27
|
+
"types": "./dist/types/testing/index.d.ts"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist/types",
|
|
32
|
+
"dist/activity.mjs",
|
|
33
|
+
"dist/template.mjs"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "node build.mjs",
|
|
37
|
+
"check": "tsc -p tsconfig.json --noEmit"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"esbuild": "^0.25.9",
|
|
41
|
+
"typescript": "^5.9.2"
|
|
42
|
+
}
|
|
43
|
+
}
|