@pulse-compute/wasm-schema-json 0.0.0 → 1.0.0-beta.2
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 +36 -1
- package/package.json +35 -6
- package/src/compiler/canonical-schema-codecs.js +351 -0
- package/src/compiler/index.js +23 -0
- package/src/compiler/schema-json-compile.js +575 -0
- package/src/compiler/schema-json-generic-parser.js +539 -0
- package/src/compiler/schema-json-sidecar-v2.js +1675 -0
- package/src/compiler/schema-json-sidecar.js +889 -0
- package/src/compiler/schema-registry.js +705 -0
- package/src/index.js +8 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const crypto = require('node:crypto');
|
|
7
|
+
const { spawnSync } = require('node:child_process');
|
|
8
|
+
const { resolveAsc } = loadBuildSupportAssemblyScriptCompile();
|
|
9
|
+
const { getDefaultArtifactsDir } = loadBuildSupportArtifactsDir();
|
|
10
|
+
const { PACKAGE_VERSION, normalizeArtifact, normalizeDiagnostic } = loadContractsDiagnostics();
|
|
11
|
+
const {
|
|
12
|
+
SCHEMA_JSON_GENERIC_PARSER_VERSION,
|
|
13
|
+
SCHEMA_JSON_GENERIC_PARSER_PLAN_VERSION,
|
|
14
|
+
SCHEMA_JSON_GENERIC_PARSER_ABI_VERSION,
|
|
15
|
+
SCHEMA_JSON_GENERIC_PARSER_SMOKE_VERSION,
|
|
16
|
+
SCHEMA_JSON_GENERIC_PARSER_PHASE,
|
|
17
|
+
SCHEMA_JSON_GENERIC_PARSER_ARTIFACT,
|
|
18
|
+
SCHEMA_JSON_GENERIC_PARSER_SMOKE_ARTIFACT,
|
|
19
|
+
SCHEMA_JSON_GENERIC_PARSER_ID,
|
|
20
|
+
SCHEMA_JSON_GENERIC_PARSER_NAME,
|
|
21
|
+
SCHEMA_JSON_GENERIC_PARSER_MODE,
|
|
22
|
+
SCHEMA_JSON_GENERIC_PARSER_FILE,
|
|
23
|
+
SCHEMA_JSON_GENERIC_PARSER_SMOKE_RUNNER_FILE,
|
|
24
|
+
SCHEMA_JSON_GENERIC_PARSER_WASM_FILE,
|
|
25
|
+
SCHEMA_JSON_GENERIC_PARSER_WAT_FILE,
|
|
26
|
+
SCHEMA_JSON_GENERIC_PARSER_DOC_FILE,
|
|
27
|
+
SCHEMA_JSON_GENERIC_VALUE_KINDS,
|
|
28
|
+
SCHEMA_JSON_GENERIC_PARSER_ACCESSORS,
|
|
29
|
+
SCHEMA_JSON_GENERIC_RESERVED_SURFACE,
|
|
30
|
+
SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS,
|
|
31
|
+
normalizeJsonParserConfig,
|
|
32
|
+
genericParserNeeded,
|
|
33
|
+
defaultSchemaJsonGenericParserPolicy
|
|
34
|
+
} = loadContractsGenericParser();
|
|
35
|
+
const {
|
|
36
|
+
SCHEMA_JSON_PLAN_VERSION,
|
|
37
|
+
SCHEMA_JSON_REPORT_VERSION,
|
|
38
|
+
SCHEMA_JSON_REGISTRY_VERSION,
|
|
39
|
+
SCHEMA_JSON_ABI_VERSION,
|
|
40
|
+
SCHEMA_JSON_BODY_POLICY_VERSION,
|
|
41
|
+
DEFAULT_SCHEMA_JSON_CONTENT_TYPE_POLICY,
|
|
42
|
+
DEFAULT_SCHEMA_JSON_MAX_BYTES,
|
|
43
|
+
PHASE: SCHEMA_JSON_PHASE
|
|
44
|
+
} = loadContractsSchemaJsonV1();
|
|
45
|
+
|
|
46
|
+
function loadContractsDiagnostics() {
|
|
47
|
+
try { return require('@pulse-compute/wasm-contracts/diagnostics'); }
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) return require('../../../contracts/src/diagnostics.js');
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function loadContractsGenericParser() {
|
|
54
|
+
try { return require('@pulse-compute/wasm-contracts/schema-json/generic-parser'); }
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) return require('../../../contracts/src/schema-json/generic-parser.js');
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function loadContractsSchemaJsonV1() {
|
|
61
|
+
try { return require('@pulse-compute/wasm-contracts/schema-json/v1'); }
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-contracts')) return require('../../../contracts/src/schema-json/v1.js');
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function loadBuildSupportArtifactsDir() {
|
|
68
|
+
try { return require('@pulse-compute/wasm-build-support/artifacts-dir'); }
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-build-support')) return require('../../../build-support/src/artifacts-dir.js');
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function loadBuildSupportAssemblyScriptCompile() {
|
|
75
|
+
try { return require('@pulse-compute/wasm-build-support/assemblyscript-compile'); }
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (error && error.code === 'MODULE_NOT_FOUND' && String(error.message).includes('@pulse-compute/wasm-build-support')) return require('../../../build-support/src/assemblyscript-compile.js');
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function sha256Text(text) { return crypto.createHash('sha256').update(String(text || ''), 'utf8').digest('hex'); }
|
|
83
|
+
function sha256Buffer(buffer) { return crypto.createHash('sha256').update(buffer).digest('hex'); }
|
|
84
|
+
function makeFile(file, text) { return { file, bytes: Buffer.byteLength(text, 'utf8'), sha256: sha256Text(text), text }; }
|
|
85
|
+
function stableSlash(value) { return String(value || '').replace(/\\/g, '/'); }
|
|
86
|
+
function stripAnsi(value) { return String(value || '').replace(/\u001b\[[0-9;]*m/g, ''); }
|
|
87
|
+
function truncate(value, max = 12000) { const text = String(value || ''); return text.length <= max ? text : `${text.slice(0, max)}\n... <truncated ${text.length - max} chars>`; }
|
|
88
|
+
function parseAscWarnings(stderr) {
|
|
89
|
+
const text = stripAnsi(stderr);
|
|
90
|
+
const warnings = [];
|
|
91
|
+
const regex = /WARNING\s+(AS\d+):\s+([^\n]+)(?:.|\n)*?in ([^\n]+)\((\d+),(\d+)\)/g;
|
|
92
|
+
let match;
|
|
93
|
+
while ((match = regex.exec(text))) warnings.push({ code: match[1], message: match[2].trim(), file: match[3], line: Number(match[4]), column: Number(match[5]) });
|
|
94
|
+
if (warnings.length === 0 && /WARNING\s+AS\d+:/m.test(text)) warnings.push({ code: 'AS_WARNING', message: 'AssemblyScript emitted one or more warnings. Inspect stderr.' });
|
|
95
|
+
return warnings;
|
|
96
|
+
}
|
|
97
|
+
function fileRecord(filePath, cwd, kind) {
|
|
98
|
+
const buffer = fs.readFileSync(filePath);
|
|
99
|
+
return { file: stableSlash(path.relative(cwd, filePath)), kind, bytes: buffer.length, sha256: sha256Buffer(buffer) };
|
|
100
|
+
}
|
|
101
|
+
function plainObject(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
|
|
102
|
+
function configArtifact(options = {}) { return options.resolvedConfig?.artifact || options.resolvedConfig || {}; }
|
|
103
|
+
function normalizeBuilderOptions(args) {
|
|
104
|
+
if (args.length <= 1) return args[0] || {};
|
|
105
|
+
return args[2] || {};
|
|
106
|
+
}
|
|
107
|
+
function makeDiagnostic(code, message, hint, details) {
|
|
108
|
+
return normalizeDiagnostic({ severity: 'error', phase: 'schema-json-generic-parser', pass: 'schema-json-generic-parser', code, message, hint, details, loc: { file: '<config>' } });
|
|
109
|
+
}
|
|
110
|
+
function makeWarning(code, message, hint, details) {
|
|
111
|
+
return normalizeDiagnostic({ severity: 'warning', phase: 'schema-json-generic-parser', pass: 'schema-json-generic-parser', code, message, hint, details, loc: { file: '<config>' } });
|
|
112
|
+
}
|
|
113
|
+
function jsonConfig(config) {
|
|
114
|
+
const artifact = config && config.artifact ? config.artifact : config;
|
|
115
|
+
const runtime = plainObject(artifact && artifact.runtime);
|
|
116
|
+
const payload = plainObject(runtime.payload);
|
|
117
|
+
return plainObject(payload.json || (artifact && artifact.json));
|
|
118
|
+
}
|
|
119
|
+
function validateSelection(selection, options) {
|
|
120
|
+
const diagnostics = [];
|
|
121
|
+
const warnings = [];
|
|
122
|
+
const json = jsonConfig(configArtifact(options));
|
|
123
|
+
const schemas = Array.isArray(json.schemas) ? json.schemas : [];
|
|
124
|
+
const parser = selection.parser;
|
|
125
|
+
for (const code of selection.diagnostics || []) {
|
|
126
|
+
if (code === SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.parserRequired) {
|
|
127
|
+
diagnostics.push(makeDiagnostic(code, 'Schema JSON generic parser fallback requires runtime.payload.json.parser = "as-json" when no schemas are supplied.', 'Use runtime.payload.json = { target: "auto", parser: "as-json" } or provide json.schemas for schema sidecars.', { target: selection.target, schemaCount: selection.schemaCount }));
|
|
128
|
+
} else if (code === SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.unsupportedParser) {
|
|
129
|
+
diagnostics.push(makeDiagnostic(code, `Unsupported schema JSON fallback parser ${JSON.stringify(parser)}.`, 'The initial generic fallback supports parser = "as-json" only.', { target: selection.target, parser }));
|
|
130
|
+
} else if (code === SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.schemaTargetRequiresSchemas) {
|
|
131
|
+
diagnostics.push(makeDiagnostic(code, 'json.target = "schema" requires at least one schema declaration.', 'Provide runtime.payload.json.schemas or use target = "auto" with parser = "as-json" for schema-less generic parsing.', { target: selection.target }));
|
|
132
|
+
} else {
|
|
133
|
+
diagnostics.push(makeDiagnostic(code, 'Schema JSON generic parser configuration is invalid.', 'Check runtime.payload.json.target, parser, and schemas.', { target: selection.target, parser }));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
for (const code of selection.warnings || []) {
|
|
137
|
+
if (code === SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.schemasTakePrecedence) {
|
|
138
|
+
warnings.push(makeWarning(code, 'json.target = "auto" prefers explicit schema sidecars when schemas are supplied.', 'Remove json.schemas or set target = "parser" to force the generic parser fallback.', { schemas: schemas.length }));
|
|
139
|
+
} else if (code === SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.schemasIgnored) {
|
|
140
|
+
warnings.push(makeWarning(code, 'json.target = "parser" uses the generic parser and ignores json.schemas.', 'Use target = "schema" or target = "auto" when explicit schemas should own parsing.', { schemas: schemas.length }));
|
|
141
|
+
} else {
|
|
142
|
+
warnings.push(makeWarning(code, 'Schema JSON generic parser selected with a warning.', 'Review runtime.payload.json configuration.', { target: selection.target, parser }));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (!selection.genericParser && options.forceGenericParser && diagnostics.length === 0) {
|
|
146
|
+
diagnostics.push(makeDiagnostic(SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.parserRequired, 'Forced schema JSON generic parser emission did not select the as-json parser.', 'Use runtime.payload.json = { target: "parser", parser: "as-json" } or { target: "auto", parser: "as-json" } with no schemas.', { target: selection.target, parser, schemaCount: selection.schemaCount }));
|
|
147
|
+
}
|
|
148
|
+
return { diagnostics, warnings };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function genericParserSource() {
|
|
152
|
+
return String.raw`// Generated by PulseWasm Pass 34.
|
|
153
|
+
const JSON_NULL: i32 = ${SCHEMA_JSON_GENERIC_VALUE_KINDS.null};
|
|
154
|
+
const JSON_BOOL: i32 = ${SCHEMA_JSON_GENERIC_VALUE_KINDS.bool};
|
|
155
|
+
const JSON_NUMBER: i32 = ${SCHEMA_JSON_GENERIC_VALUE_KINDS.number};
|
|
156
|
+
const JSON_STRING: i32 = ${SCHEMA_JSON_GENERIC_VALUE_KINDS.string};
|
|
157
|
+
const JSON_ARRAY: i32 = ${SCHEMA_JSON_GENERIC_VALUE_KINDS.array};
|
|
158
|
+
const JSON_OBJECT: i32 = ${SCHEMA_JSON_GENERIC_VALUE_KINDS.object};
|
|
159
|
+
|
|
160
|
+
export class PulseSchemaJsonError { code: string = ""; message: string = ""; statusCode: i32 = 400; }
|
|
161
|
+
export class JsonValue { kind: i32 = JSON_NULL; raw: string = "null"; }
|
|
162
|
+
export class JsonParseResult { ok: bool = false; valueRef: usize = 0; errorRef: usize = 0; }
|
|
163
|
+
|
|
164
|
+
function makeValue(kind: i32, raw: string): usize { const v = new JsonValue(); v.kind = kind; v.raw = raw; return changetype<usize>(v); }
|
|
165
|
+
function makeError(code: string, message: string, statusCode: i32 = 400): usize { const e = new PulseSchemaJsonError(); e.code = code; e.message = message; e.statusCode = statusCode; return changetype<usize>(e); }
|
|
166
|
+
function makeOk(valueRef: usize): usize { const r = new JsonParseResult(); r.ok = true; r.valueRef = valueRef; return changetype<usize>(r); }
|
|
167
|
+
function makeErr(errorRef: usize): usize { const r = new JsonParseResult(); r.ok = false; r.errorRef = errorRef; return changetype<usize>(r); }
|
|
168
|
+
function ws(ch: i32): bool { return ch == 32 || ch == 10 || ch == 13 || ch == 9; }
|
|
169
|
+
function skipWs(s: string, p: i32): i32 { let i = p; while (i < s.length && ws(s.charCodeAt(i))) i += 1; return i; }
|
|
170
|
+
function trimJson(s: string): string { const start = skipWs(s, 0); let end = s.length - 1; while (end >= start && ws(s.charCodeAt(end))) end -= 1; return start <= end ? s.substring(start, end + 1) : ""; }
|
|
171
|
+
function kindOfRaw(raw: string): i32 { if (raw == "null") return JSON_NULL; if (raw == "true" || raw == "false") return JSON_BOOL; if (raw.length > 0 && raw.charCodeAt(0) == 34) return JSON_STRING; if (raw.length > 0 && raw.charCodeAt(0) == 91) return JSON_ARRAY; if (raw.length > 0 && raw.charCodeAt(0) == 123) return JSON_OBJECT; return JSON_NUMBER; }
|
|
172
|
+
function findStringEnd(s: string, p: i32): i32 { let i = p + 1; while (i < s.length) { const ch = s.charCodeAt(i); if (ch == 92) { i += 2; continue; } if (ch == 34) return i + 1; i += 1; } return -1; }
|
|
173
|
+
function findNumberEnd(s: string, p: i32): i32 { let i = p; if (i < s.length && s.charCodeAt(i) == 45) i += 1; let seen = false; while (i < s.length) { const ch = s.charCodeAt(i); if (ch >= 48 && ch <= 57) { seen = true; i += 1; continue; } if (ch == 46) { i += 1; continue; } break; } return seen ? i : -1; }
|
|
174
|
+
function findValueEnd(s: string, p: i32): i32 { const start = skipWs(s, p); if (start >= s.length) return -1; const ch = s.charCodeAt(start); if (ch == 34) return findStringEnd(s, start); if (ch == 123 || ch == 91) { let depth = 0; let i = start; while (i < s.length) { const c = s.charCodeAt(i); if (c == 34) { const e = findStringEnd(s, i); if (e < 0) return -1; i = e; continue; } if (c == 123 || c == 91) depth += 1; if (c == 125 || c == 93) { depth -= 1; if (depth == 0) return i + 1; } i += 1; } return -1; } if (s.substr(start, 4) == "true") return start + 4; if (s.substr(start, 5) == "false") return start + 5; if (s.substr(start, 4) == "null") return start + 4; return findNumberEnd(s, start); }
|
|
175
|
+
function validRaw(raw: string): bool { if (raw.length == 0) return false; const end = findValueEnd(raw, 0); return end == raw.length; }
|
|
176
|
+
function unquote(raw: string): string { if (raw.length < 2 || raw.charCodeAt(0) != 34) return ""; return raw.substring(1, raw.length - 1); }
|
|
177
|
+
function rawOf(valueRef: usize): string { return valueRef == 0 ? "" : changetype<JsonValue>(valueRef).raw; }
|
|
178
|
+
function rawNum(valueRef: usize): string { return kind(valueRef) == JSON_NUMBER ? rawOf(valueRef) : "0"; }
|
|
179
|
+
|
|
180
|
+
export function parse(json: string): usize { const raw = trimJson(json); if (!validRaw(raw)) return makeErr(makeError("PULSEWASM_SCHEMA_JSON_PARSE_ERROR", "Invalid JSON input.", 400)); return makeOk(makeValue(kindOfRaw(raw), raw)); }
|
|
181
|
+
export function ok(resultRef: usize): bool { return resultRef != 0 && changetype<JsonParseResult>(resultRef).ok; }
|
|
182
|
+
export function value(resultRef: usize): usize { return resultRef == 0 ? 0 : changetype<JsonParseResult>(resultRef).valueRef; }
|
|
183
|
+
export function error(resultRef: usize): usize { return resultRef == 0 ? 0 : changetype<JsonParseResult>(resultRef).errorRef; }
|
|
184
|
+
export function errorCode(errorRef: usize): string { return errorRef == 0 ? "" : changetype<PulseSchemaJsonError>(errorRef).code; }
|
|
185
|
+
export function errorMessage(errorRef: usize): string { return errorRef == 0 ? "" : changetype<PulseSchemaJsonError>(errorRef).message; }
|
|
186
|
+
export function errorStatusCode(errorRef: usize): i32 { return errorRef == 0 ? 0 : changetype<PulseSchemaJsonError>(errorRef).statusCode; }
|
|
187
|
+
export function kind(valueRef: usize): i32 { return valueRef == 0 ? -1 : changetype<JsonValue>(valueRef).kind; }
|
|
188
|
+
export function isNull(valueRef: usize): bool { return kind(valueRef) == JSON_NULL; }
|
|
189
|
+
export function asBool(valueRef: usize): bool { return rawOf(valueRef) == "true"; }
|
|
190
|
+
export function asF64(valueRef: usize): f64 { return F64.parseFloat(rawNum(valueRef)); }
|
|
191
|
+
export function asString(valueRef: usize): string { return kind(valueRef) == JSON_STRING ? unquote(rawOf(valueRef)) : ""; }
|
|
192
|
+
export function arrayLen(valueRef: usize): i32 { if (kind(valueRef) != JSON_ARRAY) return 0; const raw = rawOf(valueRef); let i = skipWs(raw, 1); let count = 0; if (i < raw.length && raw.charCodeAt(i) == 93) return 0; while (i < raw.length - 1) { const end = findValueEnd(raw, i); if (end < 0) return count; count += 1; i = skipWs(raw, end); if (i < raw.length && raw.charCodeAt(i) == 44) i = skipWs(raw, i + 1); else break; } return count; }
|
|
193
|
+
export function arrayGet(valueRef: usize, index: i32): usize { if (kind(valueRef) != JSON_ARRAY || index < 0) return 0; const raw = rawOf(valueRef); let i = skipWs(raw, 1); let current = 0; while (i < raw.length - 1) { const end = findValueEnd(raw, i); if (end < 0) return 0; if (current == index) { const child = trimJson(raw.substring(i, end)); return makeValue(kindOfRaw(child), child); } current += 1; i = skipWs(raw, end); if (i < raw.length && raw.charCodeAt(i) == 44) i = skipWs(raw, i + 1); else break; } return 0; }
|
|
194
|
+
export function objectGet(valueRef: usize, key: string): usize { if (kind(valueRef) != JSON_OBJECT) return 0; const raw = rawOf(valueRef); let i = skipWs(raw, 1); while (i < raw.length - 1) { if (raw.charCodeAt(i) != 34) return 0; const keyEnd = findStringEnd(raw, i); if (keyEnd < 0) return 0; const foundKey = unquote(raw.substring(i, keyEnd)); i = skipWs(raw, keyEnd); if (i >= raw.length || raw.charCodeAt(i) != 58) return 0; i = skipWs(raw, i + 1); const valueEnd = findValueEnd(raw, i); if (valueEnd < 0) return 0; if (foundKey == key) { const child = trimJson(raw.substring(i, valueEnd)); return makeValue(kindOfRaw(child), child); } i = skipWs(raw, valueEnd); if (i < raw.length && raw.charCodeAt(i) == 44) i = skipWs(raw, i + 1); else break; } return 0; }
|
|
195
|
+
export function objectHas(valueRef: usize, key: string): bool { return objectGet(valueRef, key) != 0; }
|
|
196
|
+
|
|
197
|
+
// Stable Pass 34 accessor aliases.
|
|
198
|
+
export function has(valueRef: usize, key: string): bool { return objectHas(valueRef, key); }
|
|
199
|
+
export function get(valueRef: usize, key: string): usize { return objectGet(valueRef, key); }
|
|
200
|
+
export function getString(valueRef: usize, key: string): string { return asString(get(valueRef, key)); }
|
|
201
|
+
export function getI32(valueRef: usize, key: string): i32 { return I32.parseInt(rawNum(get(valueRef, key))); }
|
|
202
|
+
export function getU32(valueRef: usize, key: string): u32 { return U32.parseInt(rawNum(get(valueRef, key))); }
|
|
203
|
+
export function getF64(valueRef: usize, key: string): f64 { return asF64(get(valueRef, key)); }
|
|
204
|
+
export function getBool(valueRef: usize, key: string): bool { return asBool(get(valueRef, key)); }
|
|
205
|
+
export function length(valueRef: usize): i32 { return arrayLen(valueRef); }
|
|
206
|
+
export function at(valueRef: usize, index: i32): usize { return arrayGet(valueRef, index); }
|
|
207
|
+
export function text(valueRef: usize): string { return rawOf(valueRef); }
|
|
208
|
+
`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function genericParserSmokeRunnerSource() {
|
|
212
|
+
return String.raw`import { parse, ok, value, error, errorCode, kind, objectHas, objectGet, arrayLen, arrayGet, asString, asF64, asBool, isNull, getString, getI32, getBool, length, at, text } from "../as/schema-json/generic/as-json-parser.as";
|
|
213
|
+
function root(): usize { const r = parse('{"name":"Ada","age":37,"active":true,"tags":["wasm","json"],"profile":{"email":"ada@example.test"},"missing":null}'); return ok(r) ? value(r) : 0; }
|
|
214
|
+
export function smoke_generic_json_object(): i32 { const v = root(); return v != 0 && kind(v) == 5 && objectHas(v, "name") ? 1 : 0; }
|
|
215
|
+
export function smoke_generic_json_string(): i32 { return asString(objectGet(root(), "name")) == "Ada" && getString(root(), "name") == "Ada" ? 1 : 0; }
|
|
216
|
+
export function smoke_generic_json_number_bool_null(): i32 { const v = root(); return asF64(objectGet(v, "age")) == 37.0 && getI32(v, "age") == 37 && asBool(objectGet(v, "active")) && getBool(v, "active") && isNull(objectGet(v, "missing")) ? 1 : 0; }
|
|
217
|
+
export function smoke_generic_json_array_nested_object(): i32 { const v = root(); const tags = objectGet(v, "tags"); const p = objectGet(v, "profile"); return arrayLen(tags) == 2 && length(tags) == 2 && asString(arrayGet(tags, 1)) == "json" && asString(at(tags, 0)) == "wasm" && asString(objectGet(p, "email")) == "ada@example.test" ? 1 : 0; }
|
|
218
|
+
export function smoke_generic_json_text(): i32 { return text(objectGet(root(), "profile")).indexOf("email") >= 0 ? 1 : 0; }
|
|
219
|
+
export function smoke_generic_json_error(): i32 { const r = parse('{"bad":'); return !ok(r) && errorCode(error(r)) == "PULSEWASM_SCHEMA_JSON_PARSE_ERROR" ? 1 : 0; }
|
|
220
|
+
`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function compileAndSmoke({ cwd, outDir, files, checkNames }) {
|
|
224
|
+
const diagnostics = [];
|
|
225
|
+
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'pulsewasm-schema-json-generic-'));
|
|
226
|
+
for (const file of files) {
|
|
227
|
+
const target = path.join(temp, file.file);
|
|
228
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
229
|
+
fs.writeFileSync(target, file.text, 'utf8');
|
|
230
|
+
}
|
|
231
|
+
const runner = path.join(temp, SCHEMA_JSON_GENERIC_PARSER_SMOKE_RUNNER_FILE);
|
|
232
|
+
const wasmFile = path.join(outDir, SCHEMA_JSON_GENERIC_PARSER_WASM_FILE);
|
|
233
|
+
const watFile = path.join(outDir, SCHEMA_JSON_GENERIC_PARSER_WAT_FILE);
|
|
234
|
+
fs.mkdirSync(path.dirname(wasmFile), { recursive: true });
|
|
235
|
+
const asc = resolveAsc(cwd);
|
|
236
|
+
const args = [runner, '--outFile', wasmFile, '--textFile', watFile, '--runtime', 'stub', '--optimize'];
|
|
237
|
+
if (!asc) {
|
|
238
|
+
diagnostics.push({ phase: 'schema-json-generic-parser', code: SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.ascMissing, severity: 'error', message: 'AssemblyScript compiler is not installed.', hint: 'Run npm install so the assemblyscript dev dependency is available.', loc: { file: '<schema-json-generic-parser>' } });
|
|
239
|
+
return { diagnostics, warnings: [], smoke: undefined, outputs: [], command: undefined, result: undefined };
|
|
240
|
+
}
|
|
241
|
+
const proc = spawnSync(asc.executable || asc.command, asc.script ? [asc.script, ...args] : args, { cwd, encoding: 'utf8', maxBuffer: 20 * 1024 * 1024, windowsHide: true });
|
|
242
|
+
const warnings = parseAscWarnings(proc.stderr);
|
|
243
|
+
if (proc.status !== 0) {
|
|
244
|
+
diagnostics.push({ phase: 'schema-json-generic-parser', code: SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.compileFailed, severity: 'error', message: 'Schema JSON generic parser smoke compilation failed.', hint: 'Inspect schema-json-generic-parser-smoke.json stderr.', loc: { file: '<schema-json-generic-parser>' }, details: { stdout: truncate(proc.stdout), stderr: truncate(proc.stderr), exitCode: proc.status } });
|
|
245
|
+
}
|
|
246
|
+
const outputs = [];
|
|
247
|
+
if (fs.existsSync(wasmFile)) outputs.push(fileRecord(wasmFile, cwd, 'schema-json-generic-parser-wasm'));
|
|
248
|
+
if (fs.existsSync(watFile)) outputs.push(fileRecord(watFile, cwd, 'schema-json-generic-parser-wat'));
|
|
249
|
+
let smoke;
|
|
250
|
+
if (diagnostics.length === 0 && fs.existsSync(wasmFile)) {
|
|
251
|
+
const wasm = fs.readFileSync(wasmFile);
|
|
252
|
+
const module = new WebAssembly.Module(wasm);
|
|
253
|
+
const instance = new WebAssembly.Instance(module, { env: { abort() { throw new Error('AssemblyScript abort'); } } });
|
|
254
|
+
const checks = checkNames.map((name) => {
|
|
255
|
+
let actual;
|
|
256
|
+
let status = 'ok';
|
|
257
|
+
let message;
|
|
258
|
+
try {
|
|
259
|
+
const fn = instance.exports[name];
|
|
260
|
+
if (typeof fn !== 'function') { status = 'error'; message = 'Missing smoke export.'; }
|
|
261
|
+
else { actual = fn(); if (actual !== 1) { status = 'error'; message = `Expected 1, got ${actual}.`; } }
|
|
262
|
+
} catch (error) { status = 'error'; message = error.message || String(error); }
|
|
263
|
+
return { name, expected: 1, actual, status, message };
|
|
264
|
+
});
|
|
265
|
+
smoke = { imports: WebAssembly.Module.imports(module), exports: WebAssembly.Module.exports(module), checks, failedChecks: checks.filter((c) => c.status !== 'ok'), wasmBytes: wasm.length };
|
|
266
|
+
if (smoke.failedChecks.length) {
|
|
267
|
+
diagnostics.push({ phase: 'schema-json-generic-parser', code: SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.smokeFailed, severity: 'error', message: 'Schema JSON generic parser smoke checks failed.', hint: 'Inspect schema-json-generic-parser-smoke.json checks.', loc: { file: '<schema-json-generic-parser>' } });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
diagnostics,
|
|
272
|
+
warnings,
|
|
273
|
+
smoke,
|
|
274
|
+
outputs,
|
|
275
|
+
command: {
|
|
276
|
+
executable: 'node node_modules/assemblyscript/bin/asc.js',
|
|
277
|
+
args: [
|
|
278
|
+
SCHEMA_JSON_GENERIC_PARSER_SMOKE_RUNNER_FILE,
|
|
279
|
+
'--outFile',
|
|
280
|
+
SCHEMA_JSON_GENERIC_PARSER_WASM_FILE,
|
|
281
|
+
'--textFile',
|
|
282
|
+
SCHEMA_JSON_GENERIC_PARSER_WAT_FILE,
|
|
283
|
+
'--runtime',
|
|
284
|
+
'stub',
|
|
285
|
+
'--optimize'
|
|
286
|
+
]
|
|
287
|
+
},
|
|
288
|
+
result: { exitCode: proc.status, stdout: truncate(proc.stdout), stderr: truncate(proc.stderr) }
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function buildMarkdown(artifact) {
|
|
293
|
+
const lines = [];
|
|
294
|
+
lines.push('# PulseWasm Schema JSON Generic Parser');
|
|
295
|
+
lines.push('');
|
|
296
|
+
lines.push(`Generated by: \`${artifact.generatedBy}\``);
|
|
297
|
+
lines.push('');
|
|
298
|
+
lines.push('## Purpose');
|
|
299
|
+
lines.push('');
|
|
300
|
+
lines.push('- provide an explicit AssemblyScript JSON value-tree fallback when schema declarations are absent');
|
|
301
|
+
lines.push('- keep values behind opaque refs and typed accessors rather than JS object semantics');
|
|
302
|
+
lines.push('- preserve schema sidecars as the preferred mode when schemas are supplied');
|
|
303
|
+
lines.push('');
|
|
304
|
+
lines.push('## Accessors');
|
|
305
|
+
lines.push('');
|
|
306
|
+
for (const accessor of SCHEMA_JSON_GENERIC_PARSER_ACCESSORS) lines.push(`- \`${accessor}\``);
|
|
307
|
+
lines.push('');
|
|
308
|
+
return `${lines.join('\n')}\n`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function buildSchemaJsonGenericParser(...args) {
|
|
312
|
+
const options = normalizeBuilderOptions(args);
|
|
313
|
+
const cwd = options.cwd || process.cwd();
|
|
314
|
+
const outDir = options.outDir || getDefaultArtifactsDir(cwd);
|
|
315
|
+
const generatedBy = options.generatedBy || PACKAGE_VERSION;
|
|
316
|
+
const config = configArtifact(options);
|
|
317
|
+
const json = jsonConfig(config);
|
|
318
|
+
let selection = normalizeJsonParserConfig(config);
|
|
319
|
+
if (options.forceGenericParser && !selection.genericParser) {
|
|
320
|
+
selection = {
|
|
321
|
+
...selection,
|
|
322
|
+
target: selection.target === 'generic' || selection.target === 'none' ? 'parser' : selection.target,
|
|
323
|
+
effectiveTarget: 'parser',
|
|
324
|
+
parser: SCHEMA_JSON_GENERIC_PARSER_NAME,
|
|
325
|
+
genericParser: true,
|
|
326
|
+
schemaSidecar: false,
|
|
327
|
+
mode: SCHEMA_JSON_GENERIC_PARSER_MODE,
|
|
328
|
+
diagnostics: [],
|
|
329
|
+
warnings: selection.schemaCount > 0 ? [SCHEMA_JSON_GENERIC_PARSER_DIAGNOSTICS.schemasIgnored] : []
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const validation = validateSelection(selection, options);
|
|
333
|
+
const diagnostics = [...validation.diagnostics];
|
|
334
|
+
const warnings = [...validation.warnings];
|
|
335
|
+
const active = selection.genericParser && diagnostics.length === 0;
|
|
336
|
+
const files = [];
|
|
337
|
+
let compileResult = { diagnostics: [], warnings: [], smoke: undefined, outputs: [], command: undefined, result: undefined };
|
|
338
|
+
const checkNames = ['smoke_generic_json_object', 'smoke_generic_json_string', 'smoke_generic_json_number_bool_null', 'smoke_generic_json_array_nested_object', 'smoke_generic_json_text', 'smoke_generic_json_error'];
|
|
339
|
+
if (active) {
|
|
340
|
+
files.push(makeFile(SCHEMA_JSON_GENERIC_PARSER_FILE, genericParserSource()));
|
|
341
|
+
files.push(makeFile(SCHEMA_JSON_GENERIC_PARSER_SMOKE_RUNNER_FILE, genericParserSmokeRunnerSource()));
|
|
342
|
+
compileResult = compileAndSmoke({ cwd, outDir, files, checkNames });
|
|
343
|
+
diagnostics.push(...compileResult.diagnostics);
|
|
344
|
+
warnings.push(...compileResult.warnings.map((warning) => normalizeDiagnostic({ ...warning, severity: 'warning', phase: 'schema-json-generic-parser', pass: 'schema-json-generic-parser' })));
|
|
345
|
+
}
|
|
346
|
+
const status = diagnostics.length ? 'error' : 'ok';
|
|
347
|
+
const contentTypePolicy = typeof json.contentTypePolicy === 'string' && json.contentTypePolicy ? json.contentTypePolicy : DEFAULT_SCHEMA_JSON_CONTENT_TYPE_POLICY;
|
|
348
|
+
const maxBytes = Number.isFinite(Number(json.maxBytes)) ? Number(json.maxBytes) : DEFAULT_SCHEMA_JSON_MAX_BYTES;
|
|
349
|
+
const summary = {
|
|
350
|
+
active,
|
|
351
|
+
target: selection.target,
|
|
352
|
+
effectiveTarget: selection.effectiveTarget,
|
|
353
|
+
parser: selection.parser || null,
|
|
354
|
+
schemas: selection.schemaCount,
|
|
355
|
+
schemaSidecar: false,
|
|
356
|
+
genericJsonTree: active,
|
|
357
|
+
generatedFiles: files.length,
|
|
358
|
+
compiled: Boolean(compileResult.smoke) && diagnostics.length === 0,
|
|
359
|
+
smokeExecuted: Boolean(compileResult.smoke),
|
|
360
|
+
smokeChecks: compileResult.smoke ? compileResult.smoke.checks.length : 0,
|
|
361
|
+
failedSmokeChecks: compileResult.smoke ? compileResult.smoke.failedChecks.length : 0,
|
|
362
|
+
seededBodyTextOnly: true,
|
|
363
|
+
directJsObjects: false,
|
|
364
|
+
diagnostics: diagnostics.length,
|
|
365
|
+
warnings: warnings.length
|
|
366
|
+
};
|
|
367
|
+
const artifact = normalizeArtifact({
|
|
368
|
+
version: SCHEMA_JSON_GENERIC_PARSER_VERSION,
|
|
369
|
+
generatedBy,
|
|
370
|
+
status,
|
|
371
|
+
phase: SCHEMA_JSON_GENERIC_PARSER_PHASE,
|
|
372
|
+
artifact: SCHEMA_JSON_GENERIC_PARSER_ARTIFACT,
|
|
373
|
+
contractId: SCHEMA_JSON_GENERIC_PARSER_ID,
|
|
374
|
+
parser: SCHEMA_JSON_GENERIC_PARSER_NAME,
|
|
375
|
+
mode: SCHEMA_JSON_GENERIC_PARSER_MODE,
|
|
376
|
+
active,
|
|
377
|
+
policy: defaultSchemaJsonGenericParserPolicy(),
|
|
378
|
+
selection,
|
|
379
|
+
moduleFile: active ? SCHEMA_JSON_GENERIC_PARSER_FILE : null,
|
|
380
|
+
smokeArtifact: SCHEMA_JSON_GENERIC_PARSER_SMOKE_ARTIFACT,
|
|
381
|
+
wasmFile: active ? SCHEMA_JSON_GENERIC_PARSER_WASM_FILE : null,
|
|
382
|
+
watFile: active ? SCHEMA_JSON_GENERIC_PARSER_WAT_FILE : null,
|
|
383
|
+
valueKinds: SCHEMA_JSON_GENERIC_VALUE_KINDS,
|
|
384
|
+
accessors: SCHEMA_JSON_GENERIC_PARSER_ACCESSORS,
|
|
385
|
+
reservedSurface: SCHEMA_JSON_GENERIC_RESERVED_SURFACE,
|
|
386
|
+
diagnostics,
|
|
387
|
+
warnings,
|
|
388
|
+
summary
|
|
389
|
+
}, cwd);
|
|
390
|
+
const plan = normalizeArtifact({
|
|
391
|
+
version: SCHEMA_JSON_GENERIC_PARSER_PLAN_VERSION,
|
|
392
|
+
generatedBy,
|
|
393
|
+
status,
|
|
394
|
+
target: selection.target,
|
|
395
|
+
effectiveTarget: selection.effectiveTarget,
|
|
396
|
+
parserModel: {
|
|
397
|
+
kind: SCHEMA_JSON_GENERIC_PARSER_MODE,
|
|
398
|
+
parser: SCHEMA_JSON_GENERIC_PARSER_NAME,
|
|
399
|
+
genericJsonTree: active,
|
|
400
|
+
dependencyFree: true,
|
|
401
|
+
parseResultAbi: true,
|
|
402
|
+
opaqueRefs: true,
|
|
403
|
+
directJsObjectMaterialization: false,
|
|
404
|
+
schemaSidecarsPreferredWhenSchemasSupplied: true
|
|
405
|
+
},
|
|
406
|
+
schemas: [],
|
|
407
|
+
diagnostics,
|
|
408
|
+
warnings,
|
|
409
|
+
summary
|
|
410
|
+
}, cwd);
|
|
411
|
+
const registry = normalizeArtifact({
|
|
412
|
+
version: SCHEMA_JSON_REGISTRY_VERSION,
|
|
413
|
+
generatedBy,
|
|
414
|
+
status,
|
|
415
|
+
target: selection.target,
|
|
416
|
+
defaultNamespace: 'app',
|
|
417
|
+
schemas: [],
|
|
418
|
+
genericParser: {
|
|
419
|
+
version: SCHEMA_JSON_GENERIC_PARSER_VERSION,
|
|
420
|
+
parser: SCHEMA_JSON_GENERIC_PARSER_NAME,
|
|
421
|
+
mode: SCHEMA_JSON_GENERIC_PARSER_MODE,
|
|
422
|
+
moduleFile: active ? SCHEMA_JSON_GENERIC_PARSER_FILE : null,
|
|
423
|
+
accessors: SCHEMA_JSON_GENERIC_PARSER_ACCESSORS,
|
|
424
|
+
valueKinds: SCHEMA_JSON_GENERIC_VALUE_KINDS
|
|
425
|
+
},
|
|
426
|
+
diagnostics,
|
|
427
|
+
warnings,
|
|
428
|
+
summary
|
|
429
|
+
}, cwd);
|
|
430
|
+
const sidecarAbi = normalizeArtifact({
|
|
431
|
+
version: SCHEMA_JSON_GENERIC_PARSER_ABI_VERSION,
|
|
432
|
+
generatedBy,
|
|
433
|
+
status,
|
|
434
|
+
phase: SCHEMA_JSON_PHASE,
|
|
435
|
+
genericParser: true,
|
|
436
|
+
parseAbi: {
|
|
437
|
+
parse: '(json: string) => JsonParseResultRef',
|
|
438
|
+
ok: '(resultRef: usize) => bool',
|
|
439
|
+
value: '(resultRef: usize) => JsonValueRef',
|
|
440
|
+
error: '(resultRef: usize) => PulseErrorRef'
|
|
441
|
+
},
|
|
442
|
+
valueAbi: {
|
|
443
|
+
kind: '(valueRef: usize) => i32',
|
|
444
|
+
has: '(valueRef: usize, key: string) => bool',
|
|
445
|
+
get: '(valueRef: usize, key: string) => JsonValueRef',
|
|
446
|
+
at: '(valueRef: usize, index: i32) => JsonValueRef',
|
|
447
|
+
text: '(valueRef: usize) => string'
|
|
448
|
+
},
|
|
449
|
+
accessors: SCHEMA_JSON_GENERIC_PARSER_ACCESSORS,
|
|
450
|
+
diagnostics,
|
|
451
|
+
warnings,
|
|
452
|
+
summary
|
|
453
|
+
}, cwd);
|
|
454
|
+
const bodyPolicy = normalizeArtifact({
|
|
455
|
+
version: SCHEMA_JSON_BODY_POLICY_VERSION,
|
|
456
|
+
generatedBy,
|
|
457
|
+
status,
|
|
458
|
+
phase: SCHEMA_JSON_PHASE,
|
|
459
|
+
canonicalHandlerSurface: 'ctx.req.parse(schemaId) for schemas; generic parser refs for explicit parser fallback',
|
|
460
|
+
requestBodyAccess: {
|
|
461
|
+
lazyByArchitecture: true,
|
|
462
|
+
phase34Mode: 'seeded-body-text-only',
|
|
463
|
+
bodyReadEffect: 'reserved',
|
|
464
|
+
automaticBodyRead: false,
|
|
465
|
+
automaticParseDuringRouting: false
|
|
466
|
+
},
|
|
467
|
+
contentTypePolicy: {
|
|
468
|
+
default: contentTypePolicy,
|
|
469
|
+
maxBytes,
|
|
470
|
+
implemented: ['accept-json-or-missing', 'require-json'],
|
|
471
|
+
reserved: ['ignore-content-type']
|
|
472
|
+
},
|
|
473
|
+
diagnostics,
|
|
474
|
+
warnings,
|
|
475
|
+
summary
|
|
476
|
+
}, cwd);
|
|
477
|
+
const report = normalizeArtifact({
|
|
478
|
+
version: SCHEMA_JSON_REPORT_VERSION,
|
|
479
|
+
generatedBy,
|
|
480
|
+
status,
|
|
481
|
+
lowering: [{
|
|
482
|
+
kind: SCHEMA_JSON_GENERIC_PARSER_MODE,
|
|
483
|
+
parser: SCHEMA_JSON_GENERIC_PARSER_NAME,
|
|
484
|
+
moduleFile: active ? SCHEMA_JSON_GENERIC_PARSER_FILE : null,
|
|
485
|
+
smokeRunner: active ? SCHEMA_JSON_GENERIC_PARSER_SMOKE_RUNNER_FILE : null,
|
|
486
|
+
accessors: SCHEMA_JSON_GENERIC_PARSER_ACCESSORS,
|
|
487
|
+
valueKinds: SCHEMA_JSON_GENERIC_VALUE_KINDS,
|
|
488
|
+
outputFiles: compileResult.outputs || []
|
|
489
|
+
}],
|
|
490
|
+
command: compileResult.command,
|
|
491
|
+
result: compileResult.result,
|
|
492
|
+
outputFiles: compileResult.outputs || [],
|
|
493
|
+
diagnostics,
|
|
494
|
+
warnings,
|
|
495
|
+
summary
|
|
496
|
+
}, cwd);
|
|
497
|
+
const smoke = normalizeArtifact({
|
|
498
|
+
version: SCHEMA_JSON_GENERIC_PARSER_SMOKE_VERSION,
|
|
499
|
+
generatedBy,
|
|
500
|
+
status,
|
|
501
|
+
active,
|
|
502
|
+
command: compileResult.command,
|
|
503
|
+
result: compileResult.result,
|
|
504
|
+
generated: { inputFiles: files.map(({ text, ...rest }) => rest), outputFiles: compileResult.outputs || [] },
|
|
505
|
+
checks: compileResult.smoke ? compileResult.smoke.checks : [],
|
|
506
|
+
diagnostics,
|
|
507
|
+
warnings,
|
|
508
|
+
summary: {
|
|
509
|
+
compiled: Boolean(compileResult.outputs?.length) && diagnostics.length === 0,
|
|
510
|
+
executed: Boolean(compileResult.smoke),
|
|
511
|
+
checks: compileResult.smoke ? compileResult.smoke.checks.length : 0,
|
|
512
|
+
failedChecks: compileResult.smoke ? compileResult.smoke.failedChecks.length : 0,
|
|
513
|
+
diagnostics: diagnostics.length,
|
|
514
|
+
warnings: warnings.length
|
|
515
|
+
}
|
|
516
|
+
}, cwd);
|
|
517
|
+
return {
|
|
518
|
+
artifact,
|
|
519
|
+
plan,
|
|
520
|
+
report,
|
|
521
|
+
registry,
|
|
522
|
+
sidecarAbi,
|
|
523
|
+
abi: sidecarAbi,
|
|
524
|
+
bodyPolicy,
|
|
525
|
+
smoke,
|
|
526
|
+
files: files.concat([makeFile(SCHEMA_JSON_GENERIC_PARSER_DOC_FILE, buildMarkdown(artifact))]),
|
|
527
|
+
outputFiles: compileResult.outputs || [],
|
|
528
|
+
diagnostics,
|
|
529
|
+
warnings,
|
|
530
|
+
selection
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
module.exports = {
|
|
535
|
+
buildSchemaJsonGenericParser,
|
|
536
|
+
genericParserNeeded,
|
|
537
|
+
genericParserSource,
|
|
538
|
+
genericParserSmokeRunnerSource
|
|
539
|
+
};
|