@flighthq/importdiagnostics 0.2.1-next.701.409c279

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,4 @@
1
+ import type { ImportDiagnostic, ImportDiagnosticSeverity } from '@flighthq/types';
2
+ export declare function collectImportDiagnostics(run: (sink: ImportDiagnostic[]) => void): ImportDiagnostic[];
3
+ export declare function reportImportDiagnostic(sink: ImportDiagnostic[] | undefined, severity: ImportDiagnosticSeverity, kind: string, origin: string, detail?: Readonly<Record<string, boolean | number | string>>): void;
4
+ //# sourceMappingURL=importDiagnosticCollector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importDiagnosticCollector.d.ts","sourceRoot":"","sources":["../src/importDiagnosticCollector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAiBlF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,IAAI,GAAG,gBAAgB,EAAE,CAIpG;AAcD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,gBAAgB,EAAE,GAAG,SAAS,EACpC,QAAQ,EAAE,wBAAwB,EAClC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,GAC3D,IAAI,CAGN"}
@@ -0,0 +1,37 @@
1
+ // The collector seam for importer diagnostics: where crumbs are recorded, and the opt-in that engages
2
+ // the recording. An `ImportDiagnostic[]` sink IS the collector — parsers write to it through
3
+ // `reportImportDiagnostic`; consumers create and drain one through `collectImportDiagnostics`.
4
+ // The opt-in engagement surface: runs `run` with a fresh collector installed and returns the crumbs it
5
+ // emitted, as plain data agents and tests can assert on. Engaging a collector is the ONLY thing that
6
+ // makes a parser record — the default parse (no collector) stays near-free and records nothing. Import
7
+ // it to inspect what an importer dropped/skipped/recovered/rejected; it and the text machinery it feeds
8
+ // (`formatImportDiagnostic`, per-package `explain*`) shed from a production bundle that never opts in.
9
+ //
10
+ // This is the importer analogue of an `explain*` query, but it COLLECTS during the single consume-once
11
+ // parse pass instead of re-walking the drop conditions in a duplicate implementation — so it can never
12
+ // drift out of sync with the parser the way a hand-mirrored explainer would.
13
+ //
14
+ // Usage: `collectImportDiagnostics((sink) => parseAwd2(bytes, sink))`.
15
+ export function collectImportDiagnostics(run) {
16
+ const diagnostics = [];
17
+ run(diagnostics);
18
+ return diagnostics;
19
+ }
20
+ // The colocated seam every `*-formats` parser calls at a data-dropping branch. When `sink` is
21
+ // undefined — the default parse path, where no caller engaged a collector — it does nothing, so an
22
+ // unopted parse pays only this single `undefined` check and no crumb is built or allocated. `kind` is a
23
+ // stable dot-namespaced code that lives here at the drop site (never a central registry, so it vanishes
24
+ // when the branch is removed); `origin` is the emitting function's own name (the true origin, not a
25
+ // wrapper); `detail` is a minimal plain-data tag, never prose (the words live in `formatImportDiagnostic`).
26
+ //
27
+ // PERF CONTRACT (the normal path must not slow): the call sits INSIDE the drop branch, so a well-formed
28
+ // parse never reaches it. In a HOT LOOP (per-vertex/per-element validation) do NOT call this per element
29
+ // — aggregate the offenders and report once after the loop (a count in `detail`), so no engaged-or-not
30
+ // per-element seam cost exists. `detail` is evaluated by the caller before the call, so keep it a small
31
+ // literal built only within the (rare, non-hot) drop branch.
32
+ export function reportImportDiagnostic(sink, severity, kind, origin, detail) {
33
+ if (sink === undefined)
34
+ return;
35
+ sink.push({ detail, kind, origin, severity });
36
+ }
37
+ //# sourceMappingURL=importDiagnosticCollector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importDiagnosticCollector.js","sourceRoot":"","sources":["../src/importDiagnosticCollector.ts"],"names":[],"mappings":"AAEA,sGAAsG;AACtG,6FAA6F;AAC7F,+FAA+F;AAE/F,uGAAuG;AACvG,qGAAqG;AACrG,uGAAuG;AACvG,wGAAwG;AACxG,uGAAuG;AACvG,EAAE;AACF,uGAAuG;AACvG,uGAAuG;AACvG,6EAA6E;AAC7E,EAAE;AACF,uEAAuE;AACvE,MAAM,UAAU,wBAAwB,CAAC,GAAuC;IAC9E,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,GAAG,CAAC,WAAW,CAAC,CAAC;IACjB,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,8FAA8F;AAC9F,mGAAmG;AACnG,wGAAwG;AACxG,wGAAwG;AACxG,oGAAoG;AACpG,4GAA4G;AAC5G,EAAE;AACF,wGAAwG;AACxG,yGAAyG;AACzG,uGAAuG;AACvG,wGAAwG;AACxG,6DAA6D;AAC7D,MAAM,UAAU,sBAAsB,CACpC,IAAoC,EACpC,QAAkC,EAClC,IAAY,EACZ,MAAc,EACd,MAA4D;IAE5D,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ImportDiagnostic } from '@flighthq/types';
2
+ export declare function formatImportDiagnostic(diagnostic: Readonly<ImportDiagnostic>): string;
3
+ //# sourceMappingURL=importDiagnosticText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importDiagnosticText.d.ts","sourceRoot":"","sources":["../src/importDiagnosticText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AASxD,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAQrF"}
@@ -0,0 +1,17 @@
1
+ // The human-text side of an import diagnostic — kept in its own concept module so a production bundle
2
+ // that records crumbs (or never engages diagnostics at all) sheds every byte of the text machinery.
3
+ // Renders one ImportDiagnostic crumb as a stable human-readable line for logs or a CLI. Shape:
4
+ // `<severity> <origin>: <kind>[ key=value …]`, detail keys sorted for deterministic output. Pure and
5
+ // never throws; allocates only on this opt-in formatting path (the returned string, plus a sorted keys
6
+ // array when the crumb carries detail) — a bundle that never expands a crumb sheds it entirely.
7
+ export function formatImportDiagnostic(diagnostic) {
8
+ const { detail, kind, origin, severity } = diagnostic;
9
+ let detailText = '';
10
+ if (detail !== undefined) {
11
+ const keys = Object.keys(detail).sort();
12
+ for (const key of keys)
13
+ detailText += ` ${key}=${detail[key]}`;
14
+ }
15
+ return `${severity} ${origin}: ${kind}${detailText}`;
16
+ }
17
+ //# sourceMappingURL=importDiagnosticText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"importDiagnosticText.js","sourceRoot":"","sources":["../src/importDiagnosticText.ts"],"names":[],"mappings":"AAEA,sGAAsG;AACtG,oGAAoG;AAEpG,+FAA+F;AAC/F,qGAAqG;AACrG,uGAAuG;AACvG,gGAAgG;AAChG,MAAM,UAAU,sBAAsB,CAAC,UAAsC;IAC3E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC;IACtD,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,IAAI;YAAE,UAAU,IAAI,IAAI,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,GAAG,QAAQ,IAAI,MAAM,KAAK,IAAI,GAAG,UAAU,EAAE,CAAC;AACvD,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './importDiagnosticCollector';
2
+ export * from './importDiagnosticText';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './importDiagnosticCollector';
2
+ export * from './importDiagnosticText';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@flighthq/importdiagnostics",
3
+ "version": "0.2.1-next.701.409c279",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/flighthq/flight.git",
7
+ "directory": "packages/importdiagnostics"
8
+ },
9
+ "type": "module",
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src/**/*.test.ts",
21
+ "!dist/**/*.test.js",
22
+ "!dist/**/*.test.d.ts",
23
+ "!dist/**/*.test.js.map",
24
+ "!dist/**/*.test.d.ts.map"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc -b",
28
+ "clean": "tsc -b --clean",
29
+ "test": "vitest run --config vitest.config.ts",
30
+ "test:watch": "vitest --watch --config vitest.config.ts",
31
+ "prepack": "npm run clean && npm run clean:dist && npm run build",
32
+ "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
33
+ },
34
+ "dependencies": {
35
+ "@flighthq/types": "0.2.1-next.701.409c279"
36
+ },
37
+ "devDependencies": {
38
+ "typescript": "^5.3.0"
39
+ },
40
+ "description": "Shared structured-diagnostics seam for the *-formats importers — reportImportDiagnostic colocates a stable-kind ImportDiagnostic at each parser data-dropping branch (a no-op when no collector is passed, so an unopted parse pays only one undefined check), collectImportDiagnostics runs a parse with a collector installed as the drift-free explain* alternative to re-walking drop conditions, and formatImportDiagnostic renders one line for humans; deps types only, sideEffects false",
41
+ "sideEffects": false
42
+ }
@@ -0,0 +1,55 @@
1
+ import type { ImportDiagnostic } from '@flighthq/types';
2
+ import { ImportDiagnosticSeverity } from '@flighthq/types';
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ import { collectImportDiagnostics, reportImportDiagnostic } from './importDiagnosticCollector';
6
+
7
+ describe('collectImportDiagnostics', () => {
8
+ it('returns the crumbs the run callback emits into its engaged collector', () => {
9
+ // Stands in for `collectImportDiagnostics((sink) => parseAwd2(bytes, sink))`.
10
+ const diagnostics = collectImportDiagnostics((sink) => {
11
+ reportImportDiagnostic(sink, ImportDiagnosticSeverity.Skip, 'awd2.method-material', 'resolveAwdMaterial', {
12
+ count: 2,
13
+ });
14
+ reportImportDiagnostic(sink, ImportDiagnosticSeverity.Reject, 'awd2.unsupported-version', 'parseAwd2', {
15
+ version: 3,
16
+ });
17
+ });
18
+ expect(diagnostics).toHaveLength(2);
19
+ expect(diagnostics.map((d) => d.kind)).toEqual(['awd2.method-material', 'awd2.unsupported-version']);
20
+ });
21
+
22
+ it('returns an empty array when the parse emits nothing (well-formed input)', () => {
23
+ expect(collectImportDiagnostics(() => {})).toEqual([]);
24
+ });
25
+ });
26
+
27
+ describe('reportImportDiagnostic', () => {
28
+ it('pushes a structured crumb onto the collector when one is engaged', () => {
29
+ const sink: ImportDiagnostic[] = [];
30
+ reportImportDiagnostic(sink, ImportDiagnosticSeverity.Drop, 'awd2.skin-vertex-mismatch', 'buildAwdSkin', {
31
+ actual: 3,
32
+ expected: 4,
33
+ });
34
+ expect(sink).toHaveLength(1);
35
+ expect(sink[0]).toEqual({
36
+ detail: { actual: 3, expected: 4 },
37
+ kind: 'awd2.skin-vertex-mismatch',
38
+ origin: 'buildAwdSkin',
39
+ severity: 'Drop',
40
+ });
41
+ });
42
+
43
+ it('carries the detail through undefined when none is supplied', () => {
44
+ const sink: ImportDiagnostic[] = [];
45
+ reportImportDiagnostic(sink, ImportDiagnosticSeverity.Reject, 'md2.bad-magic', 'parseMd2');
46
+ expect(sink[0].detail).toBeUndefined();
47
+ expect(sink[0].severity).toBe('Reject');
48
+ });
49
+
50
+ it('does nothing (no throw, no allocation) when the collector is undefined — the default silent path', () => {
51
+ expect(() =>
52
+ reportImportDiagnostic(undefined, ImportDiagnosticSeverity.Drop, 'gltf.accessor-out-of-bounds', 'readAccessor'),
53
+ ).not.toThrow();
54
+ });
55
+ });
@@ -0,0 +1,25 @@
1
+ import { ImportDiagnosticSeverity } from '@flighthq/types';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { formatImportDiagnostic } from './importDiagnosticText';
5
+
6
+ describe('formatImportDiagnostic', () => {
7
+ it('renders severity, origin, and kind with detail keys sorted', () => {
8
+ const line = formatImportDiagnostic({
9
+ detail: { expected: 4, actual: 3 },
10
+ kind: 'awd2.skin-vertex-mismatch',
11
+ origin: 'buildAwdSkin',
12
+ severity: ImportDiagnosticSeverity.Drop,
13
+ });
14
+ expect(line).toBe('Drop buildAwdSkin: awd2.skin-vertex-mismatch actual=3 expected=4');
15
+ });
16
+
17
+ it('omits the detail section when there is no detail', () => {
18
+ const line = formatImportDiagnostic({
19
+ kind: 'md2.bad-magic',
20
+ origin: 'parseMd2',
21
+ severity: ImportDiagnosticSeverity.Reject,
22
+ });
23
+ expect(line).toBe('Reject parseMd2: md2.bad-magic');
24
+ });
25
+ });