@graphvideo/kernel 1.0.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.
@@ -0,0 +1,28 @@
1
+ import type { Info, InfoEnvelope, SpanRecord, WorldChangeContext } from './types';
2
+ import type { StateDelta } from './observation';
3
+ import type { EffectAdapter } from './effects';
4
+ import type { Node } from './node';
5
+ /**
6
+ * Controlled Causal Context Implementation (ChangeContextImpl)
7
+ */
8
+ export declare class ChangeContextImpl<S = any> implements WorldChangeContext<S> {
9
+ private node;
10
+ private readonly rawInfo;
11
+ private readonly changeId;
12
+ private readonly envelope?;
13
+ private readonly signal?;
14
+ readonly reads: Set<string>;
15
+ readonly writes: Set<string>;
16
+ readonly spans: SpanRecord[];
17
+ readonly stateDeltas: StateDelta[];
18
+ private readonly traceValues;
19
+ constructor(node: Node<S, any>, rawInfo: Info, changeId: string, envelope?: InfoEnvelope | undefined, signal?: AbortSignal | undefined);
20
+ read<K extends keyof S>(key: K): S[K];
21
+ write<K extends keyof S>(key: K, value: S[K]): void;
22
+ patchState(patch: Partial<S>): void;
23
+ send(info: Info, target: string): void;
24
+ effectAdapter<Request, Observation>(adapter: EffectAdapter<Request, Observation>, request: Request, options?: {
25
+ signal?: AbortSignal;
26
+ }): Promise<Observation>;
27
+ span<T>(name: string, action: () => Promise<T> | T): Promise<T>;
28
+ }
@@ -0,0 +1,36 @@
1
+ import type { Node } from './node';
2
+ import type { Info, InfoEnvelope, Probe } from './types';
3
+ import type { TraceSession, RuntimeIdKind } from './observation';
4
+ export interface KernelDeliveryOptions {
5
+ infoId?: string;
6
+ causedByChangeId?: string;
7
+ causeInfoId?: string;
8
+ submissionId?: string;
9
+ signal?: AbortSignal;
10
+ }
11
+ export interface DeliveryKernelContext {
12
+ nodes: Map<string, Node<any>>;
13
+ envelopes: InfoEnvelope[];
14
+ probes: Probe[];
15
+ traceSession: TraceSession;
16
+ logicalClock: number;
17
+ pendingDeliveryCount: number;
18
+ scheduledMailboxNodeIds: Set<string>;
19
+ mount(...nodes: Node<any>[]): any;
20
+ ensureInitialStateSnapshot(): void;
21
+ publishSchedulerState(): void;
22
+ now(): number;
23
+ nextId(kind: RuntimeIdKind): string;
24
+ trackSubmissionDeliveryEnqueued(submissionId?: string): void;
25
+ trackSubmissionDeliverySettled(submissionId?: string, error?: unknown): void;
26
+ }
27
+ export declare function deliverSendNowHelper(ctx: DeliveryKernelContext, source: Node<any>, info: Info, target: Node<any> | string, options?: KernelDeliveryOptions): void;
28
+ export declare function injectRootInfoNowHelper(ctx: DeliveryKernelContext, target: Node<any>, info: Info, options?: {
29
+ signal?: AbortSignal;
30
+ submissionId?: string;
31
+ }): void;
32
+ export declare function enqueueMailboxDeliveryHelper(ctx: DeliveryKernelContext, target: Node<any>, info: Info, envelope: InfoEnvelope, options: {
33
+ signal?: AbortSignal;
34
+ }): void;
35
+ export declare function scheduleMailboxDrainHelper(ctx: DeliveryKernelContext, target: Node<any>): void;
36
+ export declare function settleMailboxDeliveryHelper(ctx: DeliveryKernelContext, submissionId?: string, error?: unknown): void;
@@ -0,0 +1,51 @@
1
+ import type { Clock } from './observation';
2
+ import { ValueCodec, type EncodedValue, type ValueCodecOptions } from './observation';
3
+ import type { EffectAdapter } from './effects';
4
+ export interface EffectFixture<Request> {
5
+ readonly schemaVersion: 1;
6
+ readonly fixtureId: string;
7
+ readonly request: Request;
8
+ readonly expectedObservation?: EncodedValue;
9
+ }
10
+ export interface EffectHarnessRecord {
11
+ readonly schemaVersion: 1;
12
+ readonly fixtureId: string;
13
+ readonly executionId: string;
14
+ readonly adapterId: string;
15
+ readonly request: EncodedValue;
16
+ readonly observation?: EncodedValue;
17
+ readonly transportMetadata: readonly EncodedValue[];
18
+ readonly rawSummaries: readonly {
19
+ readonly kind: string;
20
+ readonly text: string;
21
+ readonly redacted: boolean;
22
+ }[];
23
+ readonly startedAt: number;
24
+ readonly completedAt: number;
25
+ readonly durationMs: number;
26
+ readonly status: 'succeeded' | 'failed';
27
+ readonly expectedMatched?: boolean;
28
+ readonly error?: string;
29
+ }
30
+ export interface EffectHarnessResult<Observation> {
31
+ readonly record: EffectHarnessRecord;
32
+ readonly observation?: Observation;
33
+ }
34
+ export interface EffectHarnessOptions {
35
+ readonly clock?: Clock;
36
+ readonly valueCodec?: ValueCodec;
37
+ readonly valueCodecOptions?: ValueCodecOptions;
38
+ readonly maxFixtureBytes?: number;
39
+ readonly maxRawSummaryCharacters?: number;
40
+ }
41
+ export declare class EffectHarness {
42
+ private readonly clock;
43
+ private readonly valueCodec;
44
+ private readonly valueCodecOptions;
45
+ private readonly maxFixtureBytes;
46
+ private readonly maxRawSummaryCharacters;
47
+ constructor(options?: EffectHarnessOptions);
48
+ run<Request, Observation>(adapter: EffectAdapter<Request, Observation>, fixture: EffectFixture<Request>, options?: {
49
+ signal?: AbortSignal;
50
+ }): Promise<EffectHarnessResult<Observation>>;
51
+ }
@@ -0,0 +1,191 @@
1
+ //#region kernel/src/observation.ts
2
+ var e = {
3
+ now: () => Date.now(),
4
+ monotonicNow: () => typeof performance < "u" ? performance.now() : Date.now()
5
+ }, t = class {
6
+ defaultOptions;
7
+ constructor(e = {}) {
8
+ this.defaultOptions = e;
9
+ }
10
+ encode(e, t = {}) {
11
+ let n = {
12
+ ...this.defaultOptions,
13
+ ...t
14
+ };
15
+ return this._encodeValue(e, n.maxDepth ?? 8, n.rootPath ?? [], n);
16
+ }
17
+ _encodeValue(e, t, n, r) {
18
+ if (e == null) return {
19
+ $type: "primitive",
20
+ value: e
21
+ };
22
+ let i = typeof e;
23
+ if (i === "string" || i === "number" || i === "boolean") return {
24
+ $type: "primitive",
25
+ value: e
26
+ };
27
+ if (i === "bigint" || i === "symbol" || i === "function") return {
28
+ $type: "ref",
29
+ kind: i,
30
+ summary: String(e)
31
+ };
32
+ if (t <= 0) return {
33
+ $type: "truncated",
34
+ originalType: i,
35
+ summary: "[Max Depth Reached]"
36
+ };
37
+ if (e instanceof Map) {
38
+ let i = [];
39
+ for (let [a, o] of e.entries()) i.push([this._encodeValue(a, t - 1, [...n, String(a)], r), this._encodeValue(o, t - 1, [...n, String(a)], r)]);
40
+ return {
41
+ $type: "map",
42
+ entries: i
43
+ };
44
+ }
45
+ if (e instanceof Set) return {
46
+ $type: "set",
47
+ values: Array.from(e.values()).map((e, i) => this._encodeValue(e, t - 1, [...n, String(i)], r))
48
+ };
49
+ if (Array.isArray(e)) {
50
+ let i = r.maxArrayLength ?? 100;
51
+ return {
52
+ $type: "array",
53
+ value: e.slice(0, i).map((e, i) => this._encodeValue(e, t - 1, [...n, String(i)], r))
54
+ };
55
+ }
56
+ if (e instanceof Uint8Array || typeof Buffer < "u" && Buffer.isBuffer(e)) return {
57
+ $type: "bytes",
58
+ value: typeof Buffer < "u" ? Buffer.from(e).toString("base64") : "",
59
+ byteLength: e.byteLength
60
+ };
61
+ if (i === "object") {
62
+ let i = {}, a = e, o = Object.keys(a);
63
+ for (let e of o) i[e] = r.redactedKeys?.includes(e) ? {
64
+ $type: "ref",
65
+ kind: "redacted",
66
+ summary: "[REDACTED]"
67
+ } : this._encodeValue(a[e], t - 1, [...n, e], r);
68
+ return {
69
+ $type: "object",
70
+ value: i
71
+ };
72
+ }
73
+ return {
74
+ $type: "primitive",
75
+ value: String(e)
76
+ };
77
+ }
78
+ decode(e) {
79
+ if (!e || typeof e != "object") return e;
80
+ switch (e.$type) {
81
+ case "primitive": return e.value;
82
+ case "array": return e.value.map((e) => this.decode(e));
83
+ case "map": {
84
+ let t = /* @__PURE__ */ new Map();
85
+ for (let [n, r] of e.entries) t.set(this.decode(n), this.decode(r));
86
+ return t;
87
+ }
88
+ case "set": {
89
+ let t = /* @__PURE__ */ new Set();
90
+ for (let n of e.values) t.add(this.decode(n));
91
+ return t;
92
+ }
93
+ case "object": {
94
+ let t = {};
95
+ for (let [n, r] of Object.entries(e.value)) t[n] = this.decode(r);
96
+ return t;
97
+ }
98
+ case "bytes": return typeof Buffer < "u" ? Buffer.from(e.value, "base64") : e.value;
99
+ case "ref":
100
+ case "truncated": return e.summary;
101
+ default: return e;
102
+ }
103
+ }
104
+ };
105
+ new t();
106
+ //#endregion
107
+ //#region kernel/src/effect-harness.ts
108
+ var n = 65536, r = 2048, i = class {
109
+ clock;
110
+ valueCodec;
111
+ valueCodecOptions;
112
+ maxFixtureBytes;
113
+ maxRawSummaryCharacters;
114
+ constructor(i = {}) {
115
+ if (this.clock = i.clock ?? e, this.valueCodec = i.valueCodec ?? new t(), this.valueCodecOptions = i.valueCodecOptions ?? {}, this.maxFixtureBytes = i.maxFixtureBytes ?? n, this.maxRawSummaryCharacters = i.maxRawSummaryCharacters ?? r, !Number.isSafeInteger(this.maxFixtureBytes) || this.maxFixtureBytes <= 0) throw Error("EffectHarness maxFixtureBytes 必须是正安全整数");
116
+ if (!Number.isSafeInteger(this.maxRawSummaryCharacters) || this.maxRawSummaryCharacters <= 0) throw Error("EffectHarness maxRawSummaryCharacters 必须是正安全整数");
117
+ }
118
+ async run(e, t, n = {}) {
119
+ if (!e.id.trim()) throw Error("EffectAdapter id 不能为空");
120
+ if (t.schemaVersion !== 1) throw Error(`EffectFixture schemaVersion 不支持: ${t.schemaVersion}`);
121
+ if (!t.fixtureId.trim()) throw Error("EffectFixture fixtureId 不能为空");
122
+ if (n.signal?.aborted) throw n.signal.reason ?? /* @__PURE__ */ Error("Effect 已取消");
123
+ let r = this.valueCodec.encode(t.request, {
124
+ ...this.valueCodecOptions,
125
+ maxBytes: Math.min(this.valueCodecOptions.maxBytes ?? this.maxFixtureBytes, this.maxFixtureBytes),
126
+ rootPath: ["fixture", "request"]
127
+ }), i = [], a = [], o = this.clock.now(), s = this.clock.monotonicNow(), c = `${t.fixtureId}/${e.id}`, l = {
128
+ clock: this.clock,
129
+ signal: n.signal,
130
+ recordTransport: (e) => {
131
+ i.push(this.valueCodec.encode(e, {
132
+ ...this.valueCodecOptions,
133
+ maxBytes: this.maxFixtureBytes,
134
+ rootPath: ["transportMetadata", String(i.length)]
135
+ }));
136
+ },
137
+ recordRawSummary: (e) => {
138
+ if (!e.kind.trim()) throw Error("Effect raw summary kind 不能为空");
139
+ if (e.text.length > this.maxRawSummaryCharacters) throw Error(`Effect raw summary 超过 ${this.maxRawSummaryCharacters} 字符限制`);
140
+ a.push({
141
+ kind: e.kind,
142
+ text: e.text,
143
+ redacted: e.redacted ?? !1
144
+ });
145
+ }
146
+ };
147
+ try {
148
+ let n = await e.execute(t.request, l), u = this.valueCodec.encode(n, {
149
+ ...this.valueCodecOptions,
150
+ maxBytes: this.maxFixtureBytes,
151
+ rootPath: ["fixture", "observation"]
152
+ }), d = this.clock.now(), f = this.clock.monotonicNow() - s;
153
+ return {
154
+ observation: n,
155
+ record: {
156
+ schemaVersion: 1,
157
+ fixtureId: t.fixtureId,
158
+ executionId: c,
159
+ adapterId: e.id,
160
+ request: r,
161
+ observation: u,
162
+ transportMetadata: i,
163
+ rawSummaries: a,
164
+ startedAt: o,
165
+ completedAt: d,
166
+ durationMs: f,
167
+ status: "succeeded",
168
+ ...t.expectedObservation === void 0 ? {} : { expectedMatched: JSON.stringify(u) === JSON.stringify(t.expectedObservation) }
169
+ }
170
+ };
171
+ } catch (n) {
172
+ let l = this.clock.now();
173
+ return { record: {
174
+ schemaVersion: 1,
175
+ fixtureId: t.fixtureId,
176
+ executionId: c,
177
+ adapterId: e.id,
178
+ request: r,
179
+ transportMetadata: i,
180
+ rawSummaries: a,
181
+ startedAt: o,
182
+ completedAt: l,
183
+ durationMs: this.clock.monotonicNow() - s,
184
+ status: "failed",
185
+ error: n instanceof Error ? n.message : String(n)
186
+ } };
187
+ }
188
+ }
189
+ };
190
+ //#endregion
191
+ export { i as EffectHarness };
@@ -0,0 +1,16 @@
1
+ import type { Clock } from './observation';
2
+ /** Optional diagnostics are supplied only by an explicit test harness. */
3
+ export interface EffectContext {
4
+ readonly clock: Clock;
5
+ readonly signal?: AbortSignal;
6
+ recordTransport?(metadata: unknown): void;
7
+ recordRawSummary?(summary: {
8
+ readonly kind: string;
9
+ readonly text: string;
10
+ readonly redacted?: boolean;
11
+ }): void;
12
+ }
13
+ export interface EffectAdapter<Request = unknown, Observation = unknown> {
14
+ readonly id: string;
15
+ execute(request: Request, context: EffectContext): Promise<Observation>;
16
+ }
@@ -0,0 +1,8 @@
1
+ export * from './types';
2
+ export * from './node';
3
+ export * from './context';
4
+ export * from './observation';
5
+ export * from './effects';
6
+ export * from './scheduler';
7
+ export * from './runtime';
8
+ export * from './effect-harness';