geospec 0.1.0-beta.2 → 0.1.0-beta.4

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.
Files changed (46) hide show
  1. package/agent/doctrine.md +8 -0
  2. package/agent/geospec-authoring/SKILL.md +19 -20
  3. package/agent/geospec-authoring/api-constants.md +11 -1
  4. package/agent/geospec-authoring/api-functions.md +9 -1
  5. package/agent/geospec-authoring/api-index.md +22 -7
  6. package/agent/geospec-authoring/api-types.md +91 -3
  7. package/agent/geospec-authoring/public-api-constants.md +33 -1
  8. package/agent/geospec-authoring/public-api-functions.md +21 -2
  9. package/agent/geospec-authoring/public-api-index.md +57 -20
  10. package/agent/geospec-authoring/public-api-types-2.md +267 -220
  11. package/agent/geospec-authoring/public-api-types-3.md +217 -0
  12. package/agent/geospec-authoring/public-api-types.md +37 -137
  13. package/agent/resources.js +14 -13
  14. package/agent/skills.json +3 -2
  15. package/dist/assertion-client/client.d.mts +12 -2
  16. package/dist/assertion-client/client.mjs +2 -2
  17. package/dist/assertion-client/index.d.mts +2 -2
  18. package/dist/assertion-client/index.mjs +1 -1
  19. package/dist/config/tau-project-artifact.mjs +1 -1
  20. package/dist/create-geospec.d.mts +3 -2
  21. package/dist/create-geospec.mjs +1 -1
  22. package/dist/engine/client.d.mts +21 -1
  23. package/dist/engine/client.mjs +1 -1
  24. package/dist/index.d.mts +2 -2
  25. package/dist/model/export-intent.d.mts +17 -1
  26. package/dist/model/export-intent.mjs +1 -1
  27. package/dist/model/index.d.mts +4 -3
  28. package/dist/model/index.mjs +1 -1
  29. package/dist/model/native-model-loader.d.mts +30 -7
  30. package/dist/model/native-model-loader.mjs +1 -1
  31. package/dist/model/region-scope.d.mts +23 -0
  32. package/dist/model/region-scope.mjs +1 -0
  33. package/dist/model/subject-assertions.mjs +1 -1
  34. package/dist/model/types.d.mts +50 -3
  35. package/dist/runner/collector.d.mts +4 -2
  36. package/dist/runner/collector.mjs +2 -2
  37. package/dist/runner/index.d.mts +3 -2
  38. package/dist/runner/index.mjs +1 -1
  39. package/dist/runner/native/native-serial-runner.mjs +1 -1
  40. package/dist/runner/run-geospec-module.mjs +2 -2
  41. package/dist/runner/timeout-codes.d.mts +45 -0
  42. package/dist/runner/timeout-codes.mjs +1 -0
  43. package/dist/runner/types.d.mts +34 -1
  44. package/dist/runner/worker/runner-types.d.mts +24 -3
  45. package/dist/runner/worker/serial-runner.mjs +1 -1
  46. package/package.json +4 -4
@@ -0,0 +1,23 @@
1
+ import "../mesh/types.mjs";
2
+ import "./types.mjs";
3
+ import { GeoSpecAssertion } from "../runner/types.mjs";
4
+ //#region src/model/region-scope.d.ts
5
+ /**
6
+ * The claims a region-cropped subject answers: measurements of the cropped geometry itself. Each
7
+ * verdict holds for the region only, and the assertion records the region it stands for.
8
+ *
9
+ * Every other kind quantifies over the whole model (component counts, all-components
10
+ * interference, occurrences, watertightness, integrity, topology, diagnostics) or relates parts the
11
+ * crop may have cut, so a crop can neither prove nor disprove it.
12
+ *
13
+ * @public
14
+ */
15
+ declare const geoSpecRegionScopedKinds: ReadonlySet<GeoSpecAssertion['kind']>;
16
+ /**
17
+ * Diagnostic code of a whole-model claim made on a region-cropped subject.
18
+ *
19
+ * @public
20
+ */
21
+ declare const geoSpecRegionWholeModelClaimCode = "GEOSPEC_REGION_WHOLE_MODEL_CLAIM";
22
+ //#endregion
23
+ export { geoSpecRegionScopedKinds, geoSpecRegionWholeModelClaimCode };
@@ -0,0 +1 @@
1
+ const e=new Set([`boundingBox`,`volume`,`surfaceArea`,`mass`,`centerOfMass`]),t=`GEOSPEC_REGION_WHOLE_MODEL_CLAIM`,n=(n,r)=>n===void 0||e.has(r)?void 0:{code:t,severity:`error`,message:`A '${r}' claim is about the whole model, and this subject is cropped to the region min [${n.min.join(`, `)}] max [${n.max.join(`, `)}] mm, so the claim cannot be decided on it.`,suggestion:`Keep the claim and make it on a whole-model load (loadModel without region). If the whole model exceeds a host limit, provision more resources (host admission limits or workUnitLimit) and report the claim as not decided at this capacity. A region load answers only bounding box, volume, surface area, mass and center of mass of that region.`,details:{kind:r,region:{min:[...n.min],max:[...n.max]},regionScopedKinds:[...e]}};export{e as geoSpecRegionScopedKinds,t as geoSpecRegionWholeModelClaimCode,n as regionScopeRefusal};
@@ -1 +1 @@
1
- import{resolveGeoSpecSubject as e}from"./subject.mjs";import{createGeoSpecMatcherMethods as t}from"../assertion-client/client.mjs";import{diagnosticForTransport as n,geometryDiagnosticSchema as r}from"./errors.mjs";const i=i=>{let a=e(i),o=a.client.expectGeo(a.identity),s=s=>t({subject:i,polarity:s?`negative`:`positive`,invoke:t=>{e(i);let c=s?o.not:o,l=Reflect.apply(c[t.matcher],c,t.arguments),u=l.diagnostics.map(e=>n(r.parse(e)));return{kind:t.kind,subject:a.identity,...a.load===void 0?{}:{loadId:a.load.loadId},expected:t.expected,passed:l.status===`passed`,diagnostics:u,report:l}}});return Object.assign(s(!1),{not:s(!0)})};export{i as expectGeoSubject};
1
+ import{resolveGeoSpecSubject as e}from"./subject.mjs";import{createGeoSpecMatcherMethods as t}from"../assertion-client/client.mjs";import{diagnosticForTransport as n,geometryDiagnosticSchema as r}from"./errors.mjs";import{regionScopeRefusal as i}from"./region-scope.mjs";const a=(a,o)=>{let s=e(a),c=o?.workUnitLimit??s.load?.workUnitLimit,l=s.client.expectGeo(s.identity,c===void 0?void 0:{workUnitLimit:c}),u=o=>t({subject:a,polarity:o?`negative`:`positive`,invoke:t=>{e(a);let c=s.load?.region,u=c===void 0?void 0:{min:[...c.min],max:[...c.max]},d=u===void 0?{}:{region:u},f=i(c,t.kind);if(f!==void 0)return{kind:t.kind,subject:s.identity,...s.load===void 0?{}:{loadId:s.load.loadId},...d,expected:t.expected,passed:!1,diagnostics:[f]};let p=o?l.not:l,m=Reflect.apply(p[t.matcher],p,t.arguments),h=m.diagnostics.map(e=>n(r.parse(e)));return{kind:t.kind,subject:s.identity,...s.load===void 0?{}:{loadId:s.load.loadId},...d,expected:t.expected,passed:m.status===`passed`,diagnostics:h,report:m}}});return Object.assign(u(!1),{not:u(!0)})};export{a as expectGeoSubject};
@@ -36,11 +36,13 @@ type GeoSpecModelFormat = MeshFileFormat | 'step' | 'stp';
36
36
  * small so typed runtime clients do not have to widen their full generic
37
37
  * method surface to GeoSpec's testing DSL. A client with `shutdown` is awaited
38
38
  * through it when GeoSpec releases an owned runtime, so its host resources are
39
- * closed before the caller (for example a worker thread) exits.
39
+ * closed before the caller (for example a worker thread) exits. A client with
40
+ * `snapshotSource` lets one run reuse an identical file load after confirming
41
+ * that the source closure it exported is unchanged.
40
42
  *
41
43
  * @public
42
44
  */
