New:Socket for Asana Is Now Available.Learn more
Get Started

@archstone/compiler

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@archstone/compiler - npm Package Compare versions

Comparing version
0.13.0
to
0.14.0
+79
-4
dist/index.d.ts
import { LoadResult, PolicyDoc } from '@archstone/schema';
type JsonType = "string" | "number" | "boolean" | "null" | "array" | "object";
/** The closed set of `JsonType`, as a value — so a reader validating a recorded shape checks
* against the same list the writer produces, not a second copy of it. */
declare const JSON_TYPES: readonly JsonType[];
/** A recorded response shape: JSONPath-ish key path -> JSON type. Values are never present. */
type ShapeMap = Record<string, JsonType>;
/** sha256:<hex> of a JSON value's shape. Two payloads with the same keys/types but
* different values fingerprint identically; a renamed or retyped key changes it.
*
* Computed from the raw entry list, NOT from `describeShape`, so its output is
* bit-for-bit what ADD-18 shipped — see `describeShape`'s note on duplicate paths.
* Every contract already committed in the wild depends on this not moving. */
declare function fingerprintShape(value: unknown): string;
/**
* The same traversal as `fingerprintShape`, kept as a `path -> type` map (ADD-114 D-1) so a
* drift report can name paths and a human can read one in a binding.
*
* **Lossy in one pathological case, deliberately.** A JSON key containing a dot collides in
* this flattened path space — `{"a.b": 1, "a": {"b": 2}}` yields `$.a.b` twice, and a map keeps
* one. The pair list `fingerprintShape` hashes keeps both, which is why the two are computed
* from the same traversal but not from each other. The consequence is bounded and fail-safe:
* for such a payload `fingerprintShapeMap(describeShape(x)) !== fingerprintShape(x)`, so
* ADD-114 D-3's consistency check reports the recorded shape as stale and suppresses the diff,
* rather than naming fields from a shape that cannot represent this provider. Health is
* unaffected in every case — the fingerprint remains the sole authority (D-2).
*/
declare function describeShape(value: unknown): ShapeMap;
/**
* Re-derive a fingerprint from an already-recorded `ShapeMap`.
*
* Exists for ADD-114 D-3: `shape` and `fingerprint` are two records of one observation and can
* disagree if either is hand-edited, so `verify` re-derives one from the other before trusting
* a diff. For any payload without the duplicate-path collision above,
* `fingerprintShapeMap(describeShape(x)) === fingerprintShape(x)`.
*/
declare function fingerprintShapeMap(shape: ShapeMap): string;
type SemanticType = "location" | "date-range" | "party" | "preference-set" | "money" | "identifier" | "string" | "text" | "time-slot" | "quantity" | "enum" | "date" | "datetime";

@@ -77,2 +114,11 @@ /** The closed set of semantic types (mirrors cdl.schema.json). A field `type:` not in

fingerprint: string;
/**
* The recorded shape itself — `path -> type`, values never present (ADD-114 D-1).
*
* Optional, and NARRATIVE ONLY: `fingerprint` above remains the sole authority for a
* binding's health (ADD-114 D-2, preserving ADD-18 D-4). This exists so `verify` can name
* WHICH paths moved instead of only reporting that a hash did. A contract without it
* verifies exactly as it did before ADD-114.
*/
shape?: ShapeMap;
probeFixture: string;

@@ -193,5 +239,34 @@ }

/** sha256:<hex> of a JSON value's shape. Two payloads with the same keys/types but
* different values fingerprint identically; a renamed or retyped key changes it. */
declare function fingerprintShape(value: unknown): string;
interface ShapeAddition {
path: string;
type: JsonType;
}
interface ShapeRetype {
path: string;
from: JsonType;
to: JsonType;
}
/** What moved between a recorded shape and a live one. Every list is sorted by path, so
* two runs over the same pair of shapes produce identical reports. */
interface ShapeDiff {
added: ShapeAddition[];
removed: ShapeAddition[];
retyped: ShapeRetype[];
}
/**
* Compare a recorded response shape against a live one.
*
* A path present in both with the same type does not appear in the result — the diff carries
* only what changed, so an unchanged contract produces three empty lists rather than a full
* inventory the caller has to filter.
*/
declare function diffShape(recorded: ShapeMap, live: ShapeMap): ShapeDiff;
/** True when a diff has anything to report. */
declare function hasShapeDrift(diff: ShapeDiff): boolean;
/**
* The operator-facing sentence for a diff — one spelling, so the human report and any future
* consumer never describe the same comparison differently (the ADD-19/`contractViolationMessage`
* precedent).
*/
declare function shapeDriftSummary(diff: ShapeDiff): string;

@@ -220,2 +295,2 @@ type Severity = "error" | "warning";

