ark-runtime-kernel 1.0.0 → 1.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/bin/ark-check.mjs CHANGED
@@ -22,14 +22,24 @@ function parseArgs(argv) {
22
22
  json: false,
23
23
  strictConfig: false,
24
24
  init: false,
25
+ installAgentGates: false,
25
26
  force: false,
27
+ baseline: undefined,
28
+ updateBaseline: false,
26
29
  };
27
30
  for (let i = 2; i < argv.length; i += 1) {
28
31
  const arg = argv[i];
29
32
  if (arg === '--json') args.json = true;
30
33
  else if (arg === '--strict-config') args.strictConfig = true;
31
34
  else if (arg === '--init') args.init = true;
35
+ else if (arg === '--install-agent-gates') args.installAgentGates = true;
32
36
  else if (arg === '--force') args.force = true;
37
+ else if (arg === '--baseline' || arg === '--update-baseline') {
38
+ if (arg === '--update-baseline') args.updateBaseline = true;
39
+ // optional path value: consume the next arg only when it isn't another flag
40
+ const next = argv[i + 1];
41
+ args.baseline = next && !next.startsWith('-') ? argv[++i] : '.ark-baseline.json';
42
+ }
33
43
  else if (arg === '--root') args.root = path.resolve(argv[++i]);
34
44
  else if (arg === '--config') args.config = argv[++i];
35
45
  else if (arg === '--manifest') args.manifest = argv[++i];
@@ -42,10 +52,16 @@ function parseArgs(argv) {
42
52
 
43
53
  function usage() {
44
54
  return [
45
- 'Usage: ark-check --root <project> --config <ark.config.json> [--manifest <ark.manifest.json>] [--tsconfig <tsconfig.json>] [--strict-config] [--json]',
55
+ 'Usage: ark-check --root <project> --config <ark.config.json> [--manifest <ark.manifest.json>] [--tsconfig <tsconfig.json>] [--strict-config] [--json] [--baseline [file]]',
46
56
  ' ark-check --init [--force]',
57
+ ' ark-check --install-agent-gates [--force]',
58
+ ' ark-check --update-baseline [file] freeze current violations (default .ark-baseline.json)',
47
59
  ' ark-check --print-config eleven-layer',
48
60
  '',
61
+ 'Adopting Ark in an existing codebase? Run --update-baseline once to freeze existing',
62
+ 'violations, commit the baseline file, and gate CI with --baseline: only NEW violations',
63
+ 'fail the check, so the ratchet only moves toward zero.',
64
+ '',
49
65
  '--init scans the project for the built-in layer directory conventions (src/domain,',
50
66
  'src/application, src/adapters/persistence, ...) and writes an ark.config.json covering',
51
67
  'only the layers that actually exist, with the default rules filtered to those layers.',
@@ -69,6 +85,9 @@ function usage() {
69
85
  '',
70
86
  'Generate a starter 11-layer config:',
71
87
  ' ark-check --print-config eleven-layer > ark.config.json',
88
+ '',
89
+ 'Install agent + CI enforcement templates:',
90
+ ' ark-check --install-agent-gates',
72
91
  ].join('\n');
73
92
  }
74
93
 
@@ -192,6 +211,170 @@ function runInit(args) {
192
211
  console.log(' (bind its validate_code tool to your agent\'s pre-write hook — see README)');
193
212
  }
194
213
 
214
+ function ensureDirForFile(file) {
215
+ fs.mkdirSync(path.dirname(file), { recursive: true });
216
+ }
217
+
218
+ function writeTemplate(root, relativePath, content, force) {
219
+ const fullPath = path.join(root, relativePath);
220
+ if (fs.existsSync(fullPath) && !force) {
221
+ return { relativePath, status: 'skipped' };
222
+ }
223
+ ensureDirForFile(fullPath);
224
+ fs.writeFileSync(fullPath, content);
225
+ return { relativePath, status: fs.existsSync(fullPath) ? 'written' : 'written' };
226
+ }
227
+
228
+ function packageManager(root) {
229
+ if (fs.existsSync(path.join(root, 'pnpm-lock.yaml'))) {
230
+ return {
231
+ cache: 'pnpm',
232
+ setup: ['corepack enable'],
233
+ install: 'pnpm install --frozen-lockfile',
234
+ run: 'pnpm exec ark-check --root . --config ark.config.json --strict-config',
235
+ };
236
+ }
237
+ if (fs.existsSync(path.join(root, 'yarn.lock'))) {
238
+ return {
239
+ cache: 'yarn',
240
+ setup: ['corepack enable'],
241
+ install: 'yarn install --frozen-lockfile',
242
+ run: 'yarn ark-check --root . --config ark.config.json --strict-config',
243
+ };
244
+ }
245
+ return {
246
+ cache: 'npm',
247
+ setup: [],
248
+ install: fs.existsSync(path.join(root, 'package-lock.json')) ? 'npm ci' : 'npm install',
249
+ run: 'npx ark-check --root . --config ark.config.json --strict-config',
250
+ };
251
+ }
252
+
253
+ function agentInstructions() {
254
+ return `# Ark Enforcement
255
+
256
+ Before editing TypeScript or JavaScript source files:
257
+
258
+ 1. Read the Ark contract from \`ark://manifest\` when the MCP server is available.
259
+ 2. Keep source files inside the layer boundaries declared in \`ark.config.json\`.
260
+ 3. Do not bypass Ark publishers, event contracts, or source metadata for runtime mutations.
261
+ 4. After edits, run \`npx ark-check --root . --config ark.config.json --strict-config\`.
262
+ 5. If Ark reports violations, fix the architecture instead of weakening the gate.
263
+
264
+ The project is only considered Ark-enforced when the write gate, CI gate, and runtime path all pass.
265
+ `;
266
+ }
267
+
268
+ function mcpJson() {
269
+ return `${JSON.stringify({
270
+ mcpServers: {
271
+ ark: {
272
+ type: 'stdio',
273
+ command: 'npx',
274
+ args: ['ark-mcp', '--root', '.', '--config', 'ark.config.json'],
275
+ },
276
+ },
277
+ }, null, 2)}\n`;
278
+ }
279
+
280
+ function codexTomlSnippet() {
281
+ return `[mcp_servers.ark]
282
+ command = "npx"
283
+ args = ["ark-mcp", "--root", ".", "--config", "ark.config.json"]
284
+ `;
285
+ }
286
+
287
+ function cursorRule() {
288
+ return `---
289
+ description: Ark architecture contract
290
+ alwaysApply: true
291
+ ---
292
+
293
+ Before writing or editing TypeScript or JavaScript source files, read the
294
+ \`ark://manifest\` resource from the \`ark\` MCP server when available.
295
+
296
+ Validate the full post-edit file content with the \`validate_code\` tool before
297
+ writing whenever your runtime supports it. After edits, run:
298
+
299
+ \`\`\`bash
300
+ npx ark-check --root . --config ark.config.json --strict-config
301
+ \`\`\`
302
+
303
+ If Ark reports violations, fix the architecture instead of bypassing the gate.
304
+ `;
305
+ }
306
+
307
+ function githubWorkflow(pm) {
308
+ const setupSteps = pm.setup.map((command) => ` - run: ${command}`).join('\n');
309
+ return `name: Ark architecture gate
310
+
311
+ on:
312
+ pull_request:
313
+ push:
314
+ branches: [main, master]
315
+
316
+ jobs:
317
+ ark-check:
318
+ runs-on: ubuntu-latest
319
+ steps:
320
+ - uses: actions/checkout@v4
321
+ - uses: actions/setup-node@v4
322
+ with:
323
+ node-version: 20
324
+ cache: ${pm.cache}
325
+ ${setupSteps ? `${setupSteps}\n` : ''} - run: ${pm.install}
326
+ - run: ${pm.run}
327
+ `;
328
+ }
329
+
330
+ function claudeSettings() {
331
+ return `${JSON.stringify({
332
+ hooks: {
333
+ PreToolUse: [
334
+ {
335
+ matcher: 'Write|Edit|MultiEdit',
336
+ hooks: [
337
+ {
338
+ type: 'command',
339
+ command:
340
+ 'npx ark-mcp --hook --root "$CLAUDE_PROJECT_DIR" --config ark.config.json',
341
+ },
342
+ ],
343
+ },
344
+ ],
345
+ },
346
+ }, null, 2)}\n`;
347
+ }
348
+
349
+ function runInstallAgentGates(args) {
350
+ const root = args.root;
351
+ const pm = packageManager(root);
352
+ const templates = [
353
+ ['AGENTS.md', agentInstructions()],
354
+ ['.mcp.json', mcpJson()],
355
+ ['.cursor/mcp.json', mcpJson()],
356
+ ['.cursor/rules/ark.mdc', cursorRule()],
357
+ ['.claude/settings.json', claudeSettings()],
358
+ ['.github/workflows/ark-check.yml', githubWorkflow(pm)],
359
+ ['docs/ark-codex-config.toml', codexTomlSnippet()],
360
+ ];
361
+
362
+ const results = templates.map(([relativePath, content]) =>
363
+ writeTemplate(root, relativePath, content, args.force)
364
+ );
365
+
366
+ console.log('Ark agent gate templates:');
367
+ for (const result of results) {
368
+ const marker = result.status === 'written' ? 'wrote' : 'skipped';
369
+ console.log(` ${marker.padEnd(7)} ${result.relativePath}`);
370
+ }
371
+ console.log('');
372
+ console.log('Next steps:');
373
+ console.log(' 1. Review the generated files and commit the ones that match your tools.');
374
+ console.log(' 2. Run: npx ark-check --root . --config ark.config.json --strict-config');
375
+ console.log(' 3. Wire Codex manually from docs/ark-codex-config.toml if your host uses ~/.codex/config.toml.');
376
+ }
377
+
195
378
  function readManifest(root, manifestPath) {
196
379
  if (!manifestPath) return undefined;
197
380
  const fullPath = path.isAbsolute(manifestPath)
@@ -564,6 +747,71 @@ function publishHasSource(ts, node) {
564
747
  );
565
748
  }
566
749
 
750
+ // ponytail: baseline keys exclude the line number so unrelated edits that shift lines
751
+ // don't resurrect frozen violations; the trade-off is that N identical violations in one
752
+ // file collapse to one key.
753
+ function baselineKey(violation) {
754
+ return [
755
+ violation.ruleId,
756
+ violation.file,
757
+ violation.fromLayer ?? '',
758
+ violation.toLayer ?? '',
759
+ violation.target ?? '',
760
+ ].join('|');
761
+ }
762
+
763
+ function readBaseline(root, baselinePath) {
764
+ const fullPath = path.isAbsolute(baselinePath) ? baselinePath : path.join(root, baselinePath);
765
+ if (!fs.existsSync(fullPath)) return { keys: new Set(), fullPath, exists: false };
766
+ const raw = JSON.parse(fs.readFileSync(fullPath, 'utf8'));
767
+ return { keys: new Set(raw.violations ?? []), fullPath, exists: true };
768
+ }
769
+
770
+ function writeBaseline(root, baselinePath, violations) {
771
+ const fullPath = path.isAbsolute(baselinePath) ? baselinePath : path.join(root, baselinePath);
772
+ const keys = [...new Set(violations.map(baselineKey))].sort();
773
+ fs.writeFileSync(
774
+ fullPath,
775
+ `${JSON.stringify({ version: 1, note: 'Frozen ark-check violations. Only NEW violations fail --baseline runs. Regenerate with: ark-check --update-baseline', violations: keys }, null, 2)}\n`
776
+ );
777
+ return { fullPath, count: keys.length };
778
+ }
779
+
780
+ const useColor = process.stderr.isTTY && !process.env.NO_COLOR;
781
+ const color = {
782
+ red: (s) => (useColor ? `\x1b[31m${s}\x1b[0m` : s),
783
+ yellow: (s) => (useColor ? `\x1b[33m${s}\x1b[0m` : s),
784
+ green: (s) => (useColor ? `\x1b[32m${s}\x1b[0m` : s),
785
+ dim: (s) => (useColor ? `\x1b[2m${s}\x1b[0m` : s),
786
+ bold: (s) => (useColor ? `\x1b[1m${s}\x1b[0m` : s),
787
+ };
788
+
789
+ const FIX_HINTS = {
790
+ LAYER_IMPORT_VIOLATION:
791
+ 'Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.',
792
+ LAYER_INTENT_REFERENCE_VIOLATION:
793
+ 'Reference intents through a layer that owns them (e.g. subscribe from an adapter, not from the domain).',
794
+ RAW_EVENT_PUBLISH:
795
+ 'Define the intent with ark.registry.define(...) and publish through the returned creator.',
796
+ PUBLISH_MISSING_SOURCE:
797
+ 'Add metadata.source (the publishing intent name) to the publish call.',
798
+ PUBLISH_SOURCE_LAYER_MISMATCH:
799
+ 'Use a source intent that belongs to the same layer as the publishing file, or move the file.',
800
+ };
801
+
802
+ function printViolation(violation) {
803
+ const location = `${violation.file}:${violation.line}`;
804
+ console.error(`${color.red('✖')} ${color.bold(violation.ruleId)} ${location}`);
805
+ if (violation.fromLayer && violation.toLayer) {
806
+ const target = violation.target ? ` ${color.dim(`(${violation.target})`)}` : '';
807
+ console.error(` ${violation.fromLayer} → ${violation.toLayer}${target}`);
808
+ }
809
+ console.error(` ${violation.message}`);
810
+ const hint = FIX_HINTS[violation.ruleId];
811
+ if (hint) console.error(` ${color.dim(`fix: ${hint}`)}`);
812
+ console.error('');
813
+ }
814
+
567
815
  function moduleSpecifierFromCall(ts, node) {
568
816
  if (!ts.isCallExpression(node)) return undefined;
569
817
 
@@ -592,6 +840,10 @@ async function main() {
592
840
  runInit(args);
593
841
  return;
594
842
  }
843
+ if (args.installAgentGates) {
844
+ runInstallAgentGates(args);
845
+ return;
846
+ }
595
847
  if (args.printConfig) {
596
848
  if (args.printConfig !== 'eleven-layer') {
597
849
  console.error(`Unknown config profile: ${args.printConfig}`);
@@ -733,38 +985,82 @@ async function main() {
733
985
  visit(sourceFile);
734
986
  }
735
987
 
988
+ if (args.updateBaseline) {
989
+ const { fullPath, count } = writeBaseline(root, args.baseline, violations);
990
+ console.log(`Wrote ${fullPath} with ${count} frozen violation key(s).`);
991
+ console.log('Commit it and gate CI with: ark-check --baseline (only NEW violations fail).');
992
+ return;
993
+ }
994
+
995
+ let suppressed = [];
996
+ let activeViolations = violations;
997
+ let staleBaselineKeys = 0;
998
+ if (args.baseline) {
999
+ const baseline = readBaseline(root, args.baseline);
1000
+ if (baseline.exists) {
1001
+ suppressed = violations.filter((violation) => baseline.keys.has(baselineKey(violation)));
1002
+ activeViolations = violations.filter(
1003
+ (violation) => !baseline.keys.has(baselineKey(violation))
1004
+ );
1005
+ const currentKeys = new Set(violations.map(baselineKey));
1006
+ staleBaselineKeys = [...baseline.keys].filter((key) => !currentKeys.has(key)).length;
1007
+ } else {
1008
+ warnings.push(
1009
+ configWarning(
1010
+ 'BASELINE_NOT_FOUND',
1011
+ `Baseline file not found: ${baseline.fullPath}. Generate it with: ark-check --update-baseline`
1012
+ )
1013
+ );
1014
+ }
1015
+ }
1016
+
1017
+ const ok = activeViolations.length === 0 && (!args.strictConfig || warnings.length === 0);
1018
+
736
1019
  if (args.json) {
737
1020
  console.log(JSON.stringify({
738
- ok: violations.length === 0 && (!args.strictConfig || warnings.length === 0),
739
- violations,
1021
+ ok,
1022
+ violations: activeViolations,
1023
+ suppressedViolations: suppressed.length,
1024
+ staleBaselineKeys,
740
1025
  warnings,
741
1026
  }, null, 2));
742
- } else if (violations.length === 0) {
1027
+ } else {
743
1028
  for (const warning of warnings) {
744
- console.error(`warning ${warning.ruleId} ${warning.message}`);
1029
+ console.error(`${color.yellow('warning')} ${warning.ruleId} ${warning.message}`);
745
1030
  }
746
- if (warnings.length === 0) {
747
- console.log('Ark check passed.');
748
- } else if (args.strictConfig) {
749
- console.error(`Ark check failed with ${warnings.length} config warning(s).`);
750
- } else {
751
- console.log(`Ark check passed with ${warnings.length} config warning(s).`);
1031
+ for (const violation of activeViolations) {
1032
+ printViolation(violation);
752
1033
  }
753
- } else {
754
- for (const warning of warnings) {
755
- console.error(`warning ${warning.ruleId} ${warning.message}`);
1034
+
1035
+ const baselineNote =
1036
+ suppressed.length > 0 ? ` (${suppressed.length} suppressed by baseline)` : '';
1037
+ if (staleBaselineKeys > 0) {
1038
+ console.error(
1039
+ color.dim(
1040
+ `${staleBaselineKeys} baseline entr(y/ies) no longer occur — tighten the ratchet with: ark-check --update-baseline`
1041
+ )
1042
+ );
756
1043
  }
757
- for (const violation of violations) {
1044
+ if (activeViolations.length === 0) {
1045
+ if (warnings.length === 0) {
1046
+ console.log(`${color.green('✔')} Ark check passed.${baselineNote}`);
1047
+ } else if (args.strictConfig) {
1048
+ console.error(
1049
+ `${color.red('✖')} Ark check failed with ${warnings.length} config warning(s).${baselineNote}`
1050
+ );
1051
+ } else {
1052
+ console.log(
1053
+ `${color.green('✔')} Ark check passed with ${warnings.length} config warning(s).${baselineNote}`
1054
+ );
1055
+ }
1056
+ } else {
758
1057
  console.error(
759
- `${violation.file}:${violation.line} ${violation.ruleId} ${violation.message}`
1058
+ `${color.red('✖')} ${activeViolations.length} violation(s).${baselineNote}`
760
1059
  );
761
1060
  }
762
1061
  }
763
1062
 
764
- process.exitCode =
765
- violations.length === 0 && (!args.strictConfig || warnings.length === 0)
766
- ? 0
767
- : 1;
1063
+ process.exitCode = ok ? 0 : 1;
768
1064
  }
769
1065
 
770
1066
  main().catch((error) => {
package/bin/ark-mcp.mjs CHANGED
File without changes
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ if (process.env.ARK_POSTINSTALL_SILENT === '1') {
4
+ process.exit(0);
5
+ }
6
+
7
+ console.log(`Ark installed, but not enforced yet.
8
+ Run: npx ark init
9
+
10
+ For non-interactive setup:
11
+ Run: npx ark init --yes
12
+ `);
package/bin/ark.mjs ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import readline from 'node:readline/promises';
7
+
8
+ const here = path.dirname(fileURLToPath(import.meta.url));
9
+ const arkCheck = path.join(here, 'ark-check.mjs');
10
+
11
+ function parseArgs(argv) {
12
+ const args = {
13
+ command: argv[2],
14
+ root: process.cwd(),
15
+ yes: false,
16
+ force: false,
17
+ strict: true,
18
+ help: false,
19
+ };
20
+
21
+ for (let i = 3; i < argv.length; i += 1) {
22
+ const arg = argv[i];
23
+ if (arg === '--root') args.root = path.resolve(argv[++i]);
24
+ else if (arg === '--yes' || arg === '-y') args.yes = true;
25
+ else if (arg === '--force') args.force = true;
26
+ else if (arg === '--no-strict') args.strict = false;
27
+ else if (arg === '--help' || arg === '-h') args.help = true;
28
+ }
29
+
30
+ return args;
31
+ }
32
+
33
+ function usage() {
34
+ return `Usage:
35
+ ark init [--root <project>] [--yes] [--force] [--no-strict]
36
+
37
+ Commands:
38
+ init Configure Ark project enforcement with explicit prompts.
39
+
40
+ Options:
41
+ --yes Non-interactive defaults: create config if needed, install gate templates, run strict check.
42
+ --force Allow generated files to overwrite existing files.
43
+ --no-strict Skip the final strict ark-check run.
44
+ `;
45
+ }
46
+
47
+ function runArkCheck(args, options = {}) {
48
+ const result = spawnSync(process.execPath, [arkCheck, ...args], {
49
+ cwd: options.cwd,
50
+ stdio: options.stdio ?? 'inherit',
51
+ encoding: 'utf8',
52
+ });
53
+ return result.status ?? 1;
54
+ }
55
+
56
+ async function askYesNo(rl, question, defaultYes = true) {
57
+ const suffix = defaultYes ? ' [Y/n] ' : ' [y/N] ';
58
+ const answer = (await rl.question(`${question}${suffix}`)).trim().toLowerCase();
59
+ if (!answer) return defaultYes;
60
+ return answer === 'y' || answer === 'yes';
61
+ }
62
+
63
+ async function init(args) {
64
+ const root = args.root;
65
+ const configPath = path.join(root, 'ark.config.json');
66
+ const rl = args.yes
67
+ ? null
68
+ : readline.createInterface({ input: process.stdin, output: process.stdout });
69
+
70
+ try {
71
+ let shouldInit = !fs.existsSync(configPath);
72
+ if (fs.existsSync(configPath)) {
73
+ shouldInit = args.force
74
+ ? true
75
+ : args.yes
76
+ ? false
77
+ : await askYesNo(rl, 'ark.config.json already exists. Regenerate it?', false);
78
+ }
79
+
80
+ if (shouldInit) {
81
+ const initArgs = ['--root', root, '--init'];
82
+ if (args.force) initArgs.push('--force');
83
+ const status = runArkCheck(initArgs, { cwd: root });
84
+ if (status !== 0) return status;
85
+ } else {
86
+ console.log('Skipped ark.config.json generation.');
87
+ }
88
+
89
+ const installGates = args.yes || await askYesNo(rl, 'Configure agent and CI gate templates?', true);
90
+ if (installGates) {
91
+ const gateArgs = ['--root', root, '--install-agent-gates'];
92
+ if (args.force) gateArgs.push('--force');
93
+ const status = runArkCheck(gateArgs, { cwd: root });
94
+ if (status !== 0) return status;
95
+ }
96
+
97
+ const runStrict =
98
+ args.strict && (args.yes || await askYesNo(rl, 'Run strict architecture check now?', true));
99
+ if (runStrict) {
100
+ return runArkCheck(
101
+ ['--root', root, '--config', 'ark.config.json', '--strict-config'],
102
+ { cwd: root }
103
+ );
104
+ }
105
+
106
+ console.log('Ark init complete. Run `npx ark-check --root . --config ark.config.json --strict-config` before merging.');
107
+ return 0;
108
+ } finally {
109
+ rl?.close();
110
+ }
111
+ }
112
+
113
+ async function main() {
114
+ const args = parseArgs(process.argv);
115
+ if (args.help || !args.command) {
116
+ console.log(usage());
117
+ return 0;
118
+ }
119
+
120
+ if (args.command === 'init') {
121
+ return init(args);
122
+ }
123
+
124
+ console.error(`Unknown command: ${args.command}`);
125
+ console.error(usage());
126
+ return 2;
127
+ }
128
+
129
+ process.exitCode = await main();
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // src/version.ts
4
- var version = "1.0.0";
4
+ var version = "1.2.0";
5
5
 
6
6
  // src/kernel/intent/IntentRegistry.ts
7
7
  var IntentRegistry = class {
@@ -1105,6 +1105,12 @@ function createEventBus(options) {
1105
1105
  }
1106
1106
 
1107
1107
  // src/kernel/event-contracts/EventContractRegistry.ts
1108
+ function formatStandardSchemaPath(path) {
1109
+ if (!path || path.length === 0) return void 0;
1110
+ return path.map(
1111
+ (segment) => typeof segment === "object" && segment !== null && "key" in segment ? String(segment.key) : String(segment)
1112
+ ).join(".");
1113
+ }
1108
1114
  function actualType(value) {
1109
1115
  if (Array.isArray(value)) return "array";
1110
1116
  if (value === null) return "object";
@@ -1249,6 +1255,25 @@ var EventContractRegistryImpl = class {
1249
1255
  }
1250
1256
  }
1251
1257
  }
1258
+ if (contract.standardSchema) {
1259
+ const result = contract.standardSchema["~standard"].validate(event.payload);
1260
+ if (result instanceof Promise) {
1261
+ issues.push({
1262
+ intent: event.intent,
1263
+ version: contract.version,
1264
+ message: "Standard Schema validator returned a Promise; event contract validation is synchronous."
1265
+ });
1266
+ } else if (result.issues) {
1267
+ for (const issue of result.issues) {
1268
+ issues.push({
1269
+ intent: event.intent,
1270
+ version: contract.version,
1271
+ field: formatStandardSchemaPath(issue.path),
1272
+ message: issue.message
1273
+ });
1274
+ }
1275
+ }
1276
+ }
1252
1277
  return { ok: issues.length === 0, contract, issues };
1253
1278
  }
1254
1279
  clear() {