@you-agent-factory/factory-emulator 0.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,5 @@
1
+ // Code generated by scripts/generate-emulator.mjs; DO NOT EDIT.
2
+
3
+ import schema from "../../generated/factory.schema.json" with { type: "json" };
4
+
5
+ export const factorySchema = schema;
@@ -0,0 +1,4 @@
1
+ // Code generated by scripts/generate-emulator.mjs; DO NOT EDIT.
2
+
3
+ export declare const SUPPORTED_SCENARIO_VERSION: "you-agent-factory.emulator.scenario.v1";
4
+ export declare const scenarioSchema: Readonly<Record<string, unknown>>;
@@ -0,0 +1,6 @@
1
+ // Code generated by scripts/generate-emulator.mjs; DO NOT EDIT.
2
+
3
+ import schema from "../../generated/factory-emulator-scenario.schema.json" with { type: "json" };
4
+
5
+ export const SUPPORTED_SCENARIO_VERSION = "you-agent-factory.emulator.scenario.v1";
6
+ export const scenarioSchema = schema;
@@ -0,0 +1,103 @@
1
+ // Code generated by scripts/generate-emulator.mjs; DO NOT EDIT.
2
+
3
+ export type EmulatorScenarioVersion = "you-agent-factory.emulator.scenario.v1";
4
+
5
+ export interface EmulatorInitialSubmission {
6
+ /** Minimum length: 1. Maximum length: 128. */
7
+ id: string;
8
+ /** Minimum length: 1. Maximum length: 128. */
9
+ workType: string;
10
+ input?: Record<string, unknown>;
11
+ }
12
+
13
+ export interface EmulatorRule {
14
+ /** Minimum length: 1. Maximum length: 128. */
15
+ id: string;
16
+ match: EmulatorMatcher;
17
+ /** Minimum items: 1. */
18
+ outcomes: readonly EmulatorOutcome[];
19
+ exhaustionBehavior: EmulatorExhaustionBehavior;
20
+ }
21
+
22
+ export type EmulatorMatcher =
23
+ | {
24
+ kind: "all";
25
+ }
26
+ | {
27
+ kind: "workType";
28
+ /** Minimum length: 1. Maximum length: 128. */
29
+ workType: string;
30
+ }
31
+ | {
32
+ kind: "submissionId";
33
+ /** Minimum length: 1. Maximum length: 128. */
34
+ submissionId: string;
35
+ };
36
+
37
+ export type EmulatorOutcome =
38
+ | {
39
+ kind: "complete";
40
+ /** Deterministic virtual execution duration in milliseconds. Defaults to zero. Minimum: 0. */
41
+ durationMs?: number;
42
+ output?: Record<string, unknown>;
43
+ lineageCursor?: EmulatorLineageCursor;
44
+ }
45
+ | {
46
+ kind: "reject";
47
+ /** Deterministic virtual execution duration in milliseconds. Defaults to zero. Minimum: 0. */
48
+ durationMs?: number;
49
+ /** Minimum length: 1. Maximum length: 512. */
50
+ reason: string;
51
+ };
52
+
53
+ export type EmulatorLineageCursor =
54
+ | {
55
+ kind: "initialSubmission";
56
+ /** Minimum length: 1. Maximum length: 128. */
57
+ submissionId: string;
58
+ }
59
+ | {
60
+ kind: "scriptedOutcome";
61
+ /** Minimum length: 1. Maximum length: 128. */
62
+ ruleId: string;
63
+ /** Minimum: 0. */
64
+ outcomeIndex: number;
65
+ };
66
+
67
+ export type EmulatorExhaustionBehavior =
68
+ | {
69
+ kind: "repeatLast";
70
+ }
71
+ | {
72
+ kind: "useUnmatchedBehavior";
73
+ }
74
+ | {
75
+ kind: "reject";
76
+ /** Minimum length: 1. Maximum length: 512. */
77
+ reason: string;
78
+ };
79
+
80
+ export type EmulatorUnmatchedBehavior =
81
+ | {
82
+ kind: "ignore";
83
+ }
84
+ | {
85
+ kind: "reject";
86
+ /** Minimum length: 1. Maximum length: 512. */
87
+ reason: string;
88
+ };
89
+
90
+ export interface EmulatorScenario {
91
+ version: EmulatorScenarioVersion;
92
+ /** Minimum length: 1. Maximum length: 128. */
93
+ id: string;
94
+ /** Minimum length: 1. Maximum length: 256. */
95
+ seed: string;
96
+ /** Format: date-time. Pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(?:\.[0-9]+)?Z$. */
97
+ startAt: string;
98
+ initialSubmissions?: readonly EmulatorInitialSubmission[];
99
+ rules: readonly EmulatorRule[];
100
+ unmatchedBehavior: EmulatorUnmatchedBehavior;
101
+ /** Bounded transient emulator activity metadata. It is never a canonical Factory event field. Minimum length: 1. Maximum length: 120. */
102
+ activityLabel?: string;
103
+ }
@@ -0,0 +1,55 @@
1
+ const IDENTITY_KINDS = new Set([
2
+ "completion",
3
+ "dispatch",
4
+ "event",
5
+ "request",
6
+ "session",
7
+ "token",
8
+ "trace",
9
+ "work",
10
+ ]);
11
+
12
+ /** Derives one domain-separated identity from stable semantic coordinates. */
13
+ export function deriveFactoryEmulatorIdentity(kind, coordinates) {
14
+ if (!IDENTITY_KINDS.has(kind)) {
15
+ throw new TypeError(`unsupported Factory emulator identity kind: ${kind}`);
16
+ }
17
+ const source = `${kind}\u0000${canonicalStringify(coordinates)}`;
18
+ return `emulator-${kind}-${hash64(source, 0xcbf29ce484222325n)}${hash64(
19
+ source,
20
+ 0x84222325cbf29cen,
21
+ )}`;
22
+ }
23
+
24
+ /** Produces a key-order-independent representation for deterministic inputs. */
25
+ export function canonicalStringify(value) {
26
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
27
+ return JSON.stringify(value);
28
+ }
29
+ if (typeof value === "number") {
30
+ if (!Number.isFinite(value)) {
31
+ throw new TypeError("deterministic identity inputs must contain finite numbers");
32
+ }
33
+ return JSON.stringify(value);
34
+ }
35
+ if (Array.isArray(value)) {
36
+ return `[${value.map((entry) => canonicalStringify(entry ?? null)).join(",")}]`;
37
+ }
38
+ if (value && typeof value === "object") {
39
+ const entries = Object.keys(value)
40
+ .filter((key) => value[key] !== undefined)
41
+ .sort()
42
+ .map((key) => `${JSON.stringify(key)}:${canonicalStringify(value[key])}`);
43
+ return `{${entries.join(",")}}`;
44
+ }
45
+ throw new TypeError("deterministic identity inputs must contain data values only");
46
+ }
47
+
48
+ function hash64(value, offset) {
49
+ let hash = offset;
50
+ for (let index = 0; index < value.length; index += 1) {
51
+ hash ^= BigInt(value.charCodeAt(index));
52
+ hash = BigInt.asUintN(64, hash * 0x100000001b3n);
53
+ }
54
+ return hash.toString(16).padStart(16, "0");
55
+ }
package/src/index.js ADDED
@@ -0,0 +1,34 @@
1
+ export {
2
+ FactoryEventSinkCapacityError,
3
+ FactoryEventSinkClosedError,
4
+ createFactoryRecordingSink,
5
+ createMemoryFactoryEventSink,
6
+ } from "./sinks.js";
7
+ export {
8
+ FactoryEmulatorAdvanceInProgressError,
9
+ FactoryEmulatorClosedError,
10
+ FactoryEmulatorPendingTransactionError,
11
+ createFactoryEmulator,
12
+ } from "./emulator.js";
13
+ export {
14
+ FactoryEmulatorConfigurationError,
15
+ FactoryEmulatorDurationError,
16
+ FactoryEmulatorExecutionPausedError,
17
+ FactoryEmulatorLifecycleError,
18
+ FactoryEmulatorPendingCommandError,
19
+ FactoryEmulatorSubmissionError,
20
+ DEFAULT_FACTORY_EMULATOR_LIMITS,
21
+ FACTORY_EMULATOR_LIMIT_HARD_CAPS,
22
+ createFactoryEmulatorSession,
23
+ } from "./session.js";
24
+ export {
25
+ scenarioSchema,
26
+ SUPPORTED_SCENARIO_VERSION,
27
+ } from "./generated/scenario-schema.js";
28
+ export { parseEmulatorScenario } from "./parser.js";
29
+ export { emulatorScenarioExamples } from "./examples.js";
30
+ export { inspectEmulatorSupport } from "./support.js";
31
+ export {
32
+ resolveEmulatorScenarioResult,
33
+ selectEmulatorRule,
34
+ } from "./semantics.js";
package/src/index.ts ADDED
@@ -0,0 +1,102 @@
1
+ export type {
2
+ FactoryEventBatch,
3
+ FactoryEventSink,
4
+ FactoryEventSinkCloseReceipt,
5
+ FactoryEventSinkWriteReceipt,
6
+ } from "./contracts.js";
7
+ export {
8
+ FactoryEventSinkCapacityError,
9
+ FactoryEventSinkClosedError,
10
+ createFactoryRecordingSink,
11
+ createMemoryFactoryEventSink,
12
+ } from "./sinks.js";
13
+ export type {
14
+ FactoryRecordingSink,
15
+ FactoryRecordingSinkOptions,
16
+ MemoryFactoryEventSink,
17
+ MemoryFactoryEventSinkOptions,
18
+ } from "./sinks.js";
19
+ export {
20
+ FactoryEmulatorAdvanceInProgressError,
21
+ FactoryEmulatorClosedError,
22
+ FactoryEmulatorPendingTransactionError,
23
+ createFactoryEmulator,
24
+ } from "./emulator.js";
25
+ export {
26
+ FactoryEmulatorConfigurationError,
27
+ FactoryEmulatorDurationError,
28
+ FactoryEmulatorExecutionPausedError,
29
+ FactoryEmulatorLifecycleError,
30
+ FactoryEmulatorPendingCommandError,
31
+ FactoryEmulatorSubmissionError,
32
+ DEFAULT_FACTORY_EMULATOR_LIMITS,
33
+ FACTORY_EMULATOR_LIMIT_HARD_CAPS,
34
+ createFactoryEmulatorSession,
35
+ } from "./session.js";
36
+ export type {
37
+ FactoryEmulatorCommandError,
38
+ FactoryEmulatorSessionAdvanceReceipt,
39
+ FactoryEmulatorBudgetUsage,
40
+ FactoryEmulatorSessionCloseReceipt,
41
+ FactoryEmulatorResetReceipt,
42
+ FactoryEmulatorSession,
43
+ FactoryEmulatorSessionCounters,
44
+ FactoryEmulatorConfigurationDiagnostic,
45
+ FactoryEmulatorDataError,
46
+ FactoryEmulatorExecutionDiagnostic,
47
+ FactoryEmulatorLimits,
48
+ FactoryEmulatorSessionOptions,
49
+ FactoryEmulatorPendingTransactionStatus,
50
+ FactoryEmulatorSessionError,
51
+ FactoryEmulatorSessionState,
52
+ FactoryEmulatorSessionStatus,
53
+ FactoryEmulatorSessionWork,
54
+ FactoryEmulatorStartReceipt,
55
+ FactoryEmulatorSubmitReceipt,
56
+ } from "./session.js";
57
+ export type {
58
+ FactoryEmulator,
59
+ FactoryEmulatorAdvanceReceipt,
60
+ FactoryEmulatorCloseCalculator,
61
+ FactoryEmulatorCloseReceipt,
62
+ FactoryEmulatorOptions,
63
+ FactoryEmulatorStatus,
64
+ FactoryEmulatorTick,
65
+ FactoryEmulatorTickCalculator,
66
+ } from "./emulator.js";
67
+ export type {
68
+ EmulatorExhaustionBehavior,
69
+ EmulatorInitialSubmission,
70
+ EmulatorLineageCursor,
71
+ EmulatorMatcher,
72
+ EmulatorOutcome,
73
+ EmulatorRule,
74
+ EmulatorScenario,
75
+ EmulatorScenarioVersion,
76
+ EmulatorUnmatchedBehavior,
77
+ } from "./generated/scenario.js";
78
+ export {
79
+ scenarioSchema,
80
+ SUPPORTED_SCENARIO_VERSION,
81
+ } from "./generated/scenario-schema.js";
82
+ export {
83
+ parseEmulatorScenario,
84
+ type EmulatorFactoryDefinition,
85
+ type EmulatorScenarioDiagnostic,
86
+ type EmulatorScenarioDiagnosticCode,
87
+ type EmulatorScenarioParseResult,
88
+ } from "./parser.js";
89
+ export {
90
+ emulatorScenarioExamples,
91
+ type EmulatorScenarioExample,
92
+ } from "./examples.js";
93
+ export {
94
+ inspectEmulatorSupport,
95
+ type EmulatorSupportInspection,
96
+ } from "./support.js";
97
+ export {
98
+ resolveEmulatorScenarioResult,
99
+ selectEmulatorRule,
100
+ type EmulatorScenarioResolution,
101
+ type EmulatorSubmission,
102
+ } from "./semantics.js";
package/src/limits.js ADDED
@@ -0,0 +1,125 @@
1
+ export const DEFAULT_FACTORY_EMULATOR_LIMITS = Object.freeze({
2
+ maxCompletedDispatches: 1_000,
3
+ maxEvents: 10_000,
4
+ maxVirtualElapsedMs: 60 * 60 * 1_000,
5
+ maxZeroDurationBatches: 1_000,
6
+ maxSynchronousBatches: 100,
7
+ maxSynchronousWorkItems: 100,
8
+ });
9
+
10
+ export const FACTORY_EMULATOR_LIMIT_HARD_CAPS = Object.freeze({
11
+ maxCompletedDispatches: 100_000,
12
+ maxEvents: 1_000_000,
13
+ maxVirtualElapsedMs: 365 * 24 * 60 * 60 * 1_000,
14
+ maxZeroDurationBatches: 100_000,
15
+ maxSynchronousBatches: 10_000,
16
+ maxSynchronousWorkItems: 10_000,
17
+ });
18
+
19
+ const limitNames = Object.freeze(Object.keys(DEFAULT_FACTORY_EMULATOR_LIMITS));
20
+ const minimumLimitValues = Object.freeze({
21
+ maxCompletedDispatches: 1,
22
+ // start always publishes INITIAL_STRUCTURE_REQUEST and RUN_REQUEST atomically.
23
+ maxEvents: 2,
24
+ maxVirtualElapsedMs: 1,
25
+ maxZeroDurationBatches: 1,
26
+ maxSynchronousBatches: 1,
27
+ maxSynchronousWorkItems: 1,
28
+ });
29
+
30
+ /** Normalizes caller policy without reading environment or process-global state. */
31
+ export function normalizeFactoryEmulatorLimits(limits) {
32
+ if (limits === undefined) {
33
+ return { success: true, limits: { ...DEFAULT_FACTORY_EMULATOR_LIMITS } };
34
+ }
35
+ if (limits === null || typeof limits !== "object" || Array.isArray(limits)) {
36
+ return invalid("/limits", "must be an object containing positive integer limits");
37
+ }
38
+ const unknown = Object.keys(limits).find((name) => !limitNames.includes(name));
39
+ if (unknown !== undefined) {
40
+ return invalid(`/limits/${unknown}`, "is not a supported emulator limit");
41
+ }
42
+
43
+ const normalized = { ...DEFAULT_FACTORY_EMULATOR_LIMITS };
44
+ for (const name of limitNames) {
45
+ const value = limits[name];
46
+ if (value === undefined) {
47
+ continue;
48
+ }
49
+ if (!Number.isSafeInteger(value) || value < minimumLimitValues[name]) {
50
+ return invalid(
51
+ `/limits/${name}`,
52
+ `must be a safe integer no less than ${minimumLimitValues[name]}`,
53
+ );
54
+ }
55
+ if (value > FACTORY_EMULATOR_LIMIT_HARD_CAPS[name]) {
56
+ return invalid(
57
+ `/limits/${name}`,
58
+ `must not exceed the library hard cap ${FACTORY_EMULATOR_LIMIT_HARD_CAPS[name]}`,
59
+ );
60
+ }
61
+ normalized[name] = value;
62
+ }
63
+ return { success: true, limits: normalized };
64
+ }
65
+
66
+ export function budgetExceededDiagnostic({
67
+ configured,
68
+ limit,
69
+ observed,
70
+ virtualTime,
71
+ virtualElapsedMs,
72
+ }) {
73
+ return {
74
+ kind: "budget-exceeded",
75
+ limit,
76
+ configured,
77
+ observed,
78
+ virtualTime,
79
+ virtualElapsedMs,
80
+ };
81
+ }
82
+
83
+ export function zeroDurationCycleDiagnostic({
84
+ configured,
85
+ observed,
86
+ virtualTime,
87
+ virtualElapsedMs,
88
+ }) {
89
+ return {
90
+ kind: "zero-duration-cycle",
91
+ limit: "zeroDurationBatches",
92
+ configured,
93
+ observed,
94
+ virtualTime,
95
+ virtualElapsedMs,
96
+ };
97
+ }
98
+
99
+ export function synchronousWorkLimitDiagnostic({
100
+ configured,
101
+ observed,
102
+ virtualTime,
103
+ virtualElapsedMs,
104
+ }) {
105
+ return {
106
+ kind: "synchronous-work-limit",
107
+ limit: "schedulerWorkItems",
108
+ configured,
109
+ observed,
110
+ virtualTime,
111
+ virtualElapsedMs,
112
+ };
113
+ }
114
+
115
+ function invalid(path, message) {
116
+ return {
117
+ success: false,
118
+ diagnostics: [{
119
+ code: "INVALID_LIMIT_CONFIGURATION",
120
+ path,
121
+ message,
122
+ expectation: "a supported safe integer within its documented minimum and hard cap",
123
+ }],
124
+ };
125
+ }
package/src/parser.js ADDED
@@ -0,0 +1,113 @@
1
+ import Ajv2020 from "ajv/dist/2020.js";
2
+ import addFormats from "ajv-formats";
3
+ import { scenarioSchema } from "./generated/scenario-schema.js";
4
+ import { factorySchema } from "./generated/factory-schema.js";
5
+ import { scenarioSemanticDiagnostics } from "./semantics.js";
6
+ import { factorySupportDiagnostics } from "./support.js";
7
+ import { dataOnlyDiagnostics } from "./data-only.js";
8
+
9
+ const ajv = new Ajv2020({
10
+ allErrors: true,
11
+ coerceTypes: false,
12
+ removeAdditional: false,
13
+ strict: false,
14
+ useDefaults: false,
15
+ });
16
+ addFormats(ajv);
17
+ ajv.addFormat("int32", true);
18
+ ajv.addFormat("int64", true);
19
+ const validateScenarioShape = ajv.compile(scenarioSchema);
20
+ const validateFactoryShape = ajv.compile(factorySchema);
21
+
22
+ /**
23
+ * Parses only data. It neither creates Factory events nor starts emulator
24
+ * activity, so callers can reject unsupported input before any runtime work.
25
+ */
26
+ export function parseEmulatorScenario(scenario, factory) {
27
+ const scenarioDataDiagnostics = dataOnlyDiagnostics(scenario, {
28
+ code: "INVALID_SCENARIO_SHAPE",
29
+ });
30
+ if (scenarioDataDiagnostics.length > 0) {
31
+ return failure(scenarioDataDiagnostics);
32
+ }
33
+ if (!validateScenarioShape(scenario)) {
34
+ return failure(shapeDiagnostics(validateScenarioShape.errors ?? []));
35
+ }
36
+
37
+ const factoryDiagnostics = dataOnlyDiagnostics(factory, {
38
+ code: "INVALID_FACTORY_DEFINITION",
39
+ });
40
+ if (factoryDiagnostics.length === 0 && !validateFactoryShape(factory)) {
41
+ factoryDiagnostics.push(
42
+ ...shapeDiagnostics(
43
+ validateFactoryShape.errors ?? [],
44
+ "INVALID_FACTORY_DEFINITION",
45
+ ),
46
+ );
47
+ }
48
+ if (factoryDiagnostics.length === 0) {
49
+ factoryDiagnostics.push(...factorySupportDiagnostics(factory));
50
+ }
51
+ const diagnostics =
52
+ factoryDiagnostics.length === 0
53
+ ? scenarioSemanticDiagnostics(scenario, factory)
54
+ : factoryDiagnostics;
55
+ return diagnostics.length === 0
56
+ ? { success: true, scenario, factory }
57
+ : failure(diagnostics);
58
+ }
59
+
60
+ function failure(diagnostics) {
61
+ return { success: false, diagnostics: Object.freeze(diagnostics) };
62
+ }
63
+
64
+ function shapeDiagnostics(errors, defaultCode = "INVALID_SCENARIO_SHAPE") {
65
+ return errors
66
+ .map((error) => {
67
+ const path = errorPath(error);
68
+ return {
69
+ code:
70
+ error.keyword === "const" && path === "/version"
71
+ ? "UNSUPPORTED_SCENARIO_VERSION"
72
+ : defaultCode,
73
+ path,
74
+ message: `${path} ${error.message ?? "does not match the Emulator Scenario schema"}.`,
75
+ expectation: shapeExpectation(error),
76
+ };
77
+ })
78
+ .sort(compareDiagnostics);
79
+ }
80
+
81
+ function errorPath(error) {
82
+ const property =
83
+ error.keyword === "required"
84
+ ? error.params.missingProperty
85
+ : error.keyword === "additionalProperties"
86
+ ? error.params.additionalProperty
87
+ : undefined;
88
+ return typeof property === "string"
89
+ ? `${error.instancePath}/${escapeJsonPointer(property)}`
90
+ : error.instancePath || "/";
91
+ }
92
+
93
+ function shapeExpectation(error) {
94
+ if (error.keyword === "required") {
95
+ return `required property ${JSON.stringify(error.params.missingProperty)}`;
96
+ }
97
+ if (error.keyword === "additionalProperties") {
98
+ return `no additional property ${JSON.stringify(error.params.additionalProperty)}`;
99
+ }
100
+ return error.message ?? error.keyword;
101
+ }
102
+
103
+ function compareDiagnostics(left, right) {
104
+ return (
105
+ left.path.localeCompare(right.path) ||
106
+ left.code.localeCompare(right.code) ||
107
+ left.expectation.localeCompare(right.expectation)
108
+ );
109
+ }
110
+
111
+ function escapeJsonPointer(value) {
112
+ return value.replaceAll("~", "~0").replaceAll("/", "~1");
113
+ }
package/src/parser.ts ADDED
@@ -0,0 +1,56 @@
1
+ import type { EmulatorScenario } from "./generated/scenario.js";
2
+
3
+ export type EmulatorScenarioDiagnosticCode =
4
+ | "INVALID_SCENARIO_SHAPE"
5
+ | "UNSUPPORTED_SCENARIO_VERSION"
6
+ | "INVALID_FACTORY_DEFINITION"
7
+ | "UNSUPPORTED_FACTORY_CAPABILITY"
8
+ | "DUPLICATE_SCENARIO_IDENTIFIER"
9
+ | "UNKNOWN_FACTORY_WORK_TYPE"
10
+ | "UNKNOWN_INITIAL_SUBMISSION"
11
+ | "MISSING_LINEAGE_CURSOR_TARGET"
12
+ | "FORWARD_LINEAGE_CURSOR"
13
+ | "CYCLIC_LINEAGE_CURSOR"
14
+ | "INCOMPATIBLE_LINEAGE_CURSOR"
15
+ | "SHADOWED_RULE";
16
+
17
+ export interface EmulatorScenarioDiagnostic {
18
+ code: EmulatorScenarioDiagnosticCode;
19
+ path: string;
20
+ message: string;
21
+ expectation: string;
22
+ }
23
+
24
+ /** The executable Factory subset accepted by the v1 browser emulator. */
25
+ export interface EmulatorFactoryDefinition {
26
+ [property: string]: unknown;
27
+ orchestrator?: { kind?: string; [property: string]: unknown };
28
+ resources?: unknown[];
29
+ guards?: unknown[];
30
+ workTypes?: Array<{ name?: string; [property: string]: unknown }>;
31
+ workstations?: Array<{
32
+ [property: string]: unknown;
33
+ behavior?: string;
34
+ cron?: unknown;
35
+ resources?: unknown[];
36
+ guards?: unknown[];
37
+ inputs?: Array<{ guards?: unknown[]; [property: string]: unknown }>;
38
+ }>;
39
+ }
40
+
41
+ export type EmulatorScenarioParseResult =
42
+ | {
43
+ success: true;
44
+ scenario: EmulatorScenario;
45
+ factory: EmulatorFactoryDefinition;
46
+ }
47
+ | { success: false; diagnostics: readonly EmulatorScenarioDiagnostic[] };
48
+
49
+ /**
50
+ * Validates scenario structure and Factory execution support without creating
51
+ * Factory events or starting emulator activity.
52
+ */
53
+ export declare function parseEmulatorScenario(
54
+ scenario: unknown,
55
+ factory: unknown,
56
+ ): EmulatorScenarioParseResult;