export { type Diagnostic, type IR, type IRConnector, type IRContract, type IRField, type IRFieldMapping, type IRPolicyRule, type IRResourceRegistry, type IRResponseMapping, type IRRestConnector, type IRTool, type IRType, LIFECYCLE_STATES, type Lifecycle, type PathParse, type Resolution, SEMANTIC_TYPES, type SemanticType, type Severity, compile, domainOf, evalPath, fingerprintShape, parsePath, policyScopesCapability, referencedResourceName, resolveResourceName, resourceIndex, validateSemantics };
export { type Diagnostic, type IR, type IRConnector, type IRContract, type IRField, type IRFieldMapping, type IRPolicyRule, type IRResourceRegistry, type IRResponseMapping, type IRRestConnector, type IRTool, type IRType, JSON_TYPES, type JsonType, LIFECYCLE_STATES, type Lifecycle, type PathParse, type Resolution, SEMANTIC_TYPES, type SemanticType, type Severity, type ShapeAddition, type ShapeDiff, type ShapeMap, type ShapeRetype, compile, describeShape, diffShape, domainOf, evalPath, fingerprintShape, fingerprintShapeMap, hasShapeDrift, parsePath, policyScopesCapability, referencedResourceName, resolveResourceName, resourceIndex, shapeDriftSummary, validateSemantics };

@@ -84,2 +84,3 @@ // src/ir.ts

import { createHash } from "crypto";
var JSON_TYPES = ["string", "number", "boolean", "null", "array", "object"];
function jsonType(v) {

@@ -102,11 +103,53 @@ if (v === null) return "null";

}
function hashEntries(entries) {
const sorted = [...entries].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
const canonical = JSON.stringify(sorted);
return `sha256:${createHash("sha256").update(canonical).digest("hex")}`;
}
function fingerprintShape(value) {
const entries = [];
shapeEntries(value, "$", entries);
entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
const canonical = JSON.stringify(entries);
const hash = createHash("sha256").update(canonical).digest("hex");
return `sha256:${hash}`;
return hashEntries(entries);
}
function describeShape(value) {
const entries = [];
shapeEntries(value, "$", entries);
return Object.fromEntries(entries);
}
function fingerprintShapeMap(shape) {
return hashEntries(Object.entries(shape));
}
// src/shape-diff.ts
function byPath(entries) {
return entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
}
function diffShape(recorded, live) {
const added = [];
const removed = [];
const retyped = [];
for (const [path, type] of Object.entries(live)) {
const before = recorded[path];
if (before === void 0) added.push({ path, type });
else if (before !== type) retyped.push({ path, from: before, to: type });
}
for (const [path, type] of Object.entries(recorded)) {
if (live[path] === void 0) removed.push({ path, type });
}
return { added: byPath(added), removed: byPath(removed), retyped: byPath(retyped) };
}
function hasShapeDrift(diff) {
return diff.added.length > 0 || diff.removed.length > 0 || diff.retyped.length > 0;
}
function shapeDriftSummary(diff) {
const parts = [];
const fmt = (e) => `${e.path} (${e.type})`;
if (diff.added.length > 0) parts.push(`gained ${diff.added.length} field(s): ${diff.added.map(fmt).join(", ")}`);
if (diff.removed.length > 0) parts.push(`lost ${diff.removed.length} field(s): ${diff.removed.map(fmt).join(", ")}`);
if (diff.retyped.length > 0) {
parts.push(`retyped ${diff.retyped.length} field(s): ${diff.retyped.map((e) => `${e.path} (${e.from} \u2192 ${e.to})`).join(", ")}`);
}
return parts.join("; ");
}
// src/compile.ts

@@ -189,2 +232,11 @@ var CONNECTOR_TYPES = /* @__PURE__ */ new Set(["rest", "graphql", "grpc", "sql", "soap"]);

}
function lowerShape(raw) {
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return void 0;
const out = {};
for (const [path, type] of Object.entries(raw)) {
if (typeof type !== "string" || !JSON_TYPES.includes(type)) return void 0;
out[path] = type;
}
return Object.keys(out).length > 0 ? out : void 0;
}
function lowerContract(raw) {

@@ -194,3 +246,6 @@ if (typeof raw.fingerprint !== "string") return void 0;

if (typeof probe.fixture !== "string") return void 0;
return { fingerprint: raw.fingerprint, probeFixture: probe.fixture };
const contract = { fingerprint: raw.fingerprint, probeFixture: probe.fixture };
const shape = lowerShape(raw.shape);
if (shape) contract.shape = shape;
return contract;
}

@@ -621,8 +676,13 @@ function policyScopesCapability(meta, capabilityId, provider) {

export {
JSON_TYPES,
LIFECYCLE_STATES,
SEMANTIC_TYPES,
compile,
describeShape,
diffShape,
domainOf,
evalPath,
fingerprintShape,
fingerprintShapeMap,
hasShapeDrift,
parsePath,

@@ -633,4 +693,5 @@ policyScopesCapability,

resourceIndex,
shapeDriftSummary,
validateSemantics
};
//# sourceMappingURL=index.js.map
+2
-2
{
"name": "@archstone/compiler",
"version": "0.13.0",
"version": "0.14.0",
"private": false,

@@ -41,3 +41,3 @@ "type": "module",

"jsonpath-plus": "^10.3.0",
"@archstone/schema": "0.13.0"
"@archstone/schema": "0.14.0"
},

@@ -44,0 +44,0 @@ "devDependencies": {

Sorry, the diff of this file is too big to display