@antprofuse/saddle-islands-tools 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antprofuse/saddle-islands-tools",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Independent Saddle–Islands engineering tools",
5
5
  "files": [
6
6
  "tools"
@@ -17,7 +17,7 @@ async function fixture(){
17
17
  await writeFile(path.join(project,'harness/regions/alternate.mjs'),"import {semanticLocator} from '@antprofuse/saddle-islands-tools/tools/modules/minifish/harness/semantic-anchor.mjs';export function locate(root,name){return semanticLocator(root,name)};export function assertTarget(){}");
18
18
  return {root,project,configFile,config,loaded:await loadProject(project,configFile)};
19
19
  }
20
- test('alternate project static closure records tool/adapter separately and copies no unrelated region',async()=>{const f=await fixture();try{const output=path.join(f.root,'artifact'),records=await packageRunner(f.loaded,output,'regions/alternate.mjs');assert(records.some(r=>r.kind==='tool'&&r.path==='harness/semantic-anchor.mjs'));for(const name of ['visual-expectation.mjs','visual-carrier.mjs']){assert(records.some(r=>r.kind==='tool'&&r.path==='harness/'+name));assert((await readFile(path.join(output,'tools/harness',name),'utf8')).length>0);}assert(records.some(r=>r.kind==='adapter'&&r.path==='harness/regions/alternate.mjs'));assert(records.every(r=>/^[a-f0-9]{64}$/.test(r.sha256)));assert(!records.some(r=>r.path.includes('T1')));assert((await readFile(path.join(output,'adapters/harness/regions/alternate.mjs'),'utf8')).includes('../../../tools/harness/semantic-anchor.mjs'));}finally{await rm(f.root,{recursive:true,force:true});}});
20
+ test('alternate project static closure records tool/adapter separately and copies no unrelated region',async()=>{const f=await fixture();try{const output=path.join(f.root,'artifact'),records=await packageRunner(f.loaded,output,'regions/alternate.mjs');assert(records.some(r=>r.kind==='tool'&&r.path==='harness/semantic-anchor.mjs'));for(const name of ['visual-expectation.mjs','visual-carrier.mjs','jsapi.mjs','browser.mjs']){assert(records.some(r=>r.kind==='tool'&&r.path==='harness/'+name));assert((await readFile(path.join(output,'tools/harness',name),'utf8')).length>0);}assert(records.some(r=>r.kind==='adapter'&&r.path==='harness/regions/alternate.mjs'));assert(records.every(r=>/^[a-f0-9]{64}$/.test(r.sha256)));assert(!records.some(r=>r.path.includes('T1')));assert((await readFile(path.join(output,'adapters/harness/regions/alternate.mjs'),'utf8')).includes('../../../tools/harness/semantic-anchor.mjs'));}finally{await rm(f.root,{recursive:true,force:true});}});
21
21
  for(const [name,code] of [['unknown package',"import 'fs';"],['dependency traversal',"import '../../outside.mjs';"],['direct DOM',"export const read=root=>root.evaluate(()=>1);"],['dynamic import',"export const read=()=>import('./other.mjs');"]])test(name+' is rejected from the closed observation dependency graph',async()=>{const f=await fixture();try{await writeFile(path.join(f.project,'harness/regions/alternate.mjs'),code);await writeFile(path.join(f.project,'outside.mjs'),'export const leaked=true;');await assert.rejects(packageRunner(f.loaded,path.join(f.root,'artifact'),'regions/alternate.mjs'));}finally{await rm(f.root,{recursive:true,force:true});}});
22
22
  test('symlink aliases, undeclared module, missing config, and package output are rejected',async()=>{const f=await fixture();try{const module=path.join(f.project,'harness/regions/alternate.mjs');await rm(module);await writeFile(path.join(f.project,'harness/other.mjs'),'export const safe=true;');await symlink('../other.mjs',module);await assert.rejects(packageRunner(f.loaded,path.join(f.root,'alias'),'regions/alternate.mjs'),/Symlink/);await assert.rejects(packageRunner(f.loaded,path.join(f.root,'unknown'),'regions/unknown.mjs'),/declared module/);await assert.rejects(loadProject(f.project,path.join(f.project,'missing.json')));await assert.rejects(packageRunner(f.loaded,path.join(f.project,'artifact'),'regions/alternate.mjs'),/outside/);}finally{await rm(f.root,{recursive:true,force:true});}});
23
23
  test('generic composition preserves exact preparation and original ID mappings without project names',()=>{const noop=()=>{},policy={validateSemanticTargets:noop,supplementalDefinition:()=>null,officialTitle:()=>false,validatePreparationOverrides:noop,validateFixtureTypes:noop,validateSpecialBoundaries:noop,validateIndependentFixture:noop,validatePacketPlan:noop,validatePluginEvents:noop,validatePluginAssertionIdentity:noop,validateObservationModuleName:noop};const catalog={cases:[{id:'alternate / page / value / content',title:'value'}]},definition={caseId:catalog.cases[0].id,ancestors:['open'],basis:[{path:'alternate/source.yaml'}],preparation:{},assertions:[{id:'value',kind:'text',target:'alternate / page / value',expected:'42',count:1}]},fragment={id:'open',inputs:{query:{key:'42'}},boundaries:[],actions:[{kind:'open-page'}]};const plan=compileCase(catalog,definition,[fragment],policy);assert.deepEqual(plan.inputs,fragment.inputs);assert.deepEqual(plan.assertions,definition.assertions);assert.equal(plan.case.id,definition.caseId);assert.throws(()=>compileCase(catalog,{...definition,ancestors:['unknown']},[fragment],policy),/missing preparation/);});
@@ -9,7 +9,7 @@ import {pluginPackagePlan} from './plugin-package.mjs';
9
9
  import {installHostLanguage} from './host-language.mjs';
10
10
  import {installResponseConsumption} from './response-consumption.mjs';
11
11
  import { fulfillRpc } from './rpc-wire.mjs';
12
- import { bridgePath, jsapiPath, bridgeScript, fulfillJsapi, businessJsapiOption } from './jsapi.mjs';
12
+ import { bridgePath, jsapiPath, jsapiDeliveryPath, acknowledgeJsapiDelivery, bridgeScript, fulfillJsapi, businessJsapiOption } from './jsapi.mjs';
13
13
 
14
14
  // Official Lyra keeps earlier pages painted in the same simulator stack.
15
15
  // The last non-exiting sibling is the active shell; route name alone is ambiguous.
@@ -54,6 +54,7 @@ export async function openMinifish({ page, plan, query, simulatorUrl, pageRoute,
54
54
  const request = route.request(), target = new URL(request.url());
55
55
  try {
56
56
  if (target.origin === url.origin && target.pathname === bridgePath) return await route.fulfill({contentType:'application/javascript',body:bridgeScript(url.origin,nativeApis,Boolean(process.env.MINIFISH_LOADING_DIAGNOSTICS_DIR),officialUi,process.env.MINIFISH_CALLBACK_CORRELATION==='1')});
57
+ if(target.origin===url.origin&&target.pathname===jsapiDeliveryPath)return await acknowledgeJsapiDelivery(route,plan);
57
58
  if (target.origin === url.origin && target.pathname === jsapiPath) {const wire=request.postDataJSON();callbackCorrelation?.transport(wire,request.frame());observeLocalNavigationCandidate(page,wire);if(officialUi.includes(wire.api)){plan.recordNativeUiRequest('jsapi:'+wire.api,businessJsapiOption(wire.option),wire);return await route.fulfill({status:204});}if(delegateLocalNavigation(page,wire))return await route.fulfill({status:204});return await fulfillJsapi(route, plan);}
58
59
  if(target.origin===url.origin&&target.pathname===nativeUiResultPath){assert.equal(request.method(),'POST');const receipt=request.postDataJSON();plan.recordNativeUiResult('jsapi:'+receipt.api,businessJsapiOption(receipt.option),receipt);return await route.fulfill({status:204});}
59
60
  if (target.origin === url.origin && target.pathname.startsWith('/lyraParams/')) {
@@ -1,3 +1,4 @@
1
+ import {randomUUID} from 'node:crypto';
1
2
  import {hostDeviceFixture,configureHostDeviceMount,hostDeviceMountEvidence} from './host-device-fixture.mjs';
2
3
  import assert from 'node:assert/strict';
3
4
  import {nativeUiResultPath,nativeUiApis} from './native-ui-contract.mjs';
@@ -5,6 +6,9 @@ import {nativeUiResultPath,nativeUiApis} from './native-ui-contract.mjs';
5
6
  // Captures native calls without reading or altering product business state.
6
7
  export const bridgePath = '/__bill_tests__/bridge.js';
7
8
  export const jsapiPath = '/__bill_tests__/jsapi';
9
+ export const jsapiDeliveryPath = '/__bill_tests__/jsapi-delivery';
10
+ const deliveryPlans = new WeakMap();
11
+ const deliveryHeader = 'x-minifish-response-id';
8
12
  export function bridgeScript(origin, additionalApis = [], observeLoading = false, officialUi = [], observeCorrelation = false) {
9
13
  assert(!additionalApis.includes('pageScrollTo'),'Official scroll cannot enter renderer Mock list');
10
14
  assert(officialUi.every(api=>nativeUiApis.includes(api)),'Finite official UI delegation list');
@@ -50,7 +54,12 @@ export function bridgeScript(origin, additionalApis = [], observeLoading = false
50
54
  return undefined;
51
55
  }
52
56
  if (!response.ok) return {error:99999,errorMessage:'test boundary rejected native call'};
53
- return await response.json();
57
+ const result = await response.json();
58
+ const token = response.headers.get(${JSON.stringify(deliveryHeader)});
59
+ if(!token)throw Error('Mock response missing delivery identity');
60
+ const receipt = await fetch(${JSON.stringify(origin+jsapiDeliveryPath)},{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({token})});
61
+ if(!receipt.ok)throw Error('Mock response delivery rejected');
62
+ return result;
54
63
  }
55
64
  });});`;
56
65
  }
@@ -70,8 +79,27 @@ export async function fulfillJsapi(route, plan) {
70
79
  const call=decodeNativeStorage(wire.api,businessJsapiOption(wire.option));
71
80
  let result = await plan.receive(`jsapi:${call.api}`, call.params, wire);
72
81
  if(wire.api==='getTinyLocalStorage'&&Object.hasOwn(result??{},'data'))result={...result,data:JSON.stringify({APDataStorage:result.data})};
73
- await route.fulfill({json:result});
74
- plan.markDelivered(`jsapi:${call.api}`,call.params);
82
+ let deliveries=deliveryPlans.get(plan);
83
+ if(!deliveries){deliveries=new Map();deliveryPlans.set(plan,deliveries);}
84
+ const token=randomUUID();
85
+ deliveries.set(token,{operation:`jsapi:${call.api}`,request:call.params,frame:request.frame()});
86
+ try{await route.fulfill({json:result,headers:{[deliveryHeader]:token}});}
87
+ catch(error){deliveries.delete(token);throw error;}
88
+ }
89
+
90
+ // Browser acknowledges a fully parsed response before returning it to AppX.
91
+ // This records delivery, not product callback consumption; consumption stays independent.
92
+ export async function acknowledgeJsapiDelivery(route,plan){
93
+ const request=route.request();
94
+ assert.equal(request.method(),'POST','Delivery receipt must POST');
95
+ const receipt=request.postDataJSON();
96
+ assert.deepEqual(Object.keys(receipt).sort(),['token'],'Exact delivery receipt required');
97
+ const deliveries=deliveryPlans.get(plan),entry=deliveries?.get(receipt.token);
98
+ assert(entry,'Unknown, duplicate or failed response delivery identity');
99
+ assert.equal(request.frame(),entry.frame,'Response receipt must belong to original frame');
100
+ deliveries.delete(receipt.token);
101
+ plan.markDelivered(entry.operation,entry.request);
102
+ await route.fulfill({status:204});
75
103
  }
76
104
 
77
105
  // Official @ali/antcube-appx-bridge-my root/storage/boost/impl.js maps