mirror of
https://github.com/morluto/rea
synced 2026-10-28 06:46:47 -04:00
refactor(javascript): group domain semantics and contracts (#907)
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { applicationGraphEvidenceSchema } from "./javascriptApplicationEvidenceSchemas.js";
|
||||
import { jsonValueSchema } from "../jsonValue.js";
|
||||
import { digestSchema } from "../digests.js";
|
||||
import { prefixedDigestSchema } from "../digests.js";
|
||||
|
||||
const boundedTextSchema = z.string().min(1);
|
||||
const semanticNodeIdSchema = prefixedDigestSchema("jsrg_node");
|
||||
const semanticRelationIdSchema = z
|
||||
.string()
|
||||
.regex(/^jsrg_relation_[a-f0-9]{64}$/u);
|
||||
const semanticUnknownIdSchema = z
|
||||
.string()
|
||||
.regex(/^jsrg_unknown_[a-f0-9]{64}$/u);
|
||||
|
||||
/** Semantic entity kinds admitted by JavaScript Semantic Relation Graph. */
|
||||
export const JAVASCRIPT_SEMANTIC_NODE_KINDS = [
|
||||
"module",
|
||||
"function",
|
||||
"parameter",
|
||||
"binding",
|
||||
"expression",
|
||||
"literal",
|
||||
"property-slot",
|
||||
"call-site",
|
||||
"return-site",
|
||||
"closure-capture",
|
||||
"promise",
|
||||
"task",
|
||||
"event",
|
||||
"listener",
|
||||
"timer",
|
||||
"child-process",
|
||||
"stdio",
|
||||
"signal",
|
||||
"config-source",
|
||||
"request",
|
||||
"response",
|
||||
"boundary",
|
||||
"resource",
|
||||
"unknown-frontier",
|
||||
] as const;
|
||||
|
||||
/** Required semantic-analysis coverage families. */
|
||||
export const JAVASCRIPT_SEMANTIC_RELATION_FAMILIES = [
|
||||
"boundary",
|
||||
"call-flow",
|
||||
"child-process",
|
||||
"closure",
|
||||
"configuration",
|
||||
"data-flow",
|
||||
"event",
|
||||
"object-flow",
|
||||
"promise-ownership",
|
||||
"request",
|
||||
"resource-lifecycle",
|
||||
"timer",
|
||||
] as const;
|
||||
|
||||
/** Directed semantic relations admitted by the graph. */
|
||||
export const JAVASCRIPT_SEMANTIC_RELATIONS = [
|
||||
"acquires",
|
||||
"aggregates",
|
||||
"aliases",
|
||||
"argument-to-parameter",
|
||||
"awaits",
|
||||
"calls",
|
||||
"cancels-timer",
|
||||
"captures",
|
||||
"chains",
|
||||
"coerces",
|
||||
"connects-stdio",
|
||||
"consumed-by",
|
||||
"constructs-request",
|
||||
"creates-promise",
|
||||
"defaults",
|
||||
"defines",
|
||||
"destructures",
|
||||
"detaches-task",
|
||||
"dispatches-candidate",
|
||||
"forwards-signal",
|
||||
"listens-error",
|
||||
"listens-exit",
|
||||
"owns",
|
||||
"overrides",
|
||||
"parses",
|
||||
"reads",
|
||||
"reads-argv",
|
||||
"reads-config",
|
||||
"reads-environment",
|
||||
"reads-property",
|
||||
"registers-listener",
|
||||
"releases",
|
||||
"removes-listener",
|
||||
"returns-task",
|
||||
"returns-to-call",
|
||||
"schedules-timer",
|
||||
"spawns",
|
||||
"spreads",
|
||||
"supplies-argv",
|
||||
"supplies-env",
|
||||
"supplies-request-field",
|
||||
"validates",
|
||||
"writes",
|
||||
"writes-property",
|
||||
] as const;
|
||||
|
||||
/** Map each semantic relation to its coverage family. */
|
||||
export const JAVASCRIPT_SEMANTIC_RELATION_FAMILY = {
|
||||
acquires: "resource-lifecycle",
|
||||
aggregates: "promise-ownership",
|
||||
aliases: "data-flow",
|
||||
"argument-to-parameter": "call-flow",
|
||||
awaits: "promise-ownership",
|
||||
calls: "call-flow",
|
||||
"cancels-timer": "timer",
|
||||
captures: "closure",
|
||||
chains: "promise-ownership",
|
||||
coerces: "boundary",
|
||||
"connects-stdio": "child-process",
|
||||
"consumed-by": "request",
|
||||
"constructs-request": "request",
|
||||
"creates-promise": "promise-ownership",
|
||||
defaults: "configuration",
|
||||
defines: "data-flow",
|
||||
destructures: "object-flow",
|
||||
"detaches-task": "promise-ownership",
|
||||
"dispatches-candidate": "event",
|
||||
"forwards-signal": "child-process",
|
||||
"listens-error": "child-process",
|
||||
"listens-exit": "child-process",
|
||||
owns: "promise-ownership",
|
||||
overrides: "configuration",
|
||||
parses: "boundary",
|
||||
reads: "data-flow",
|
||||
"reads-argv": "configuration",
|
||||
"reads-config": "configuration",
|
||||
"reads-environment": "configuration",
|
||||
"reads-property": "object-flow",
|
||||
"registers-listener": "event",
|
||||
releases: "resource-lifecycle",
|
||||
"removes-listener": "event",
|
||||
"returns-task": "promise-ownership",
|
||||
"returns-to-call": "call-flow",
|
||||
"schedules-timer": "timer",
|
||||
spawns: "child-process",
|
||||
spreads: "object-flow",
|
||||
"supplies-argv": "child-process",
|
||||
"supplies-env": "child-process",
|
||||
"supplies-request-field": "request",
|
||||
validates: "boundary",
|
||||
writes: "data-flow",
|
||||
"writes-property": "object-flow",
|
||||
} as const satisfies Readonly<
|
||||
Record<
|
||||
(typeof JAVASCRIPT_SEMANTIC_RELATIONS)[number],
|
||||
(typeof JAVASCRIPT_SEMANTIC_RELATION_FAMILIES)[number]
|
||||
>
|
||||
>;
|
||||
|
||||
const relativePathSchema = z
|
||||
.string()
|
||||
.min(1)
|
||||
.refine(
|
||||
(path) =>
|
||||
!path.startsWith("/") &&
|
||||
!path.includes("\\") &&
|
||||
path.split("/").every((part) => !["", ".", ".."].includes(part)),
|
||||
"Expected a normalized relative POSIX path without traversal",
|
||||
);
|
||||
|
||||
const sourcePointSchema = z.strictObject({
|
||||
line: z.number().int().min(1),
|
||||
column: z.number().int().min(0),
|
||||
});
|
||||
|
||||
const sourceRangeSchema = z
|
||||
.strictObject({ start: sourcePointSchema, end: sourcePointSchema })
|
||||
.superRefine((range, context) => {
|
||||
if (
|
||||
range.end.line < range.start.line ||
|
||||
(range.end.line === range.start.line &&
|
||||
range.end.column < range.start.column)
|
||||
)
|
||||
context.addIssue({
|
||||
code: "custom",
|
||||
path: ["end"],
|
||||
message: "Source range end must not precede its start",
|
||||
});
|
||||
});
|
||||
|
||||
const semanticPropertiesSchema = z.record(z.string().min(1), jsonValueSchema);
|
||||
|
||||
const semanticNodeIdentitySchema = z.strictObject({
|
||||
artifact_sha256: digestSchema,
|
||||
module_path: relativePathSchema,
|
||||
source_range: sourceRangeSchema.nullable(),
|
||||
role_key: z.string().min(1),
|
||||
});
|
||||
|
||||
/** One semantic entity before its canonical identifier is derived. */
|
||||
export const javaScriptSemanticNodeInputSchema = z.strictObject({
|
||||
kind: z.enum(JAVASCRIPT_SEMANTIC_NODE_KINDS),
|
||||
identity: semanticNodeIdentitySchema,
|
||||
function_node_id: semanticNodeIdSchema.nullable(),
|
||||
application_node_ids: z.array(prefixedDigestSchema("jag_node")),
|
||||
label: z.string().min(1).nullable(),
|
||||
properties: semanticPropertiesSchema,
|
||||
evidence: applicationGraphEvidenceSchema,
|
||||
});
|
||||
|
||||
/** One exact, artifact-version semantic entity. */
|
||||
export const javaScriptSemanticNodeSchema =
|
||||
javaScriptSemanticNodeInputSchema.extend({
|
||||
node_id: semanticNodeIdSchema,
|
||||
identifier_strategy: z.strictObject({
|
||||
strategy: z.literal("semantic-content-sha256"),
|
||||
stability: z.literal("artifact-version"),
|
||||
}),
|
||||
});
|
||||
|
||||
/** One directed semantic relationship before its ID is derived. */
|
||||
export const javaScriptSemanticRelationInputSchema = z.strictObject({
|
||||
source_node_id: semanticNodeIdSchema,
|
||||
target_node_id: semanticNodeIdSchema,
|
||||
relation: z.enum(JAVASCRIPT_SEMANTIC_RELATIONS),
|
||||
resolution: z.enum(["resolved", "candidate"]),
|
||||
properties: semanticPropertiesSchema,
|
||||
evidence: applicationGraphEvidenceSchema,
|
||||
});
|
||||
|
||||
/** One canonical directed semantic relationship. */
|
||||
export const javaScriptSemanticRelationSchema =
|
||||
javaScriptSemanticRelationInputSchema.extend({
|
||||
relation_id: semanticRelationIdSchema,
|
||||
identifier_strategy: z.strictObject({
|
||||
strategy: z.literal("semantic-content-sha256"),
|
||||
stability: z.literal("relationship-exact"),
|
||||
}),
|
||||
});
|
||||
|
||||
/** Reasons semantic analysis can leave an explicit frontier. */
|
||||
export const JAVASCRIPT_SEMANTIC_UNKNOWN_REASONS = [
|
||||
"ambiguous-target",
|
||||
"bound-reached",
|
||||
"dynamic-call",
|
||||
"dynamic-property",
|
||||
"dynamic-scope",
|
||||
"eval-or-generated-code",
|
||||
"incomplete-module",
|
||||
"missing-source",
|
||||
"unsupported-syntax",
|
||||
] as const;
|
||||
|
||||
/** One unresolved semantic frontier before its ID is derived. */
|
||||
export const javaScriptSemanticUnknownInputSchema = z.strictObject({
|
||||
node_id: semanticNodeIdSchema.nullable(),
|
||||
family: z.enum(JAVASCRIPT_SEMANTIC_RELATION_FAMILIES),
|
||||
relation_kinds: z.array(z.enum(JAVASCRIPT_SEMANTIC_RELATIONS)).min(1),
|
||||
reason: z.enum(JAVASCRIPT_SEMANTIC_UNKNOWN_REASONS),
|
||||
detail: boundedTextSchema,
|
||||
candidate_node_ids: z.array(semanticNodeIdSchema),
|
||||
evidence: applicationGraphEvidenceSchema,
|
||||
});
|
||||
|
||||
/** One canonical unresolved semantic frontier. */
|
||||
export const javaScriptSemanticUnknownSchema =
|
||||
javaScriptSemanticUnknownInputSchema.extend({
|
||||
unknown_id: semanticUnknownIdSchema,
|
||||
});
|
||||
|
||||
const fingerprintComponentsSchema = z.strictObject({
|
||||
parameter_arity: z.number().int().min(0),
|
||||
normalized_ast_sha256: digestSchema,
|
||||
control_flow_sha256: digestSchema,
|
||||
relation_shape_sha256: digestSchema,
|
||||
literal_set_sha256: digestSchema,
|
||||
effects: z.array(
|
||||
z.enum([
|
||||
"async",
|
||||
"child-process",
|
||||
"event",
|
||||
"network",
|
||||
"promise",
|
||||
"resource",
|
||||
"timer",
|
||||
]),
|
||||
),
|
||||
});
|
||||
|
||||
/** One bounded rename-resistant function fingerprint. */
|
||||
export const javaScriptSemanticFingerprintInputSchema = z.strictObject({
|
||||
function_node_id: semanticNodeIdSchema,
|
||||
algorithm: z.literal("rea.javascript-semantic-function"),
|
||||
status: z.enum(["complete", "partial", "unavailable"]),
|
||||
components: fingerprintComponentsSchema,
|
||||
limitations: z.array(boundedTextSchema),
|
||||
evidence: applicationGraphEvidenceSchema,
|
||||
});
|
||||
|
||||
/** One canonical function fingerprint record. */
|
||||
export const javaScriptSemanticFingerprintSchema =
|
||||
javaScriptSemanticFingerprintInputSchema.extend({
|
||||
fingerprint_id: prefixedDigestSchema("jsrg_fingerprint"),
|
||||
fingerprint_sha256: digestSchema,
|
||||
});
|
||||
|
||||
const familyCoverageSchema = z.strictObject({
|
||||
family: z.enum(JAVASCRIPT_SEMANTIC_RELATION_FAMILIES),
|
||||
status: z.enum(["complete", "partial", "unknown", "unsupported"]),
|
||||
retained_relations: z.number().int().min(0),
|
||||
omitted_relations: z.number().int().min(0).nullable(),
|
||||
unknown_ids: z.array(semanticUnknownIdSchema),
|
||||
});
|
||||
|
||||
const graphCoverageSchema = z.strictObject({
|
||||
status: z.enum(["complete", "partial", "unknown", "unavailable"]),
|
||||
truncated: z.boolean(),
|
||||
omitted_nodes: z.number().int().min(0).nullable(),
|
||||
omitted_relations: z.number().int().min(0).nullable(),
|
||||
limits: z.array(
|
||||
z.strictObject({
|
||||
name: z.string().min(1),
|
||||
value: z.number().int().min(0),
|
||||
unit: z.enum(["items", "bytes", "milliseconds", "depth", "other"]),
|
||||
}),
|
||||
),
|
||||
families: z.array(familyCoverageSchema).length(12),
|
||||
});
|
||||
|
||||
/** Graph content before its top-level commitment is derived. */
|
||||
export const javaScriptSemanticGraphInputSchema = z.strictObject({
|
||||
schema: z.literal("JavaScriptSemanticRelationGraph"),
|
||||
root_artifact_sha256: digestSchema,
|
||||
application_graph_id: prefixedDigestSchema("jag"),
|
||||
root_node_ids: z.array(semanticNodeIdSchema).min(1),
|
||||
nodes: z.array(javaScriptSemanticNodeSchema).min(1),
|
||||
relations: z.array(javaScriptSemanticRelationSchema),
|
||||
fingerprints: z.array(javaScriptSemanticFingerprintSchema),
|
||||
unknowns: z.array(javaScriptSemanticUnknownSchema),
|
||||
coverage: graphCoverageSchema,
|
||||
limitations: z.array(boundedTextSchema),
|
||||
});
|
||||
|
||||
/** Stored JavaScript Semantic Relation Graph. */
|
||||
export const javaScriptSemanticGraphRecordSchema =
|
||||
javaScriptSemanticGraphInputSchema.extend({
|
||||
graph_id: prefixedDigestSchema("jsrg"),
|
||||
});
|
||||
|
||||
/** Canonical semantic node. */
|
||||
export type JavaScriptSemanticGraphNode = z.infer<
|
||||
typeof javaScriptSemanticNodeSchema
|
||||
>;
|
||||
/** Canonical semantic relation. */
|
||||
export type JavaScriptSemanticGraphRelation = z.infer<
|
||||
typeof javaScriptSemanticRelationSchema
|
||||
>;
|
||||
/** Canonical unresolved semantic frontier. */
|
||||
export type JavaScriptSemanticGraphUnknown = z.infer<
|
||||
typeof javaScriptSemanticUnknownSchema
|
||||
>;
|
||||
/** Canonical graph content before graph ID derivation. */
|
||||
export type JavaScriptSemanticGraphInput = z.infer<
|
||||
typeof javaScriptSemanticGraphInputSchema
|
||||
>;
|
||||
Reference in New Issue
Block a user