43
- type GeoSpecRuntimeClient = Pick<RuntimeClient, 'connect' | 'terminate'> & Partial<Pick<RuntimeClient, 'shutdown'>> & {
45
+ type GeoSpecRuntimeClient = Pick<RuntimeClient, 'connect' | 'terminate'> & Partial<Pick<RuntimeClient, 'shutdown' | 'snapshotSource' | 'describe'>> & {
44
46
  open: (input: Pick<Parameters<RuntimeClient['open']>[0], 'source' | 'parameters' | 'stage' | 'watch' | 'signal'>) => Pick<ReturnType<RuntimeClient['open']>, 'export' | 'close'>;
45
47
  on?(event: 'telemetry', handler: (batch: {
46
48
  readonly entries: ReadonlyArray<{
@@ -71,6 +73,15 @@ type GeoSpecRuntimeSourceAdapter = {
71
73
  file?: string;
72
74
  }): Promise<GeoSpecRuntimeClient>;
73
75
  };
76
+ /**
77
+ * Axis-aligned crop box in canonical Z-up millimetres; every `min` axis is below its `max` axis.
78
+ *
79
+ * @public
80
+ */
81
+ type GeoSpecLoadRegion = {
82
+ readonly min: readonly [number, number, number];
83
+ readonly max: readonly [number, number, number];
84
+ };
74
85
  /**
75
86
  * Direct geometry-source model load options.
76
87
  *
@@ -102,6 +113,12 @@ type LoadModelSourceOptions = {
102
113
  meshLinearTolerance?: number;
103
114
  /** Angular tolerance in degrees used while meshing exact BRep evidence. */
104
115
  meshAngularToleranceDegrees?: number;
116
+ /**
117
+ * Default work-unit budget for every claim on this subject: a positive integer no greater than
118
+ * the host's maximum (default `geoSpecMaximumWorkUnitLimit`, 512 000 000). `expectGeo(subject, { workUnitLimit })` overrides
119
+ * it per chain. Recorded in the load evidence; it never changes the exported or admitted bytes.
120
+ */
121
+ workUnitLimit?: number;
105
122
  };
106
123
  /**
107
124
  * Inline code-CAD model load options.
@@ -131,6 +148,21 @@ type LoadModelCodeOptions<Code extends Record<string, string> = Record<string, s
131
148
  meshLinearTolerance?: number;
132
149
  /** Angular tolerance in degrees used while meshing exact BRep evidence. */
133
150
  meshAngularToleranceDegrees?: number;
151
+ /**
152
+ * Default work-unit budget for every claim on this subject: a positive integer no greater than
153
+ * the host's maximum (default `geoSpecMaximumWorkUnitLimit`, 512 000 000). `expectGeo(subject, { workUnitLimit })` overrides
154
+ * it per chain. Recorded in the load evidence; it never changes the exported or admitted bytes.
155
+ */
156
+ workUnitLimit?: number;
157
+ /**
158
+ * Axis-aligned millimetre box the kernel crops the model to before export, so a large mesh model
159
+ * can be checked region by region. Only kernels whose direct GLB export declares a `region` option
160
+ * honor it; any other kernel, a STEP load or a crop the kernel does not confirm refuses with a typed
161
+ * diagnostic instead of loading the whole model. The cropped subject is a different subject: its
162
+ * admission carries the kernel's crop notice and its load evidence records `region`, so a claim on
163
+ * it never stands for the whole model.
164
+ */
165
+ region?: GeoSpecLoadRegion;
134
166
  };
135
167
  /**
136
168
  * Filesystem-backed model load options.
@@ -158,6 +190,21 @@ type LoadModelFileOptions = {
158
190
  meshLinearTolerance?: number;
159
191
  /** Angular tolerance in degrees used while meshing exact BRep evidence. */
160
192
  meshAngularToleranceDegrees?: number;
193
+ /**
194
+ * Default work-unit budget for every claim on this subject: a positive integer no greater than
195
+ * the host's maximum (default `geoSpecMaximumWorkUnitLimit`, 512 000 000). `expectGeo(subject, { workUnitLimit })` overrides
196
+ * it per chain. Recorded in the load evidence; it never changes the exported or admitted bytes.
197
+ */
198
+ workUnitLimit?: number;
199
+ /**
200
+ * Axis-aligned millimetre box the kernel crops the model to before export, so a large mesh model
201
+ * can be checked region by region. Only kernels whose direct GLB export declares a `region` option
202
+ * honor it; any other kernel, a STEP load or a crop the kernel does not confirm refuses with a typed
203
+ * diagnostic instead of loading the whole model. The cropped subject is a different subject: its
204
+ * admission carries the kernel's crop notice and its load evidence records `region`, so a claim on
205
+ * it never stands for the whole model.
206
+ */
207
+ region?: GeoSpecLoadRegion;
161
208
  };
162
209
  /**
163
210
  * Options accepted by {@link import('./load-model.js').loadModel}.
@@ -204,4 +251,4 @@ type CreateModelLoaderOptions = {
204
251
  meshAngularToleranceDegrees?: number;
205
252
  };
206
253
  //#endregion
207
- export { CreateModelLoaderOptions, GeoSpecModelFormat, GeoSpecModelLoader, GeoSpecRuntimeClient, GeoSpecRuntimeClientFactory, GeoSpecRuntimeSourceAdapter, LoadModelCodeOptions, LoadModelFileOptions, LoadModelOptions, LoadModelSourceOptions, ManagedGeoSpecModelLoader, MeshBufferSource, MeshSource };
254
+ export { CreateModelLoaderOptions, GeoSpecLoadRegion, GeoSpecModelFormat, GeoSpecModelLoader, GeoSpecRuntimeClient, GeoSpecRuntimeClientFactory, GeoSpecRuntimeSourceAdapter, LoadModelCodeOptions, LoadModelFileOptions, LoadModelOptions, LoadModelSourceOptions, ManagedGeoSpecModelLoader, MeshBufferSource, MeshSource };
@@ -1,5 +1,5 @@
1
1
  import { GeometryDiagnostic } from "../mesh/types.mjs";
2
- import { GeoSpecMatcher, GeoSpecTestCase } from "./types.mjs";
2
+ import { GeoSpecExpectOptions, GeoSpecMatcher, GeoSpecTestCase, GeoSpecTestEvent } from "./types.mjs";
3
3
  import { GeoSpecAssertionClientOptions } from "../assertion-client/client.mjs";
4
4
  import "../assertion-client/index.mjs";
5
5
  import { GeoSpecTestNamePattern } from "./filter.mjs";
@@ -17,7 +17,7 @@ type GeoSpecCollector = {
17
17
  describeSkip(name: string, _function?: GeoSpecTestFunction): void;
18
18
  it(name: string, function_: GeoSpecTestFunction): void;
19
19
  itSkip(name: string, _function?: GeoSpecTestFunction): void;
20
- expectGeo(subject: unknown): GeoSpecMatcher;
20
+ expectGeo(subject: unknown, options?: GeoSpecExpectOptions): GeoSpecMatcher;
21
21
  waitForCompletion(testTimeout?: number, testNamePattern?: GeoSpecTestNamePattern): Promise<void>;
22
22
  };
23
23
  /** Per-module collector configuration; native subject/engine lifetime stays with the host. @public */
@@ -25,6 +25,8 @@ type GeoSpecCollectorOptions = {
25
25
  matcherWallBackstop?: number;
26
26
  forensic?: boolean;
27
27
  nativeAssertions: GeoSpecAssertionClientOptions;
28
+ /** Observes each selected test as it starts and settles, for host progress reporting. */
29
+ onTestEvent?: (event: GeoSpecTestEvent) => void;
28
30
  };
29
31
  /**
30
32
  * Assertion error thrown by GeoSpec matchers when an expectation does not hold.
@@ -1,2 +1,2 @@
1
- import{resolveGeoSpecSubject as e}from"../model/subject.mjs";import{GeoSpecAssertionError as t,createGeoSpecAssertionClient as n,createGeoSpecMatcherMethods as r}from"../assertion-client/client.mjs";import{diagnosticForTransport as i,geometryDiagnosticSchema as a}from"../model/errors.mjs";import{matchesGeoSpecTestName as o}from"./filter.mjs";const s=`__GEOSPEC_COLLECTOR__`,c=globalThis,l=e=>typeof e==`object`&&!!e&&`then`in e&&typeof e.then==`function`;var u=class extends Error{diagnostics;constructor(e){super(e.map(e=>e.message).join(`
2
- `)||`GeoSpec assertion failed.`),this.name=`GeoSpecAssertionError`,this.diagnostics=Object.freeze([...e])}};const d=e=>{if(e instanceof u)return[...e.diagnostics];if(typeof e==`object`&&e&&Reflect.get(e,`name`)===`GeoSpecModelLoadError`){let t=Reflect.get(e,`diagnostics`);if(Array.isArray(t)&&t.length>0)try{return t.map(e=>i(f(e)))}catch{}}let t=typeof e==`object`&&e&&typeof Reflect.get(e,`message`)==`string`?String(Reflect.get(e,`message`)):String(e),{details:n}=i({code:`TEST_FAILED`,severity:`error`,message:t,details:e});return t.includes(`model.volume is not a function`)?[{code:`GEOSPEC_SUBJECT_API_MISUSE`,severity:`error`,message:`GeoSpec GeometrySubject does not expose model.volume().`,suggestion:`Use expectGeo(model).toHaveVolume({ value, tolerance }) instead of reading model.volume().`,details:n}]:/Cannot read properties of undefined \(reading 'bounds'\)/u.test(t)?[{code:`GEOSPEC_SUBJECT_API_MISUSE`,severity:`error`,message:`GeoSpec GeometrySubject does not expose model.boundingBox.bounds.`,suggestion:`Use expectGeo(model).toHaveBoundingBox({ min, max, size, center, tolerance }) instead of reading model.boundingBox.`,details:n}]:[{code:`TEST_FAILED`,severity:`error`,message:t,details:n}]},f=e=>a.parse(e),p=(e,t)=>{if(e.passed=t.length===0,e.diagnostics=t,t.length>0)throw new u(t);return e};var m=class extends Error{};const h=async(e,t)=>{if(t===void 0)return e;let n;try{return await Promise.race([e,new Promise((e,r)=>{n=setTimeout(()=>{r(new m(`GeoSpec test timed out after ${t}ms.`))},t)})])}finally{n!==void 0&&clearTimeout(n)}},g=(e,t)=>{for(let n of e)n.status===`rejected`&&t(n.reason)},_=()=>{let e=c[s];if(!v(e))throw Error(`GeoSpec collector is not active. Run the module through runGeoSpecModule().`);return e},v=e=>typeof e==`object`&&!!e&&`describe`in e&&`it`in e&&`expectGeo`in e&&`tests`in e,y=e=>typeof e==`object`&&!!e&&`suite`in e&&`name`in e&&`assertions`in e;function b(a){let s=n(a.nativeAssertions),c=[],m=[],_=[],v=[],b=new WeakSet,x,S=!1,C=(e,t)=>{_.push((async()=>{try{await e}catch(e){t.onError(e)}finally{t.onFinally()}})())},w=e=>{m.push({ordinal:m.length,suite:[...c],name:e,assertions:[],status:`skipped`,diagnostics:[]})},T=n=>{if(!y(x))throw Error(`expectGeo() must be called inside it().`);let r={kind:n.kind,subject:null,expected:n.expected};x.assertions.push(r);let o=performance.now();try{let o=e(n.subject,a.nativeAssertions.engine);r.subject=o.identity,o.load!==void 0&&(r.loadId=o.load.loadId);let c=s.expectGeo(o.identity),l=(n.polarity===`negative`?c.not:c)[n.matcher],u;try{u=l(...n.arguments)}catch(e){if(!(e instanceof t))throw e;u=e.report}if(r.report=u,u.status===`passed`)return p(r,[]);let d=u.diagnostics.map(e=>i(f(e)));return p(r,d.length>0?d:[{code:`GEOSPEC_NATIVE_ASSERTION_FAILED`,severity:`error`,message:`Native claim '${u.claimId}' ended with status '${u.status}'.`,details:{claimId:u.claimId,status:u.status}}])}catch(e){throw e instanceof u&&r.report!==void 0&&b.add(e),r.passed=!1,r.diagnostics??=d(e),e}finally{r.durationMs=performance.now()-o}};return{describe(e,t){c.push(e);try{let n=t();if(l(n)){let t=[...c];C(n,{onError(n){m.push({ordinal:m.length,suite:t,name:e,assertions:[],status:`failed`,diagnostics:d(n)})},onFinally(){c.pop()}});return}}catch(t){m.push({ordinal:m.length,suite:[...c],name:e,assertions:[],status:`failed`,diagnostics:d(t)})}c.pop()},describeSkip(e){w(e)},it(e,t){let n={ordinal:m.length,suite:[...c],name:e,assertions:[],status:`not-run`,diagnostics:[]};m.push(n),v.push({test:n,function_:t})},itSkip(e){w(e)},expectGeo(e){return Object.assign(r({invoke:T,polarity:`positive`,subject:e}),{not:r({invoke:T,polarity:`negative`,subject:e})})},async waitForCompletion(e,t){if(S)return;S=!0;let n=Promise.allSettled(_);try{await h(n,e)}catch(e){throw await n,e}for(let n of v){if(!o(n.test,t))continue;let r=x;x=n.test,n.test.status=`passed`;let i=performance.now(),a=[],s=e=>{a.some(t=>Object.is(t,e))||a.push(e)};try{let t;try{t=Promise.resolve(n.function_()),await h(t,e)}catch(e){s(e)}if(t!==void 0&&g(await Promise.allSettled([t]),s),a.length>0||n.test.assertions.some(e=>e.passed===!1)){n.test.status=`failed`;for(let e of a)n.test.diagnostics.push(...d(e));let{assertions:e}=n.test;if(!(a.some(e=>!(e instanceof u)||!b.has(e))||e.some(e=>e.passed===!1&&(e.report===void 0||e.report.status===`invalid`||e.report.status===`engine-error`)))){let t=e.map(e=>e.report?.status);n.test.status=t.includes(`failed`)?`failed`:t.some(e=>e!==void 0&&e!==`passed`&&e!==`unsupported`)?`inconclusive`:t.includes(`unsupported`)?`unsupported`:`failed`}}}finally{x=r,n.test.durationMs=performance.now()-i}}},tests:m}}const x=()=>{Reflect.deleteProperty(c,s)},S=e=>{c[s]=e};export{u as GeoSpecAssertionError,x as clearCollectorGlobals,s as collectorGlobalKey,b as createCollector,_ as getCollector,S as installCollector};
1
+ import{assertGeoSpecAuthoredWorkUnitLimit as e}from"../engine/client.mjs";import{resolveGeoSpecSubject as t}from"../model/subject.mjs";import{GeoSpecAssertionError as n,createGeoSpecAssertionClient as r,createGeoSpecMatcherMethods as i}from"../assertion-client/client.mjs";import{diagnosticForTransport as a,geometryDiagnosticSchema as o}from"../model/errors.mjs";import{matchesGeoSpecTestName as s}from"./filter.mjs";import{geoSpecTimeoutCode as c}from"./timeout-codes.mjs";import{regionScopeRefusal as l}from"../model/region-scope.mjs";const u=`__GEOSPEC_COLLECTOR__`,d=globalThis,f=e=>typeof e==`object`&&!!e&&`then`in e&&typeof e.then==`function`;var p=class extends Error{diagnostics;constructor(e){super(e.map(e=>e.message).join(`
2
+ `)||`GeoSpec assertion failed.`),this.name=`GeoSpecAssertionError`,this.diagnostics=Object.freeze([...e])}},m=class extends Error{testTimeout;constructor(e){super(`GeoSpec test timed out after ${e}ms.`),this.name=`GeoSpecTestTimeoutError`,this.testTimeout=e}};const h=e=>{if(e instanceof p)return[...e.diagnostics];if(e instanceof m)return[{code:c.test,severity:`error`,message:`${e.message} The test still ran to completion before the run moved on; this is a wall-clock timeout, not a geometry verdict.`,suggestion:`Keep the assertion and the model as designed. Raise testTimeout (CLI --test-timeout) so the requirement can finish; if this host cannot give it the time, report it as not decided at this capacity.`,details:{testTimeout:e.testTimeout}}];if(typeof e==`object`&&e&&Reflect.get(e,`name`)===`GeoSpecModelLoadError`){let t=Reflect.get(e,`diagnostics`);if(Array.isArray(t)&&t.length>0)try{return t.map(e=>a(g(e)))}catch{}}let t=typeof e==`object`&&e&&typeof Reflect.get(e,`message`)==`string`?String(Reflect.get(e,`message`)):String(e),{details:n}=a({code:`TEST_FAILED`,severity:`error`,message:t,details:e});return t.includes(`model.volume is not a function`)?[{code:`GEOSPEC_SUBJECT_API_MISUSE`,severity:`error`,message:`GeoSpec GeometrySubject does not expose model.volume().`,suggestion:`Use expectGeo(model).toHaveVolume({ value, tolerance }) instead of reading model.volume().`,details:n}]:/Cannot read properties of undefined \(reading 'bounds'\)/u.test(t)?[{code:`GEOSPEC_SUBJECT_API_MISUSE`,severity:`error`,message:`GeoSpec GeometrySubject does not expose model.boundingBox.bounds.`,suggestion:`Use expectGeo(model).toHaveBoundingBox({ min, max, size, center, tolerance }) instead of reading model.boundingBox.`,details:n}]:[{code:`TEST_FAILED`,severity:`error`,message:t,details:n}]},g=e=>o.parse(e),_=(e,t)=>{if(e.passed=t.length===0,e.diagnostics=t,t.length>0)throw new p(t);return e},v=async(e,t)=>{if(t===void 0)return e;let n;try{return await Promise.race([e,new Promise((e,r)=>{n=setTimeout(()=>{r(new m(t))},t)})])}finally{n!==void 0&&clearTimeout(n)}},y=(e,t)=>{for(let n of e)n.status===`rejected`&&t(n.reason)},b=()=>{let e=d[u];if(!x(e))throw Error(`GeoSpec collector is not active. Run the module through runGeoSpecModule().`);return e},x=e=>typeof e==`object`&&!!e&&`describe`in e&&`it`in e&&`expectGeo`in e&&`tests`in e,S=e=>typeof e==`object`&&!!e&&`suite`in e&&`name`in e&&`assertions`in e;function C(o){let c=r(o.nativeAssertions),u=[],d=[],m=[],b=[],x=new WeakSet,C,w=!1,T=(e,t)=>{try{o.onTestEvent?.({type:e,test:t})}catch{}},E=(e,t)=>{m.push((async()=>{try{await e}catch(e){t.onError(e)}finally{t.onFinally()}})())},D=e=>{d.push({ordinal:d.length,suite:[...u],name:e,assertions:[],status:`skipped`,diagnostics:[]})},O=(e,r)=>{if(!S(C))throw Error(`expectGeo() must be called inside it().`);let i={kind:e.kind,subject:null,expected:e.expected};C.assertions.push(i);let s=performance.now();try{let s=t(e.subject,o.nativeAssertions.engine);i.subject=s.identity,s.load!==void 0&&(i.loadId=s.load.loadId);let u=s.load?.region;u!==void 0&&(i.region={min:[...u.min],max:[...u.max]});let d=l(u,e.kind);if(d!==void 0)return _(i,[d]);let f=r?.workUnitLimit??s.load?.workUnitLimit,p=c.expectGeo(s.identity,f===void 0?void 0:{workUnitLimit:f}),m=(e.polarity===`negative`?p.not:p)[e.matcher],h;try{h=m(...e.arguments)}catch(e){if(!(e instanceof n))throw e;h=e.report}if(i.report=h,h.status===`passed`)return _(i,[]);let v=h.diagnostics.map(e=>a(g(e)));return _(i,v.length>0?v:[{code:`GEOSPEC_NATIVE_ASSERTION_FAILED`,severity:`error`,message:`Native claim '${h.claimId}' ended with status '${h.status}'.`,details:{claimId:h.claimId,status:h.status}}])}catch(e){throw e instanceof p&&i.report!==void 0&&x.add(e),i.passed=!1,i.diagnostics??=h(e),e}finally{i.durationMs=performance.now()-s}};return{describe(e,t){u.push(e);try{let n=t();if(f(n)){let t=[...u];E(n,{onError(n){d.push({ordinal:d.length,suite:t,name:e,assertions:[],status:`failed`,diagnostics:h(n)})},onFinally(){u.pop()}});return}}catch(t){d.push({ordinal:d.length,suite:[...u],name:e,assertions:[],status:`failed`,diagnostics:h(t)})}u.pop()},describeSkip(e){D(e)},it(e,t){let n={ordinal:d.length,suite:[...u],name:e,assertions:[],status:`not-run`,diagnostics:[]};d.push(n),b.push({test:n,function_:t})},itSkip(e){D(e)},expectGeo(t,n){e(n?.workUnitLimit,`expectGeo workUnitLimit`,o.nativeAssertions.maximumWorkUnitLimit);let r=e=>O(e,n);return Object.assign(i({invoke:r,polarity:`positive`,subject:t}),{not:i({invoke:r,polarity:`negative`,subject:t})})},async waitForCompletion(e,t){if(w)return;w=!0;let n=Promise.allSettled(m);try{await v(n,e)}catch(e){throw await n,e}for(let n of b){if(!s(n.test,t))continue;let r=C;C=n.test,n.test.status=`passed`,T(`test-start`,n.test);let i=performance.now(),a=[],o=e=>{a.some(t=>Object.is(t,e))||a.push(e)};try{let t;try{t=Promise.resolve(n.function_()),await v(t,e)}catch(e){o(e)}if(t!==void 0&&y(await Promise.allSettled([t]),o),a.length>0||n.test.assertions.some(e=>e.passed===!1)){n.test.status=`failed`;for(let e of a)n.test.diagnostics.push(...h(e));let{assertions:e}=n.test;if(!(a.some(e=>!(e instanceof p)||!x.has(e))||e.some(e=>e.passed===!1&&(e.report===void 0||e.report.status===`invalid`||e.report.status===`engine-error`)))){let t=e.map(e=>e.report?.status);n.test.status=t.includes(`failed`)?`failed`:t.some(e=>e!==void 0&&e!==`passed`&&e!==`unsupported`)?`inconclusive`:t.includes(`unsupported`)?`unsupported`:`failed`}}}finally{C=r,n.test.durationMs=performance.now()-i,T(`test-complete`,n.test)}}},tests:d}}const w=()=>{Reflect.deleteProperty(d,u)},T=e=>{d[u]=e};export{p as GeoSpecAssertionError,w as clearCollectorGlobals,u as collectorGlobalKey,C as createCollector,b as getCollector,T as installCollector};
@@ -1,6 +1,7 @@
1
- import { GeoSpecAssemblyOccurrenceExpectation, GeoSpecAssemblyOccurrencesExpectation, GeoSpecAssertion, GeoSpecAxisExpectation, GeoSpecBoundingBoxExpectation, GeoSpecCenterOfMassExpectation, GeoSpecChamferFeatureExpectation, GeoSpecCircularHoleExpectation, GeoSpecCircularHolePatternExpectation, GeoSpecComponentInterferenceAllowance, GeoSpecComponentInterferenceExpectation, GeoSpecComponentInterferencePairExpectation, GeoSpecConnectedComponentsExpectation, GeoSpecCylindricalFaceExpectation, GeoSpecFilletFeatureExpectation, GeoSpecGeometrySelector, GeoSpecMassExpectation, GeoSpecMatcher, GeoSpecMeshIntegrityExpectation, GeoSpecMinimumWallThicknessExpectation, GeoSpecModuleBundleCache, GeoSpecNumericExpectation, GeoSpecPlanarFaceExpectation, GeoSpecPointExpectation, GeoSpecProductStructureExpectation, GeoSpecRunFailure, GeoSpecRunResult, GeoSpecRunSuccess, GeoSpecSpatialRelationshipExpectation, GeoSpecSpatialRelationshipsExpectation, GeoSpecStepUnitsExpectation, GeoSpecSurfaceAreaExpectation, GeoSpecTestCase, GeoSpecTestStatus, GeoSpecTopologyCountsExpectation, GeoSpecValidBrepExpectation, GeoSpecVolumeExpectation, RunGeoSpecModuleOptions } from "./types.mjs";
1
+ import { GeoSpecAssemblyOccurrenceExpectation, GeoSpecAssemblyOccurrencesExpectation, GeoSpecAssertion, GeoSpecAxisExpectation, GeoSpecBoundingBoxExpectation, GeoSpecCenterOfMassExpectation, GeoSpecChamferFeatureExpectation, GeoSpecCircularHoleExpectation, GeoSpecCircularHolePatternExpectation, GeoSpecComponentInterferenceAllowance, GeoSpecComponentInterferenceExpectation, GeoSpecComponentInterferencePairExpectation, GeoSpecConnectedComponentsExpectation, GeoSpecCylindricalFaceExpectation, GeoSpecFilletFeatureExpectation, GeoSpecGeometrySelector, GeoSpecMassExpectation, GeoSpecMatcher, GeoSpecMeshIntegrityExpectation, GeoSpecMinimumWallThicknessExpectation, GeoSpecModuleBundleCache, GeoSpecNumericExpectation, GeoSpecPlanarFaceExpectation, GeoSpecPointExpectation, GeoSpecProductStructureExpectation, GeoSpecRunFailure, GeoSpecRunResult, GeoSpecRunSuccess, GeoSpecSpatialRelationshipExpectation, GeoSpecSpatialRelationshipsExpectation, GeoSpecStepUnitsExpectation, GeoSpecSurfaceAreaExpectation, GeoSpecTestCase, GeoSpecTestEvent, GeoSpecTestStatus, GeoSpecTopologyCountsExpectation, GeoSpecValidBrepExpectation, GeoSpecVolumeExpectation, RunGeoSpecModuleOptions } from "./types.mjs";
2
2
  import { GeoSpecTestNamePattern, compileGeoSpecTestNamePattern, filterGeoSpecTests, matchesGeoSpecTestName } from "./filter.mjs";
3
3
  import { GeoSpecAssertionError, GeoSpecCollector, GeoSpecCollectorOptions, clearCollectorGlobals, createCollector, installCollector } from "./collector.mjs";
4
4
  import { DiscoverGeoSpecFilesOptions, GeoSpecDiscoveryFileKind, GeoSpecDiscoveryFileStat, GeoSpecDiscoveryFileSystem, GeoSpecDiscoveryResult, defaultGeoSpecIgnoredDirectories, defaultGeoSpecInclude, discoverGeoSpecFiles, isGeoSpecTestFile } from "./discovery.mjs";
5
5
  import { runGeoSpecModule } from "./run-geospec-module.mjs";
6
- export { type DiscoverGeoSpecFilesOptions, type GeoSpecAssemblyOccurrenceExpectation, type GeoSpecAssemblyOccurrencesExpectation, type GeoSpecAssertion, GeoSpecAssertionError, type GeoSpecAxisExpectation, type GeoSpecBoundingBoxExpectation, type GeoSpecCenterOfMassExpectation, type GeoSpecChamferFeatureExpectation, type GeoSpecCircularHoleExpectation, type GeoSpecCircularHolePatternExpectation, type GeoSpecCollector, type GeoSpecCollectorOptions, type GeoSpecComponentInterferenceAllowance, type GeoSpecComponentInterferenceExpectation, type GeoSpecComponentInterferencePairExpectation, type GeoSpecConnectedComponentsExpectation, type GeoSpecCylindricalFaceExpectation, type GeoSpecDiscoveryFileKind, type GeoSpecDiscoveryFileStat, type GeoSpecDiscoveryFileSystem, type GeoSpecDiscoveryResult, type GeoSpecFilletFeatureExpectation, type GeoSpecGeometrySelector, type GeoSpecMassExpectation, type GeoSpecMatcher, type GeoSpecMeshIntegrityExpectation, type GeoSpecMinimumWallThicknessExpectation, type GeoSpecModuleBundleCache, type GeoSpecNumericExpectation, type GeoSpecPlanarFaceExpectation, type GeoSpecPointExpectation, type GeoSpecProductStructureExpectation, type GeoSpecRunFailure, type GeoSpecRunResult, type GeoSpecRunSuccess, type GeoSpecSpatialRelationshipExpectation, type GeoSpecSpatialRelationshipsExpectation, type GeoSpecStepUnitsExpectation, type GeoSpecSurfaceAreaExpectation, type GeoSpecTestCase, type GeoSpecTestNamePattern, type GeoSpecTestStatus, type GeoSpecTopologyCountsExpectation, type GeoSpecValidBrepExpectation, type GeoSpecVolumeExpectation, type RunGeoSpecModuleOptions, clearCollectorGlobals, compileGeoSpecTestNamePattern, createCollector, defaultGeoSpecIgnoredDirectories, defaultGeoSpecInclude, discoverGeoSpecFiles, filterGeoSpecTests, installCollector, isGeoSpecTestFile, matchesGeoSpecTestName, runGeoSpecModule };
6
+ import { GeoSpecTimeoutCode, geoSpecTimeoutCode, isGeoSpecTimeoutCode } from "./timeout-codes.mjs";
7
+ export { type DiscoverGeoSpecFilesOptions, type GeoSpecAssemblyOccurrenceExpectation, type GeoSpecAssemblyOccurrencesExpectation, type GeoSpecAssertion, GeoSpecAssertionError, type GeoSpecAxisExpectation, type GeoSpecBoundingBoxExpectation, type GeoSpecCenterOfMassExpectation, type GeoSpecChamferFeatureExpectation, type GeoSpecCircularHoleExpectation, type GeoSpecCircularHolePatternExpectation, type GeoSpecCollector, type GeoSpecCollectorOptions, type GeoSpecComponentInterferenceAllowance, type GeoSpecComponentInterferenceExpectation, type GeoSpecComponentInterferencePairExpectation, type GeoSpecConnectedComponentsExpectation, type GeoSpecCylindricalFaceExpectation, type GeoSpecDiscoveryFileKind, type GeoSpecDiscoveryFileStat, type GeoSpecDiscoveryFileSystem, type GeoSpecDiscoveryResult, type GeoSpecFilletFeatureExpectation, type GeoSpecGeometrySelector, type GeoSpecMassExpectation, type GeoSpecMatcher, type GeoSpecMeshIntegrityExpectation, type GeoSpecMinimumWallThicknessExpectation, type GeoSpecModuleBundleCache, type GeoSpecNumericExpectation, type GeoSpecPlanarFaceExpectation, type GeoSpecPointExpectation, type GeoSpecProductStructureExpectation, type GeoSpecRunFailure, type GeoSpecRunResult, type GeoSpecRunSuccess, type GeoSpecSpatialRelationshipExpectation, type GeoSpecSpatialRelationshipsExpectation, type GeoSpecStepUnitsExpectation, type GeoSpecSurfaceAreaExpectation, type GeoSpecTestCase, type GeoSpecTestEvent, type GeoSpecTestNamePattern, type GeoSpecTestStatus, type GeoSpecTimeoutCode, type GeoSpecTopologyCountsExpectation, type GeoSpecValidBrepExpectation, type GeoSpecVolumeExpectation, type RunGeoSpecModuleOptions, clearCollectorGlobals, compileGeoSpecTestNamePattern, createCollector, defaultGeoSpecIgnoredDirectories, defaultGeoSpecInclude, discoverGeoSpecFiles, filterGeoSpecTests, geoSpecTimeoutCode, installCollector, isGeoSpecTestFile, isGeoSpecTimeoutCode, matchesGeoSpecTestName, runGeoSpecModule };
@@ -1 +1 @@
1
- import{compileGeoSpecTestNamePattern as e,filterGeoSpecTests as t,matchesGeoSpecTestName as n}from"./filter.mjs";import{GeoSpecAssertionError as r,clearCollectorGlobals as i,createCollector as a,installCollector as o}from"./collector.mjs";import{defaultGeoSpecIgnoredDirectories as s,defaultGeoSpecInclude as c,discoverGeoSpecFiles as l,isGeoSpecTestFile as u}from"./discovery.mjs";import{runGeoSpecModule as d}from"./run-geospec-module.mjs";export{r as GeoSpecAssertionError,i as clearCollectorGlobals,e as compileGeoSpecTestNamePattern,a as createCollector,s as defaultGeoSpecIgnoredDirectories,c as defaultGeoSpecInclude,l as discoverGeoSpecFiles,t as filterGeoSpecTests,o as installCollector,u as isGeoSpecTestFile,n as matchesGeoSpecTestName,d as runGeoSpecModule};
1
+ import{compileGeoSpecTestNamePattern as e,filterGeoSpecTests as t,matchesGeoSpecTestName as n}from"./filter.mjs";import{geoSpecTimeoutCode as r,isGeoSpecTimeoutCode as i}from"./timeout-codes.mjs";import{GeoSpecAssertionError as a,clearCollectorGlobals as o,createCollector as s,installCollector as c}from"./collector.mjs";import{defaultGeoSpecIgnoredDirectories as l,defaultGeoSpecInclude as u,discoverGeoSpecFiles as d,isGeoSpecTestFile as f}from"./discovery.mjs";import{runGeoSpecModule as p}from"./run-geospec-module.mjs";export{a as GeoSpecAssertionError,o as clearCollectorGlobals,e as compileGeoSpecTestNamePattern,s as createCollector,l as defaultGeoSpecIgnoredDirectories,u as defaultGeoSpecInclude,d as discoverGeoSpecFiles,t as filterGeoSpecTests,r as geoSpecTimeoutCode,c as installCollector,f as isGeoSpecTestFile,i as isGeoSpecTimeoutCode,n as matchesGeoSpecTestName,p as runGeoSpecModule};
@@ -1 +1 @@
1
- import{createGeoSpecNativeModelLoader as e}from"../../model/native-model-loader.mjs";import{createSerialGeoSpecRunner as t}from"../worker/serial-runner.mjs";const n=new TextDecoder(`utf-8`,{fatal:!0}),r=(e,t)=>{let r=JSON.parse(n.decode(e));if(typeof r!=`object`||!r||!(`plan`in r))throw TypeError(`Native GeoSpec assertion requires a plan with admitted subjects.`);let{plan:i}=r;if(typeof i!=`object`||!i||!(`subjects`in i)||!Array.isArray(i.subjects)||i.subjects.length===0)throw TypeError(`Native GeoSpec assertion requires admitted subjects.`);for(let e of i.subjects){if(typeof e!=`object`||!e)throw TypeError(`Native GeoSpec assertion requires admitted subjects.`);let n=`subjectHash`in e;if(n===`contentHash`in e)throw TypeError(`Native GeoSpec assertion requires one admitted subject identity.`);let r=n?e.subjectHash:`contentHash`in e?e.contentHash:void 0;if(typeof r!=`string`||!t.has(r))throw TypeError(`Native GeoSpec subject was not admitted by this run.`)}},i=i=>{let a=i.nativeModelLoader??e({...i.model,engine:i.nativeAssertions.engine}),o=new Set,s=Object.assign(async e=>{let t=await a(e);return o.add(t.subjectHash),t},{async releaseAll(){try{await a.releaseAll()}finally{o.clear()}}}),c=i.nativeAssertions.engine,l={evaluateClaim(e){return r(e,o),c.evaluateClaim(e)},processRequest(e){let t=JSON.parse(n.decode(e));return typeof t==`object`&&t&&`method`in t&&t.method===`submitClaims`&&r(e,o),c.processRequest(e)},ingestSubject:(e,t,n)=>c.ingestSubject(e,t,n),subjectHandle:e=>c.subjectHandle(e),releaseSubject:e=>c.releaseSubject(e)},u=t({filesystem:i.filesystem,nativeAssertions:{...i.nativeAssertions,engine:l},nativeModelLoader:s,...i.builtinModules===void 0?{}:{builtinModules:i.builtinModules}});return{...u,async close(){try{await u.close()}finally{o.clear()}}}};export{i as createNativeGeoSpecRunner};
1
+ import{createGeoSpecNativeModelLoader as e}from"../../model/native-model-loader.mjs";import{createSerialGeoSpecRunner as t}from"../worker/serial-runner.mjs";const n=new TextDecoder(`utf-8`,{fatal:!0}),r=(e,t)=>{let r=JSON.parse(n.decode(e));if(typeof r!=`object`||!r||!(`plan`in r))throw TypeError(`Native GeoSpec assertion requires a plan with admitted subjects.`);let{plan:i}=r;if(typeof i!=`object`||!i||!(`subjects`in i)||!Array.isArray(i.subjects)||i.subjects.length===0)throw TypeError(`Native GeoSpec assertion requires admitted subjects.`);for(let e of i.subjects){if(typeof e!=`object`||!e)throw TypeError(`Native GeoSpec assertion requires admitted subjects.`);let n=`subjectHash`in e;if(n===`contentHash`in e)throw TypeError(`Native GeoSpec assertion requires one admitted subject identity.`);let r=n?e.subjectHash:`contentHash`in e?e.contentHash:void 0;if(typeof r!=`string`||!t.has(r))throw TypeError(`Native GeoSpec subject was not admitted by this run.`)}},i=i=>{let a=i.nativeModelLoader??e({...i.model,engine:i.nativeAssertions.engine,...i.nativeAssertions.maximumWorkUnitLimit===void 0?{}:{maximumWorkUnitLimit:i.nativeAssertions.maximumWorkUnitLimit}}),o=new Set,s=Object.assign(async e=>{let t=await a(e);return o.add(t.subjectHash),t},{async releaseAll(){try{await a.releaseAll()}finally{o.clear()}}}),c=i.nativeAssertions.engine,l={evaluateClaim(e){return r(e,o),c.evaluateClaim(e)},processRequest(e){let t=JSON.parse(n.decode(e));return typeof t==`object`&&t&&`method`in t&&t.method===`submitClaims`&&r(e,o),c.processRequest(e)},ingestSubject:(e,t,n)=>c.ingestSubject(e,t,n),subjectHandle:e=>c.subjectHandle(e),releaseSubject:e=>c.releaseSubject(e)},u=t({filesystem:i.filesystem,nativeAssertions:{...i.nativeAssertions,engine:l},nativeModelLoader:s,...i.builtinModules===void 0?{}:{builtinModules:i.builtinModules}});return{...u,async close(){try{await u.close()}finally{o.clear()}}}};export{i as createNativeGeoSpecRunner};
@@ -16,7 +16,7 @@ export const it = (name, fn) => getRunBinding().collector.it(name, fn);
16
16
  it.skip = (name, fn) => getRunBinding().collector.itSkip(name, fn);
17
17
 
18
18
  export const test = it;
19
- export const expectGeo = (subject) => getRunBinding().collector.expectGeo(subject);
19
+ export const expectGeo = (subject, options) => getRunBinding().collector.expectGeo(subject, options);
20
20
  `,C=e=>`
21
21
  ${x(e)}
22
22
  export const GeoSpecModelLoadError = getRunBinding().geoSpecModelLoadError;
@@ -44,4 +44,4 @@ export const createModelLoader = (defaults = {}) => {
44
44
  }
45
45
  return factory(defaults);
46
46
  };
47
- `;async function w(m){let h=a(m.testNamePattern);if(!h.success)return{success:!1,issues:[h.issue]};let g=v(m.filesystem),_=await l({filesystem:g.filesystem}),x=s({...m.matcherWallBackstop===void 0?{}:{matcherWallBackstop:m.matcherWallBackstop},...m.forensic===void 0?{}:{forensic:m.forensic},nativeAssertions:m.nativeAssertions}),w=await y(m),T=f(),E=b(),D=new Set,O=[],k=async()=>{do{let e=[...D];await Promise.allSettled(e);for(let t of e)D.delete(t)}while(D.size>0)},A=m.nativeModelLoader,j=r(m.nativeAssertions),M=!0,N=A===void 0?void 0:n=>{let r=O.length,a=`${T}:load:${r+1}`;O.push({loadId:a,status:`unavailable`});let o=(async()=>{try{let i=await A(n),{load:o,...s}=i,c=t(i),l=o===void 0?void 0:{...structuredClone(o),loadId:a};return O[r]={loadId:a,status:l?.status??`unavailable`,subject:s,...l===void 0?{}:{evidence:l}},e({engine:m.nativeAssertions.engine,client:j,identity:s,...l===void 0?{}:{load:l},isLive:()=>M,...c===void 0?{}:{ensureResident:c}})}catch(e){throw O[r]={loadId:a,status:`failed`,error:e instanceof Error?e.message:String(e),...e instanceof i?{diagnostics:e.diagnostics}:{}},e}})();return D.add(o.catch(()=>void 0)),D.add(o),o};E.set(T,{collector:x,geoSpecModelLoadError:i,...N?{nativeModelLoader:N}:{},...N?{createModelLoader:t=>{if(t.engine!==void 0||t.readSource!==void 0)throw TypeError(`Authored VM model defaults cannot replace the admitting host.`);let r=0,i=new Set,a=new Set,o,s=async i=>{let o=r,s=await N({...t,...i}),c=n(s);return c.lease!==void 0&&a.add(c.lease),e({...c,isLive:()=>c.isLive()&&r===o})},c=e=>{let t=s(e);return i.add(t),t},l=async()=>{do{let e=[...i];await Promise.allSettled(e);for(let t of e)i.delete(t)}while(i.size>0);r+=1;let e=[];for(let t of a)try{t.dispose(),a.delete(t)}catch(t){e.push(t)}if(e.length>0)throw AggregateError(e,`GeoSpec model scope disposal failed.`)};return Object.assign(c,{dispose(){return o??=l().finally(()=>{o=void 0}),o}})}}:{}}),_.registerModule(`geospec`,{version:`0.0.0-poc`,code:S(T)}),_.registerModule(`geospec/model`,{version:`0.0.0-poc`,code:C(T)});for(let[e,t]of Object.entries(m.builtinModules??{}))_.registerModule(e,t);try{let e=w?.bundle??await _.bundle(m.entryPath);if(!e.success)return{success:!1,issues:e.issues,bundle:e};let t=g.reads();m.bundleCache!==void 0&&w===void 0&&t!==void 0&&m.bundleCache.set(m.entryPath,{builtinIdentity:p(m),runToken:T,bundle:e,bundlerReads:t});let n=await _.execute(w===void 0?e.code:e.code.replaceAll(w.runToken,T)),r=async()=>{let t={},n=g.reads()!==void 0;for(let e of w?.bundlerReads??g.observed()){if(e.question===`exists`||typeof e.answer==`boolean`)continue;let r=await c(typeof e.answer==`string`?new TextEncoder().encode(e.answer):e.answer);n&&=t[e.path]===void 0||t[e.path]===`sha256:${r}`,t[e.path]=`sha256:${r}`}let r=new Map(Object.entries(t));for(let e of O){let{evidence:t}=e;for(let e of t?.exportOptions===void 0?t?.artifacts??[]:[])if(e.sourcePath!==void 0){let t=`sha256:${e.sha256}`;n&&=!r.has(e.sourcePath)||r.get(e.sourcePath)===t,r.set(e.sourcePath,t)}if(t?.sourcePath!==void 0&&t.exportOptions===void 0){let e=t.artifacts[0];if(e!==void 0){let i=`sha256:${e.sha256}`;n&&=!r.has(t.sourcePath)||r.get(t.sourcePath)===i,r.set(t.sourcePath,i)}}for(let[t,i]of Object.entries(e.evidence?.sourceRevision?.files??{}))n&&=!r.has(t)||r.get(t)===i,r.set(t,i)}return{status:n?O.some(e=>e.status!==`complete`)?`unavailable`:`complete`:`mixed`,modules:[{entryPath:m.entryPath,bundleSha256:await c(new TextEncoder().encode(w===void 0?e.code:e.code.replaceAll(w.runToken,T))),files:t,consistent:n}],loads:O}},i=e=>({discovered:x.tests.length,selected:e,completed:x.tests.filter(e=>e.status!==`not-run`&&e.status!==`skipped`).length,passed:x.tests.filter(e=>e.status===`passed`).length,failed:x.tests.filter(e=>e.status===`failed`).length,unsupported:x.tests.filter(e=>e.status===`unsupported`).length,inconclusive:x.tests.filter(e=>e.status===`inconclusive`).length,skipped:x.tests.filter(e=>e.status===`skipped`).length,notRun:x.tests.filter(e=>e.status===`not-run`).length});if(!n.success)return await k(),{success:!1,issues:n.issues,bundle:e,tests:x.tests,accounting:i(0),lineage:await r()};if(m.collectOnly===!0)return await x.waitForCompletion(m.testTimeout,/(?!)/u),await k(),{success:!0,passed:!1,tests:x.tests,bundle:e,accounting:i(0),lineage:await r()};await x.waitForCompletion(m.testTimeout,h.pattern),await k();let a=o(x.tests,h.pattern),s=await r();return{success:!0,passed:s.status===`complete`&&a.every(e=>e.status===`passed`||e.status===`skipped`),tests:a,bundle:e,accounting:i(a.length),lineage:s}}finally{await k(),M=!1,E.delete(T),E.size===0&&Reflect.deleteProperty(d,u),_.dispose()}}export{w as runGeoSpecModule};
47
+ `;async function w(m){let h=a(m.testNamePattern);if(!h.success)return{success:!1,issues:[h.issue]};let g=v(m.filesystem),_=await l({filesystem:g.filesystem}),x=s({...m.matcherWallBackstop===void 0?{}:{matcherWallBackstop:m.matcherWallBackstop},...m.forensic===void 0?{}:{forensic:m.forensic},...m.onTestEvent===void 0?{}:{onTestEvent:m.onTestEvent},nativeAssertions:m.nativeAssertions}),w=await y(m),T=f(),E=b(),D=new Set,O=[],k=async()=>{do{let e=[...D];await Promise.allSettled(e);for(let t of e)D.delete(t)}while(D.size>0)},A=m.nativeModelLoader,j=r(m.nativeAssertions),M=!0,N=A===void 0?void 0:n=>{let r=O.length,a=`${T}:load:${r+1}`;O.push({loadId:a,status:`unavailable`});let o=(async()=>{try{let i=await A(n),{load:o,...s}=i,c=t(i),l=o===void 0?void 0:{...structuredClone(o),loadId:a};return O[r]={loadId:a,status:l?.status??`unavailable`,subject:s,...l===void 0?{}:{evidence:l}},e({engine:m.nativeAssertions.engine,client:j,identity:s,...l===void 0?{}:{load:l},isLive:()=>M,...c===void 0?{}:{ensureResident:c}})}catch(e){throw O[r]={loadId:a,status:`failed`,error:e instanceof Error?e.message:String(e),...e instanceof i?{diagnostics:e.diagnostics}:{}},e}})();return D.add(o.catch(()=>void 0)),D.add(o),o};E.set(T,{collector:x,geoSpecModelLoadError:i,...N?{nativeModelLoader:N}:{},...N?{createModelLoader:t=>{if(t.engine!==void 0||t.readSource!==void 0)throw TypeError(`Authored VM model defaults cannot replace the admitting host.`);let r=0,i=new Set,a=new Set,o,s=async i=>{let o=r,s=await N({...t,...i}),c=n(s);return c.lease!==void 0&&a.add(c.lease),e({...c,isLive:()=>c.isLive()&&r===o})},c=e=>{let t=s(e);return i.add(t),t},l=async()=>{do{let e=[...i];await Promise.allSettled(e);for(let t of e)i.delete(t)}while(i.size>0);r+=1;let e=[];for(let t of a)try{t.dispose(),a.delete(t)}catch(t){e.push(t)}if(e.length>0)throw AggregateError(e,`GeoSpec model scope disposal failed.`)};return Object.assign(c,{dispose(){return o??=l().finally(()=>{o=void 0}),o}})}}:{}}),_.registerModule(`geospec`,{version:`0.0.0-poc`,code:S(T)}),_.registerModule(`geospec/model`,{version:`0.0.0-poc`,code:C(T)});for(let[e,t]of Object.entries(m.builtinModules??{}))_.registerModule(e,t);try{let e=w?.bundle??await _.bundle(m.entryPath);if(!e.success)return{success:!1,issues:e.issues,bundle:e};let t=g.reads();m.bundleCache!==void 0&&w===void 0&&t!==void 0&&m.bundleCache.set(m.entryPath,{builtinIdentity:p(m),runToken:T,bundle:e,bundlerReads:t});let n=await _.execute(w===void 0?e.code:e.code.replaceAll(w.runToken,T)),r=async()=>{let t={},n=g.reads()!==void 0;for(let e of w?.bundlerReads??g.observed()){if(e.question===`exists`||typeof e.answer==`boolean`)continue;let r=await c(typeof e.answer==`string`?new TextEncoder().encode(e.answer):e.answer);n&&=t[e.path]===void 0||t[e.path]===`sha256:${r}`,t[e.path]=`sha256:${r}`}let r=new Map(Object.entries(t));for(let e of O){let{evidence:t}=e;for(let e of t?.exportOptions===void 0?t?.artifacts??[]:[])if(e.sourcePath!==void 0){let t=`sha256:${e.sha256}`;n&&=!r.has(e.sourcePath)||r.get(e.sourcePath)===t,r.set(e.sourcePath,t)}if(t?.sourcePath!==void 0&&t.exportOptions===void 0){let e=t.artifacts[0];if(e!==void 0){let i=`sha256:${e.sha256}`;n&&=!r.has(t.sourcePath)||r.get(t.sourcePath)===i,r.set(t.sourcePath,i)}}for(let[t,i]of Object.entries(e.evidence?.sourceRevision?.files??{}))n&&=!r.has(t)||r.get(t)===i,r.set(t,i)}return{status:n?O.some(e=>e.status!==`complete`)?`unavailable`:`complete`:`mixed`,modules:[{entryPath:m.entryPath,bundleSha256:await c(new TextEncoder().encode(w===void 0?e.code:e.code.replaceAll(w.runToken,T))),files:t,consistent:n}],loads:O}},i=e=>({discovered:x.tests.length,selected:e,completed:x.tests.filter(e=>e.status!==`not-run`&&e.status!==`skipped`).length,passed:x.tests.filter(e=>e.status===`passed`).length,failed:x.tests.filter(e=>e.status===`failed`).length,unsupported:x.tests.filter(e=>e.status===`unsupported`).length,inconclusive:x.tests.filter(e=>e.status===`inconclusive`).length,skipped:x.tests.filter(e=>e.status===`skipped`).length,notRun:x.tests.filter(e=>e.status===`not-run`).length});if(!n.success)return await k(),{success:!1,issues:n.issues,bundle:e,tests:x.tests,accounting:i(0),lineage:await r()};if(m.collectOnly===!0)return await x.waitForCompletion(m.testTimeout,/(?!)/u),await k(),{success:!0,passed:!1,tests:x.tests,bundle:e,accounting:i(0),lineage:await r()};await x.waitForCompletion(m.testTimeout,h.pattern),await k();let a=o(x.tests,h.pattern),s=await r();return{success:!0,passed:s.status===`complete`&&a.every(e=>e.status===`passed`||e.status===`skipped`),tests:a,bundle:e,accounting:i(a.length),lineage:s}}finally{await k(),M=!1,E.delete(T),E.size===0&&Reflect.deleteProperty(d,u),_.dispose()}}export{w as runGeoSpecModule};
@@ -0,0 +1,45 @@
1
+ //#region src/runner/timeout-codes.d.ts
2
+ /**
3
+ * One timeout vocabulary for every GeoSpec host.
4
+ *
5
+ * GeoSpec has three wall-clock bounds, and each host reports the ones it
6
+ * applies with the same code, so a spec that times out reads the same whether
7
+ * it ran in the browser worker, the Node pool or the CLI:
8
+ *
9
+ * | Code | Bound | Set by | Hosts |
10
+ * | ----------------------- | --------------------------------------------- | --------------------------------------------- | --------------------- |
11
+ * | `GEOSPEC_TEST_TIMEOUT` | One test's async body | `test_model.testTimeout`, CLI `--test-timeout` | Every host |
12
+ * | `GEOSPEC_SHARD_TIMEOUT` | One pool shard; its worker is terminated | CLI `--shard-timeout`, pool `shardTimeout` | Node pool |
13
+ * | `GEOSPEC_RUN_TIMEOUT` | One whole run; completed requirements survive | The host's run budget | Browser worker runner |
14
+ *
15
+ * None of them is a geometry verdict: a timed-out requirement is never a pass,
16
+ * and the deterministic work-unit budget inside the engine (`MATCHER_TIMEOUT`)
17
+ * is a separate, non-wall-clock refusal.
18
+ *
19
+ * @module
20
+ */
21
+ /**
22
+ * Codes for GeoSpec wall-clock timeouts, one per bound.
23
+ *
24
+ * @public
25
+ */
26
+ declare const geoSpecTimeoutCode: {
27
+ /** A test's async body outlived `testTimeout` / `--test-timeout`. */
28
+ readonly test: "GEOSPEC_TEST_TIMEOUT";
29
+ /** A pool shard outlived the non-verdict `--shard-timeout` watchdog. */
30
+ readonly shard: "GEOSPEC_SHARD_TIMEOUT";
31
+ /** A whole run outlived its host run budget. */
32
+ readonly run: "GEOSPEC_RUN_TIMEOUT";
33
+ };
34
+ /** One GeoSpec timeout code. @public */
35
+ type GeoSpecTimeoutCode = (typeof geoSpecTimeoutCode)[keyof typeof geoSpecTimeoutCode];
36
+ /**
37
+ * Whether a diagnostic or issue code belongs to the GeoSpec timeout family.
38
+ *
39
+ * @param code - Any diagnostic or issue code.
40
+ * @returns True for {@link geoSpecTimeoutCode} values.
41
+ * @public
42
+ */
43
+ declare const isGeoSpecTimeoutCode: (code: string) => code is GeoSpecTimeoutCode;
44
+ //#endregion
45
+ export { GeoSpecTimeoutCode, geoSpecTimeoutCode, isGeoSpecTimeoutCode };
@@ -0,0 +1 @@
1
+ const e={test:`GEOSPEC_TEST_TIMEOUT`,shard:`GEOSPEC_SHARD_TIMEOUT`,run:`GEOSPEC_RUN_TIMEOUT`},t=new Set(Object.values(e)),n=e=>t.has(e);export{e as geoSpecTimeoutCode,n as isGeoSpecTimeoutCode};
@@ -1,5 +1,6 @@
1
1
  import { GeometryDiagnostic, Vec3 } from "../mesh/types.mjs";
2
2
  import { GeometrySelector } from "../selector/types.mjs";
3
+ import { GeoSpecLoadRegion } from "../model/types.mjs";
3
4
  import { GeoSpecModelLoadEvidence, GeoSpecNativeModelLoader } from "../model/native-model-loader.mjs";
4
5
  import { GeoSpecCanonicalClaimReport, GeoSpecNativeSubject } from "../engine/client.mjs";
5
6
  import { GeoSpecAssertionClientOptions } from "../assertion-client/client.mjs";
@@ -132,6 +133,20 @@ type GeoSpecMassExpectation = {
132
133
  density?: number;
133
134
  tolerance?: number;
134
135
  };
136
+ /**
137
+ * Per-chain options accepted by `expectGeo(subject, options)`.
138
+ *
139
+ * @public
140
+ */
141
+ type GeoSpecExpectOptions = {
142
+ /**
143
+ * Deterministic work-unit budget for every claim in this chain: a positive integer no
144
+ * greater than the host's maximum (default `geoSpecMaximumWorkUnitLimit`, 512 000 000). Omitted uses the subject's
145
+ * `loadModel` budget, then the host's, then the engine default. Raise it to retry a
146
+ * `MATCHER_TIMEOUT` refusal whose `details.used` exceeded `details.limit`.
147
+ */
148
+ readonly workUnitLimit?: number;
149
+ };
135
150
  /**
136
151
  * Center-of-mass expectation accepted by
137
152
  * `expectGeo(...).toHaveCenterOfMass(...)`.
@@ -139,6 +154,7 @@ type GeoSpecMassExpectation = {
139
154
  * @public
140
155
  */
141
156
  type GeoSpecCenterOfMassExpectation = {
157
+ /** Exact expected point in millimetres; per-axis ranges such as `{ greaterThan }` are not supported. */
142
158
  point: GeoSpecPointExpectation;
143
159
  tolerance?: number;
144
160
  };
@@ -579,6 +595,11 @@ type GeoSpecAssertion = {
579
595
  report?: GeoSpecCanonicalClaimReport;
580
596
  /** The host load which admitted this assertion's subject; independent of equal geometry hashes. */
581
597
  loadId?: string;
598
+ /**
599
+ * The crop this assertion's subject was loaded with. Present only for a region load: the verdict
600
+ * holds for this millimetre box, never for the whole model.
601
+ */
602
+ region?: GeoSpecLoadRegion;
582
603
  /** Wall-clock cost of matcher evaluation in milliseconds (R1: budgeted matchers only). */
583
604
  durationMs?: number;
584
605
  };
@@ -665,6 +686,16 @@ type GeoSpecTestCase = {
665
686
  /** Wall-clock cost of the test body plus its pending assertions, in milliseconds (R1). */
666
687
  durationMs?: number;
667
688
  };
689
+ /**
690
+ * A selected test starting or settling inside a module run. Observers see the
691
+ * live test case; an observer error never changes its verdict.
692
+ *
693
+ * @public
694
+ */
695
+ type GeoSpecTestEvent = {
696
+ readonly type: 'test-start' | 'test-complete';
697
+ readonly test: GeoSpecTestCase;
698
+ };
668
699
  /**
669
700
  * Options for executing a GeoSpec ESM test module.
670
701
  *
@@ -683,6 +714,8 @@ type RunGeoSpecModuleOptions = {
683
714
  matcherWallBackstop?: number;
684
715
  /** Emit structured forensic events for this run. */
685
716
  forensic?: boolean;
717
+ /** Observes each selected test as it starts and settles, for host progress reporting. */
718
+ onTestEvent?: (event: GeoSpecTestEvent) => void;
686
719
  /**
687
720
  * Host-provided protocol-3 compiled assertion client. The host owns engine and
688
721
  * admitted subject lifetimes and supplies bindings through builtinModules;
@@ -740,4 +773,4 @@ type GeoSpecRunFailure = {
740
773
  */
741
774
  type GeoSpecRunResult = GeoSpecRunSuccess | GeoSpecRunFailure;
742
775
  //#endregion
743
- export { GeoSpecAssemblyOccurrenceExpectation, GeoSpecAssemblyOccurrencesExpectation, GeoSpecAssertion, GeoSpecAxisExpectation, GeoSpecBoundingBoxExpectation, GeoSpecCenterOfMassExpectation, GeoSpecChamferFeatureExpectation, GeoSpecCircularHoleExpectation, GeoSpecCircularHolePatternExpectation, GeoSpecComponentInterferenceAllowance, GeoSpecComponentInterferenceExpectation, GeoSpecComponentInterferencePairExpectation, GeoSpecComponentSelector, GeoSpecConnectedComponentsExpectation, GeoSpecCylindricalFaceExpectation, GeoSpecFilletFeatureExpectation, GeoSpecGeometrySelector, GeoSpecMassExpectation, GeoSpecMatcher, GeoSpecMeshIntegrityExpectation, GeoSpecMinimumWallThicknessExpectation, GeoSpecModuleBundleCache, GeoSpecNoDiagnosticsExpectation, GeoSpecNumericExpectation, GeoSpecPlanarFaceExpectation, GeoSpecPointExpectation, GeoSpecProductStructureExpectation, GeoSpecRunFailure, GeoSpecRunLineage, GeoSpecRunResult, GeoSpecRunSuccess, GeoSpecSpatialRelationshipExpectation, GeoSpecSpatialRelationshipsExpectation, GeoSpecStepUnitsExpectation, GeoSpecSurfaceAreaExpectation, GeoSpecTestAccounting, GeoSpecTestCase, GeoSpecTestStatus, GeoSpecTopologyCountsExpectation, GeoSpecValidBrepExpectation, GeoSpecVoidContinuityExpectation, GeoSpecVoidWaypoint, GeoSpecVolumeExpectation, RunGeoSpecModuleOptions };
776
+ export { GeoSpecAssemblyOccurrenceExpectation, GeoSpecAssemblyOccurrencesExpectation, GeoSpecAssertion, GeoSpecAxisExpectation, GeoSpecBoundingBoxExpectation, GeoSpecCenterOfMassExpectation, GeoSpecChamferFeatureExpectation, GeoSpecCircularHoleExpectation, GeoSpecCircularHolePatternExpectation, GeoSpecComponentInterferenceAllowance, GeoSpecComponentInterferenceExpectation, GeoSpecComponentInterferencePairExpectation, GeoSpecComponentSelector, GeoSpecConnectedComponentsExpectation, GeoSpecCylindricalFaceExpectation, GeoSpecExpectOptions, GeoSpecFilletFeatureExpectation, GeoSpecGeometrySelector, GeoSpecMassExpectation, GeoSpecMatcher, GeoSpecMeshIntegrityExpectation, GeoSpecMinimumWallThicknessExpectation, GeoSpecModuleBundleCache, GeoSpecNoDiagnosticsExpectation, GeoSpecNumericExpectation, GeoSpecPlanarFaceExpectation, GeoSpecPointExpectation, GeoSpecProductStructureExpectation, GeoSpecRunFailure, GeoSpecRunLineage, GeoSpecRunResult, GeoSpecRunSuccess, GeoSpecSpatialRelationshipExpectation, GeoSpecSpatialRelationshipsExpectation, GeoSpecStepUnitsExpectation, GeoSpecSurfaceAreaExpectation, GeoSpecTestAccounting, GeoSpecTestCase, GeoSpecTestEvent, GeoSpecTestStatus, GeoSpecTopologyCountsExpectation, GeoSpecValidBrepExpectation, GeoSpecVoidContinuityExpectation, GeoSpecVoidWaypoint, GeoSpecVolumeExpectation, RunGeoSpecModuleOptions };
@@ -1,5 +1,5 @@
1
1
  import { ManagedGeoSpecNativeModelLoader } from "../../model/native-model-loader.mjs";
2
- import { GeoSpecRunResult, GeoSpecTestAccounting, RunGeoSpecModuleOptions } from "../types.mjs";
2
+ import { GeoSpecRunResult, GeoSpecTestAccounting, GeoSpecTestCase, RunGeoSpecModuleOptions } from "../types.mjs";
3
3
  import { VmFileSystem, VmIssue } from "@taucad/esbuild/vm";
4
4
  //#region src/runner/worker/runner-types.d.ts
5
5
  /**
@@ -53,8 +53,15 @@ type GeoSpecRunnerAccounting = GeoSpecTestAccounting & {
53
53
  cancelled: boolean;
54
54
  bailed: boolean;
55
55
  };
56
+ /** Identity of a test that has started but not settled. @public */
57
+ type GeoSpecRunnerTestRef = {
58
+ readonly suite: readonly string[];
59
+ readonly name: string;
60
+ readonly ordinal?: number;
61
+ };
56
62
  /**
57
- * Lifecycle event emitted by GeoSpec worker-style runners.
63
+ * Lifecycle event emitted by GeoSpec worker-style runners. Test events are
64
+ * emitted by in-process runners; a pool reports per file.
58
65
  *
59
66
  * @public
60
67
  */
@@ -64,6 +71,14 @@ type GeoSpecRunnerEvent = {
64
71
  } | {
65
72
  type: 'file-start';
66
73
  file: string;
74
+ } | {
75
+ type: 'test-start';
76
+ file: string;
77
+ test: GeoSpecRunnerTestRef;
78
+ } | {
79
+ type: 'test-complete';
80
+ file: string;
81
+ test: GeoSpecTestCase;
67
82
  } | {
68
83
  type: 'file-complete';
69
84
  file: string;
@@ -102,6 +117,12 @@ type GeoSpecRunnerRunOptions = {
102
117
  testTimeout?: number;
103
118
  /** Non-verdict matcher wall backstop. Milliseconds. */
104
119
  matcherWallBackstop?: number;
120
+ /**
121
+ * Engine work-unit budget for this run's claims, replacing the runner's
122
+ * `nativeAssertions.workUnitLimit`. A `loadModel` or `expectGeo` budget still
123
+ * wins for its own claims.
124
+ */
125
+ workUnitLimit?: number;
105
126
  /** Emit structured forensic measurements for this run. */
106
127
  forensic?: boolean;
107
128
  /**
@@ -143,4 +164,4 @@ type GeoSpecRunner = {
143
164
  close(): Promise<void>;
144
165
  };
145
166
  //#endregion
146
- export { GeoSpecForensicEvent, GeoSpecRunner, GeoSpecRunnerAccounting, GeoSpecRunnerEvent, GeoSpecRunnerFileResult, GeoSpecRunnerOptions, GeoSpecRunnerResult, GeoSpecRunnerRunOptions };
167
+ export { GeoSpecForensicEvent, GeoSpecRunner, GeoSpecRunnerAccounting, GeoSpecRunnerEvent, GeoSpecRunnerFileResult, GeoSpecRunnerOptions, GeoSpecRunnerResult, GeoSpecRunnerRunOptions, GeoSpecRunnerTestRef };
@@ -1 +1 @@
1
- import{runGeoSpecModule as e}from"../run-geospec-module.mjs";import{createNoMatchingGeoSpecTestsIssue as t}from"./no-matching-tests-issue.mjs";const n=()=>({code:`GEOSPEC_RUNNER_CLOSED`,message:`GeoSpec runner is closed.`,severity:`error`,type:`runtime`}),r=()=>({code:`GEOSPEC_RUNNER_ACTIVE`,message:`GeoSpec runner already has an active run.`,severity:`error`,type:`runtime`}),i=e=>({code:`GEOSPEC_RUNNER_ABORTED`,message:e?`GeoSpec run aborted: ${e}`:`GeoSpec run aborted.`,severity:`error`,type:`runtime`}),a=e=>({code:`GEOSPEC_RUNNER_BAILED`,message:`GeoSpec run stopped after first failure (--bail): ${e}. Remaining files were not executed.`,severity:`error`,type:`runtime`}),o=e=>{let t=0,n=0;for(let r of e)r.status!==`skipped`&&(r.status===`failed`?n+=1:r.status===`passed`&&(t+=1));return{passed:t,failed:n}},s=s=>{let c={closed:!1},l=new Set,u,d,f=new Map,p=e=>{for(let t of l)t(e)},m=()=>c.aborted,h=e=>({requestedFiles:[...e],completedFiles:[],notRunFiles:[...e],discoveryComplete:!1,discovered:0,selected:0,completed:0,passed:0,failed:0,unsupported:0,inconclusive:0,skipped:0,notRun:0,cancelled:!1,bailed:!1}),g=(e,t)=>({success:!1,passed:0,failed:1,selectedTests:0,files:[],issues:[e],accounting:h(t),lineageStatus:`unavailable`}),_=async n=>{let r=[...n.files],c=performance.now();p({type:`run-start`,files:r});let l=!1,u=async()=>{l||(l=!0,await s.nativeModelLoader?.releaseAll())},d=0,g=0,_=0,v=[],y=[],b=h(r),x=!0,S=`complete`,C=new Map;try{for(let t of r){let c=m();if(c!==void 0){let e=i(c);y.push(e),g+=1,p({type:`abort`,reason:c}),b.cancelled=!0,x=!1;break}p({type:`file-start`,file:t});let l=performance.now(),u=await e({filesystem:s.filesystem,entryPath:t,testNamePattern:n.testNamePattern,testTimeout:n.testTimeout,matcherWallBackstop:n.matcherWallBackstop,forensic:n.forensic,bundleCache:f,nativeAssertions:s.nativeAssertions,...s.nativeModelLoader?{nativeModelLoader:s.nativeModelLoader}:{},...s.builtinModules?{builtinModules:s.builtinModules}:{}}).catch(e=>({success:!1,issues:[{code:`GEOSPEC_MODULE_INTERRUPTED`,message:e instanceof Error?e.message:String(e),severity:`error`,type:`runtime`}]})),h=performance.now()-l;p({type:`file-complete`,file:t,result:u,durationMs:h}),v.push({file:t,result:u,durationMs:h}),b.completedFiles=v.map(e=>e.file),b.notRunFiles=r.slice(v.length),x&&=u.success&&u.passed,u.lineage===void 0||u.lineage.status===`unavailable`?S!==`mixed`&&(S=`unavailable`):u.lineage.status===`mixed`&&(S=`mixed`);let w=u.lineage?.loads.flatMap(e=>{let{evidence:t}=e,n=t?.artifacts[0];return t?.exportOptions===void 0?[...(t?.artifacts??[]).flatMap(e=>e.sourcePath===void 0?[]:[{[e.sourcePath]:`sha256:${e.sha256}`}]),...t?.sourcePath===void 0||n===void 0?[]:[{[t.sourcePath]:`sha256:${n.sha256}`}]]:[]})??[];for(let e of[...u.lineage?.modules.map(e=>e.files)??[],...w,...u.lineage?.loads.map(e=>e.evidence?.sourceRevision?.files??{})??[]])for(let[t,n]of Object.entries(e))C.has(t)&&C.get(t)!==n&&(S=`mixed`),C.set(t,n);if(u.accounting!==void 0)for(let e of[`discovered`,`selected`,`completed`,`passed`,`failed`,`unsupported`,`inconclusive`,`skipped`,`notRun`])b[e]+=u.accounting[e];if(u.success){_+=u.tests.length;let e=o(u.tests);d+=e.passed,g+=e.failed}else g+=1;if(n.bail===!0&&(!x||g>0)){y.push(a(t)),b.bailed=!0;break}}_===0&&g===0&&(y.push(t()),g+=1);try{await u()}catch(e){y.push({code:`GEOSPEC_NATIVE_CLEANUP_FAILED`,message:e instanceof Error?e.message:String(e),severity:`error`,type:`runtime`}),x=!1}b.discoveryComplete=b.notRunFiles.length===0&&v.every(e=>e.result.success&&e.result.accounting!==void 0);let l={success:x&&S===`complete`&&g===0&&y.length===0,passed:d,failed:g,selectedTests:_,files:v,...y.length>0?{issues:y}:{},durationMs:performance.now()-c,accounting:b,lineageStatus:S};return p({type:`run-complete`,result:l}),l}finally{await u()}};return{async run(e){if(new Set(e.files).size!==e.files.length)return g({code:`GEOSPEC_DUPLICATE_FILES`,message:`GeoSpec requested files must be unique.`,severity:`error`,type:`runtime`},e.files);if(c.closed)return g(n(),e.files);if(u!==void 0)return g(r(),e.files);delete c.aborted;let t=()=>void 0,i=new Promise(e=>{t=e});u=i;try{return await _(e)}finally{t(),u===i&&(u=void 0)}},on(e,t){let n=n=>{n.type===e&&t(n)};return l.add(n),()=>l.delete(n)},abort(e){c.aborted=e??`requested`},async close(){d===void 0&&(c.closed=!0,c.aborted??=`runner closed`,d=(async()=>{await u,f.clear(),p({type:`close`})})()),await d}}};export{o as countRunnerTests,s as createSerialGeoSpecRunner};
1
+ import{runGeoSpecModule as e}from"../run-geospec-module.mjs";import{createNoMatchingGeoSpecTestsIssue as t}from"./no-matching-tests-issue.mjs";const n=()=>({code:`GEOSPEC_RUNNER_CLOSED`,message:`GeoSpec runner is closed.`,severity:`error`,type:`runtime`}),r=()=>({code:`GEOSPEC_RUNNER_ACTIVE`,message:`GeoSpec runner already has an active run.`,severity:`error`,type:`runtime`}),i=e=>({code:`GEOSPEC_RUNNER_ABORTED`,message:e?`GeoSpec run aborted: ${e}`:`GeoSpec run aborted.`,severity:`error`,type:`runtime`}),a=e=>({code:`GEOSPEC_RUNNER_BAILED`,message:`GeoSpec run stopped after first failure (--bail): ${e}. Remaining files were not executed.`,severity:`error`,type:`runtime`}),o=e=>{let t=0,n=0;for(let r of e)r.status!==`skipped`&&(r.status===`failed`?n+=1:r.status===`passed`&&(t+=1));return{passed:t,failed:n}},s=s=>{let c={closed:!1},l=new Set,u,d,f=new Map,p=e=>{for(let t of l)t(e)},m=()=>c.aborted,h=e=>({requestedFiles:[...e],completedFiles:[],notRunFiles:[...e],discoveryComplete:!1,discovered:0,selected:0,completed:0,passed:0,failed:0,unsupported:0,inconclusive:0,skipped:0,notRun:0,cancelled:!1,bailed:!1}),g=(e,t)=>({success:!1,passed:0,failed:1,selectedTests:0,files:[],issues:[e],accounting:h(t),lineageStatus:`unavailable`}),_=async n=>{let r=[...n.files],c=performance.now();p({type:`run-start`,files:r});let l=!1,u=async()=>{l||(l=!0,await s.nativeModelLoader?.releaseAll())},d=0,g=0,_=0,v=[],y=[],b=h(r),x=!0,S=`complete`,C=new Map;try{for(let t of r){let c=m();if(c!==void 0){let e=i(c);y.push(e),g+=1,p({type:`abort`,reason:c}),b.cancelled=!0,x=!1;break}p({type:`file-start`,file:t});let l=performance.now(),u=await e({filesystem:s.filesystem,entryPath:t,testNamePattern:n.testNamePattern,testTimeout:n.testTimeout,matcherWallBackstop:n.matcherWallBackstop,forensic:n.forensic,onTestEvent:e=>{p(e.type===`test-start`?{type:`test-start`,file:t,test:{suite:[...e.test.suite],name:e.test.name,...e.test.ordinal===void 0?{}:{ordinal:e.test.ordinal}}}:{type:`test-complete`,file:t,test:e.test})},bundleCache:f,nativeAssertions:n.workUnitLimit===void 0?s.nativeAssertions:{...s.nativeAssertions,workUnitLimit:n.workUnitLimit},...s.nativeModelLoader?{nativeModelLoader:s.nativeModelLoader}:{},...s.builtinModules?{builtinModules:s.builtinModules}:{}}).catch(e=>({success:!1,issues:[{code:`GEOSPEC_MODULE_INTERRUPTED`,message:e instanceof Error?e.message:String(e),severity:`error`,type:`runtime`}]})),h=performance.now()-l;p({type:`file-complete`,file:t,result:u,durationMs:h}),v.push({file:t,result:u,durationMs:h}),b.completedFiles=v.map(e=>e.file),b.notRunFiles=r.slice(v.length),x&&=u.success&&u.passed,u.lineage===void 0||u.lineage.status===`unavailable`?S!==`mixed`&&(S=`unavailable`):u.lineage.status===`mixed`&&(S=`mixed`);let w=u.lineage?.loads.flatMap(e=>{let{evidence:t}=e,n=t?.artifacts[0];return t?.exportOptions===void 0?[...(t?.artifacts??[]).flatMap(e=>e.sourcePath===void 0?[]:[{[e.sourcePath]:`sha256:${e.sha256}`}]),...t?.sourcePath===void 0||n===void 0?[]:[{[t.sourcePath]:`sha256:${n.sha256}`}]]:[]})??[];for(let e of[...u.lineage?.modules.map(e=>e.files)??[],...w,...u.lineage?.loads.map(e=>e.evidence?.sourceRevision?.files??{})??[]])for(let[t,n]of Object.entries(e))C.has(t)&&C.get(t)!==n&&(S=`mixed`),C.set(t,n);if(u.accounting!==void 0)for(let e of[`discovered`,`selected`,`completed`,`passed`,`failed`,`unsupported`,`inconclusive`,`skipped`,`notRun`])b[e]+=u.accounting[e];if(u.success){_+=u.tests.length;let e=o(u.tests);d+=e.passed,g+=e.failed}else g+=1;if(n.bail===!0&&(!x||g>0)){y.push(a(t)),b.bailed=!0;break}}_===0&&g===0&&(y.push(t()),g+=1);try{await u()}catch(e){y.push({code:`GEOSPEC_NATIVE_CLEANUP_FAILED`,message:e instanceof Error?e.message:String(e),severity:`error`,type:`runtime`}),x=!1}b.discoveryComplete=b.notRunFiles.length===0&&v.every(e=>e.result.success&&e.result.accounting!==void 0);let l={success:x&&S===`complete`&&g===0&&y.length===0,passed:d,failed:g,selectedTests:_,files:v,...y.length>0?{issues:y}:{},durationMs:performance.now()-c,accounting:b,lineageStatus:S};return p({type:`run-complete`,result:l}),l}finally{await u()}};return{async run(e){if(new Set(e.files).size!==e.files.length)return g({code:`GEOSPEC_DUPLICATE_FILES`,message:`GeoSpec requested files must be unique.`,severity:`error`,type:`runtime`},e.files);if(c.closed)return g(n(),e.files);if(u!==void 0)return g(r(),e.files);delete c.aborted;let t=()=>void 0,i=new Promise(e=>{t=e});u=i;try{return await _(e)}finally{t(),u===i&&(u=void 0)}},on(e,t){let n=n=>{n.type===e&&t(n)};return l.add(n),()=>l.delete(n)},abort(e){c.aborted=e??`requested`},async close(){d===void 0&&(c.closed=!0,c.aborted??=`runner closed`,d=(async()=>{await u,f.clear(),p({type:`close`})})()),await d}}};export{o as countRunnerTests,s as createSerialGeoSpecRunner};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geospec",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.4",
4
4
  "description": "Geometry specification API for CAD models: DSL, selector language, diagnostics schema, and the native runner integration",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -96,9 +96,9 @@
96
96
  "imports": {},
97
97
  "dependencies": {
98
98
  "zod": "4.4.3",
99
- "@taucad/esbuild": "0.1.0-beta.2",
100
- "@taucad/project-core": "0.1.0-beta.2",
101
- "@taucad/runtime": "0.1.0-beta.2"
99
+ "@taucad/project-core": "0.1.0-beta.4",
100
+ "@taucad/runtime": "0.1.0-beta.4",
101
+ "@taucad/esbuild": "0.1.0-beta.4"
102
102
  },
103
103
  "devDependencies": {
104
104
  "@gltf-transform/core": "^4.5.1"