@milaboratories/pl-middle-layer 1.55.21 → 1.55.23

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.
@@ -37,6 +37,7 @@ function getServiceInjectors() {
37
37
  findTableColumn: (tableSpec, selector) => vm.exportSingleValue(driver.findTableColumn(vm.importObjectViaJson(tableSpec), vm.importObjectViaJson(selector)))
38
38
  };
39
39
  },
40
+ Dialog: () => ({}),
40
41
  PFrame: ({ host, vm }) => ({
41
42
  createPFrame: (def) => vm.exportSingleValue(host.createPFrame(vm.importObjectViaJson(def))),
42
43
  createPTable: (def) => vm.exportSingleValue(host.createPTable(vm.importObjectViaJson(def))),
@@ -1 +1 @@
1
- {"version":3,"file":"service_injectors.cjs","names":["Services","ServiceNotRegisteredError","PoolEntryGuard"],"sources":["../../src/js_render/service_injectors.ts"],"sourcesContent":["import type { QuickJSHandle, VmFunctionImplementation } from \"quickjs-emscripten\";\nimport type { InferServiceModel, ServiceBrand } from \"@milaboratories/pl-model-common\";\nimport { Services, ServiceNotRegisteredError } from \"@milaboratories/pl-model-common\";\nimport type {\n AxesId,\n AxesSpec,\n DataInfo,\n PColumn,\n PColumnSpec,\n PColumnValues,\n PTableColumnId,\n PTableColumnSpec,\n SingleAxisSelector,\n BuildQueryInput,\n DeleteColumnRequest,\n DiscoverColumnsRequest,\n PFrameDef,\n SpecQuery,\n PTableDef,\n PTableDefV2,\n SpecFrameHandle,\n} from \"@milaboratories/pl-model-common\";\nimport { PoolEntryGuard } from \"@milaboratories/pl-model-common\";\nimport type { JsExecutionContext } from \"./context\";\nimport type { ComputableContextHelper } from \"./computable_context\";\n\ntype VmMethod = VmFunctionImplementation<QuickJSHandle>;\n\nexport type ServiceInjectorContext = {\n host: ComputableContextHelper;\n vm: JsExecutionContext;\n};\n\n// Each injector returns a record of method name -> VM function implementation.\n// The framework automatically registers them with serviceFnKey(serviceId, methodName).\nexport type ServiceInjector<Methods extends string = string> = (\n ctx: ServiceInjectorContext,\n) => Record<Methods, VmMethod>;\n\n// Type-safe injector for a specific service — must return all methods from the model interface.\ntype ServiceInjectorFor<S extends keyof typeof Services> = ServiceInjector<\n string & keyof InferServiceModel<ServiceBrand<(typeof Services)[S]>>\n>;\n\n// Complete, type-checked injector map.\n// Adding a service to Services without an entry here is a compile-time error.\n// Missing a method from the interface is also a compile-time error.\ntype ServiceInjectorMap = { [K in keyof typeof Services]: ServiceInjectorFor<K> };\n\nexport function getServiceInjectors(): ServiceInjectorMap {\n return {\n PFrameSpec: ({ host, vm }: ServiceInjectorContext) => {\n const driver = host.serviceRegistry.get(Services.PFrameSpec);\n if (!driver)\n throw new ServiceNotRegisteredError(\n `Service \"${Services.PFrameSpec}\" has no factory in ModelServiceRegistry. Provide a non-null factory.`,\n );\n\n return {\n createSpecFrame: (specs: QuickJSHandle) => {\n using guard = new PoolEntryGuard(\n driver.createSpecFrame(vm.importObjectViaJson(specs) as Record<string, PColumnSpec>),\n );\n host.addOnDestroy(guard.entry.unref);\n const entry = guard.keep();\n // TODO: add [Symbol.dispose] once QuickJS supports ES2024 explicit resource management\n const obj = vm.vm.newObject();\n vm.vm.newString(entry.key).consume((k) => vm.vm.setProp(obj, \"key\", k));\n vm.vm\n .newFunction(\"unref\", () => {\n entry.unref();\n })\n .consume((fn) => vm.vm.setProp(obj, \"unref\", fn));\n return obj;\n },\n\n listColumns: (handle: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.listColumns(vm.vm.getString(handle) as SpecFrameHandle)),\n\n discoverColumns: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.discoverColumns(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DiscoverColumnsRequest,\n ),\n ),\n\n buildQuery: (input: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.buildQuery(vm.importObjectViaJson(input) as BuildQueryInput),\n ),\n\n deleteColumn: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.deleteColumn(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DeleteColumnRequest,\n ),\n ),\n\n evaluateQuery: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.evaluateQuery(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as SpecQuery,\n ),\n ),\n\n expandAxes: (spec: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.expandAxes(vm.importObjectViaJson(spec) as AxesSpec)),\n\n collapseAxes: (ids: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.collapseAxes(vm.importObjectViaJson(ids) as AxesId)),\n\n findAxis: (spec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findAxis(\n vm.importObjectViaJson(spec) as AxesSpec,\n vm.importObjectViaJson(selector) as SingleAxisSelector,\n ),\n ),\n\n findTableColumn: (tableSpec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findTableColumn(\n vm.importObjectViaJson(tableSpec) as PTableColumnSpec[],\n vm.importObjectViaJson(selector) as PTableColumnId,\n ),\n ),\n };\n },\n\n PFrame: ({ host, vm }: ServiceInjectorContext) => ({\n createPFrame: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPFrame(\n vm.importObjectViaJson(def) as PFrameDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTable: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTable(\n vm.importObjectViaJson(def) as PTableDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTableV2: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTableV2(\n vm.importObjectViaJson(def) as PTableDefV2<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n }),\n };\n}\n"],"mappings":";;;;AAiDA,SAAgB,sBAA0C;AACxD,QAAO;EACL,aAAa,EAAE,MAAM,SAAiC;GACpD,MAAM,SAAS,KAAK,gBAAgB,IAAIA,gCAAAA,SAAS,WAAW;AAC5D,OAAI,CAAC,OACH,OAAM,IAAIC,gCAAAA,0BACR,YAAYD,gCAAAA,SAAS,WAAW,uEACjC;AAEH,UAAO;IACL,kBAAkB,UAAyB;;;MACzC,MAAM,QAAA,YAAA,EAAQ,IAAIE,gCAAAA,eAChB,OAAO,gBAAgB,GAAG,oBAAoB,MAAM,CAAgC,CACrF,CAAA;AACD,WAAK,aAAa,MAAM,MAAM,MAAM;MACpC,MAAM,QAAQ,MAAM,MAAM;MAE1B,MAAM,MAAM,GAAG,GAAG,WAAW;AAC7B,SAAG,GAAG,UAAU,MAAM,IAAI,CAAC,SAAS,MAAM,GAAG,GAAG,QAAQ,KAAK,OAAO,EAAE,CAAC;AACvE,SAAG,GACA,YAAY,eAAe;AAC1B,aAAM,OAAO;QACb,CACD,SAAS,OAAO,GAAG,GAAG,QAAQ,KAAK,SAAS,GAAG,CAAC;AACnD,aAAO;;;;;;;IAGT,cAAc,WACZ,GAAG,oBAAoB,OAAO,YAAY,GAAG,GAAG,UAAU,OAAO,CAAoB,CAAC;IAExF,kBAAkB,QAAuB,YACvC,GAAG,oBACD,OAAO,gBACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,UACX,GAAG,oBACD,OAAO,WAAW,GAAG,oBAAoB,MAAM,CAAoB,CACpE;IAEH,eAAe,QAAuB,YACpC,GAAG,oBACD,OAAO,aACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,gBAAgB,QAAuB,YACrC,GAAG,oBACD,OAAO,cACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,SACX,GAAG,oBAAoB,OAAO,WAAW,GAAG,oBAAoB,KAAK,CAAa,CAAC;IAErF,eAAe,QACb,GAAG,oBAAoB,OAAO,aAAa,GAAG,oBAAoB,IAAI,CAAW,CAAC;IAEpF,WAAW,MAAqB,aAC9B,GAAG,kBACD,OAAO,SACL,GAAG,oBAAoB,KAAK,EAC5B,GAAG,oBAAoB,SAAS,CACjC,CACF;IAEH,kBAAkB,WAA0B,aAC1C,GAAG,kBACD,OAAO,gBACL,GAAG,oBAAoB,UAAU,EACjC,GAAG,oBAAoB,SAAS,CACjC,CACF;IACJ;;EAGH,SAAS,EAAE,MAAM,UAAkC;GACjD,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,iBAAiB,QACf,GAAG,kBACD,KAAK,eACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GACJ;EACF"}
1
+ {"version":3,"file":"service_injectors.cjs","names":["Services","ServiceNotRegisteredError","PoolEntryGuard"],"sources":["../../src/js_render/service_injectors.ts"],"sourcesContent":["import type { QuickJSHandle, VmFunctionImplementation } from \"quickjs-emscripten\";\nimport type { InferServiceModel, ServiceBrand } from \"@milaboratories/pl-model-common\";\nimport { Services, ServiceNotRegisteredError } from \"@milaboratories/pl-model-common\";\nimport type {\n AxesId,\n AxesSpec,\n DataInfo,\n PColumn,\n PColumnSpec,\n PColumnValues,\n PTableColumnId,\n PTableColumnSpec,\n SingleAxisSelector,\n BuildQueryInput,\n DeleteColumnRequest,\n DiscoverColumnsRequest,\n PFrameDef,\n SpecQuery,\n PTableDef,\n PTableDefV2,\n SpecFrameHandle,\n} from \"@milaboratories/pl-model-common\";\nimport { PoolEntryGuard } from \"@milaboratories/pl-model-common\";\nimport type { JsExecutionContext } from \"./context\";\nimport type { ComputableContextHelper } from \"./computable_context\";\n\ntype VmMethod = VmFunctionImplementation<QuickJSHandle>;\n\nexport type ServiceInjectorContext = {\n host: ComputableContextHelper;\n vm: JsExecutionContext;\n};\n\n// Each injector returns a record of method name -> VM function implementation.\n// The framework automatically registers them with serviceFnKey(serviceId, methodName).\nexport type ServiceInjector<Methods extends string = string> = (\n ctx: ServiceInjectorContext,\n) => Record<Methods, VmMethod>;\n\n// Type-safe injector for a specific service — must return all methods from the model interface.\ntype ServiceInjectorFor<S extends keyof typeof Services> = ServiceInjector<\n string & keyof InferServiceModel<ServiceBrand<(typeof Services)[S]>>\n>;\n\n// Complete, type-checked injector map.\n// Adding a service to Services without an entry here is a compile-time error.\n// Missing a method from the interface is also a compile-time error.\ntype ServiceInjectorMap = { [K in keyof typeof Services]: ServiceInjectorFor<K> };\n\nexport function getServiceInjectors(): ServiceInjectorMap {\n return {\n PFrameSpec: ({ host, vm }: ServiceInjectorContext) => {\n const driver = host.serviceRegistry.get(Services.PFrameSpec);\n if (!driver)\n throw new ServiceNotRegisteredError(\n `Service \"${Services.PFrameSpec}\" has no factory in ModelServiceRegistry. Provide a non-null factory.`,\n );\n\n return {\n createSpecFrame: (specs: QuickJSHandle) => {\n using guard = new PoolEntryGuard(\n driver.createSpecFrame(vm.importObjectViaJson(specs) as Record<string, PColumnSpec>),\n );\n host.addOnDestroy(guard.entry.unref);\n const entry = guard.keep();\n // TODO: add [Symbol.dispose] once QuickJS supports ES2024 explicit resource management\n const obj = vm.vm.newObject();\n vm.vm.newString(entry.key).consume((k) => vm.vm.setProp(obj, \"key\", k));\n vm.vm\n .newFunction(\"unref\", () => {\n entry.unref();\n })\n .consume((fn) => vm.vm.setProp(obj, \"unref\", fn));\n return obj;\n },\n\n listColumns: (handle: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.listColumns(vm.vm.getString(handle) as SpecFrameHandle)),\n\n discoverColumns: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.discoverColumns(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DiscoverColumnsRequest,\n ),\n ),\n\n buildQuery: (input: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.buildQuery(vm.importObjectViaJson(input) as BuildQueryInput),\n ),\n\n deleteColumn: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.deleteColumn(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DeleteColumnRequest,\n ),\n ),\n\n evaluateQuery: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.evaluateQuery(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as SpecQuery,\n ),\n ),\n\n expandAxes: (spec: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.expandAxes(vm.importObjectViaJson(spec) as AxesSpec)),\n\n collapseAxes: (ids: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.collapseAxes(vm.importObjectViaJson(ids) as AxesId)),\n\n findAxis: (spec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findAxis(\n vm.importObjectViaJson(spec) as AxesSpec,\n vm.importObjectViaJson(selector) as SingleAxisSelector,\n ),\n ),\n\n findTableColumn: (tableSpec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findTableColumn(\n vm.importObjectViaJson(tableSpec) as PTableColumnSpec[],\n vm.importObjectViaJson(selector) as PTableColumnId,\n ),\n ),\n };\n },\n\n // Dialog has no model-side surface — workflow scripts cannot open save dialogs.\n // Declared as an empty injector to satisfy the exhaustive ServiceInjectorMap.\n Dialog: () => ({}) as Record<never, VmMethod>,\n\n PFrame: ({ host, vm }: ServiceInjectorContext) => ({\n createPFrame: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPFrame(\n vm.importObjectViaJson(def) as PFrameDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTable: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTable(\n vm.importObjectViaJson(def) as PTableDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTableV2: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTableV2(\n vm.importObjectViaJson(def) as PTableDefV2<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n }),\n };\n}\n"],"mappings":";;;;AAiDA,SAAgB,sBAA0C;AACxD,QAAO;EACL,aAAa,EAAE,MAAM,SAAiC;GACpD,MAAM,SAAS,KAAK,gBAAgB,IAAIA,gCAAAA,SAAS,WAAW;AAC5D,OAAI,CAAC,OACH,OAAM,IAAIC,gCAAAA,0BACR,YAAYD,gCAAAA,SAAS,WAAW,uEACjC;AAEH,UAAO;IACL,kBAAkB,UAAyB;;;MACzC,MAAM,QAAA,YAAA,EAAQ,IAAIE,gCAAAA,eAChB,OAAO,gBAAgB,GAAG,oBAAoB,MAAM,CAAgC,CACrF,CAAA;AACD,WAAK,aAAa,MAAM,MAAM,MAAM;MACpC,MAAM,QAAQ,MAAM,MAAM;MAE1B,MAAM,MAAM,GAAG,GAAG,WAAW;AAC7B,SAAG,GAAG,UAAU,MAAM,IAAI,CAAC,SAAS,MAAM,GAAG,GAAG,QAAQ,KAAK,OAAO,EAAE,CAAC;AACvE,SAAG,GACA,YAAY,eAAe;AAC1B,aAAM,OAAO;QACb,CACD,SAAS,OAAO,GAAG,GAAG,QAAQ,KAAK,SAAS,GAAG,CAAC;AACnD,aAAO;;;;;;;IAGT,cAAc,WACZ,GAAG,oBAAoB,OAAO,YAAY,GAAG,GAAG,UAAU,OAAO,CAAoB,CAAC;IAExF,kBAAkB,QAAuB,YACvC,GAAG,oBACD,OAAO,gBACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,UACX,GAAG,oBACD,OAAO,WAAW,GAAG,oBAAoB,MAAM,CAAoB,CACpE;IAEH,eAAe,QAAuB,YACpC,GAAG,oBACD,OAAO,aACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,gBAAgB,QAAuB,YACrC,GAAG,oBACD,OAAO,cACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,SACX,GAAG,oBAAoB,OAAO,WAAW,GAAG,oBAAoB,KAAK,CAAa,CAAC;IAErF,eAAe,QACb,GAAG,oBAAoB,OAAO,aAAa,GAAG,oBAAoB,IAAI,CAAW,CAAC;IAEpF,WAAW,MAAqB,aAC9B,GAAG,kBACD,OAAO,SACL,GAAG,oBAAoB,KAAK,EAC5B,GAAG,oBAAoB,SAAS,CACjC,CACF;IAEH,kBAAkB,WAA0B,aAC1C,GAAG,kBACD,OAAO,gBACL,GAAG,oBAAoB,UAAU,EACjC,GAAG,oBAAoB,SAAS,CACjC,CACF;IACJ;;EAKH,eAAe,EAAE;EAEjB,SAAS,EAAE,MAAM,UAAkC;GACjD,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,iBAAiB,QACf,GAAG,kBACD,KAAK,eACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GACJ;EACF"}
@@ -36,6 +36,7 @@ function getServiceInjectors() {
36
36
  findTableColumn: (tableSpec, selector) => vm.exportSingleValue(driver.findTableColumn(vm.importObjectViaJson(tableSpec), vm.importObjectViaJson(selector)))
37
37
  };
38
38
  },
39
+ Dialog: () => ({}),
39
40
  PFrame: ({ host, vm }) => ({
40
41
  createPFrame: (def) => vm.exportSingleValue(host.createPFrame(vm.importObjectViaJson(def))),
41
42
  createPTable: (def) => vm.exportSingleValue(host.createPTable(vm.importObjectViaJson(def))),
@@ -1 +1 @@
1
- {"version":3,"file":"service_injectors.js","names":[],"sources":["../../src/js_render/service_injectors.ts"],"sourcesContent":["import type { QuickJSHandle, VmFunctionImplementation } from \"quickjs-emscripten\";\nimport type { InferServiceModel, ServiceBrand } from \"@milaboratories/pl-model-common\";\nimport { Services, ServiceNotRegisteredError } from \"@milaboratories/pl-model-common\";\nimport type {\n AxesId,\n AxesSpec,\n DataInfo,\n PColumn,\n PColumnSpec,\n PColumnValues,\n PTableColumnId,\n PTableColumnSpec,\n SingleAxisSelector,\n BuildQueryInput,\n DeleteColumnRequest,\n DiscoverColumnsRequest,\n PFrameDef,\n SpecQuery,\n PTableDef,\n PTableDefV2,\n SpecFrameHandle,\n} from \"@milaboratories/pl-model-common\";\nimport { PoolEntryGuard } from \"@milaboratories/pl-model-common\";\nimport type { JsExecutionContext } from \"./context\";\nimport type { ComputableContextHelper } from \"./computable_context\";\n\ntype VmMethod = VmFunctionImplementation<QuickJSHandle>;\n\nexport type ServiceInjectorContext = {\n host: ComputableContextHelper;\n vm: JsExecutionContext;\n};\n\n// Each injector returns a record of method name -> VM function implementation.\n// The framework automatically registers them with serviceFnKey(serviceId, methodName).\nexport type ServiceInjector<Methods extends string = string> = (\n ctx: ServiceInjectorContext,\n) => Record<Methods, VmMethod>;\n\n// Type-safe injector for a specific service — must return all methods from the model interface.\ntype ServiceInjectorFor<S extends keyof typeof Services> = ServiceInjector<\n string & keyof InferServiceModel<ServiceBrand<(typeof Services)[S]>>\n>;\n\n// Complete, type-checked injector map.\n// Adding a service to Services without an entry here is a compile-time error.\n// Missing a method from the interface is also a compile-time error.\ntype ServiceInjectorMap = { [K in keyof typeof Services]: ServiceInjectorFor<K> };\n\nexport function getServiceInjectors(): ServiceInjectorMap {\n return {\n PFrameSpec: ({ host, vm }: ServiceInjectorContext) => {\n const driver = host.serviceRegistry.get(Services.PFrameSpec);\n if (!driver)\n throw new ServiceNotRegisteredError(\n `Service \"${Services.PFrameSpec}\" has no factory in ModelServiceRegistry. Provide a non-null factory.`,\n );\n\n return {\n createSpecFrame: (specs: QuickJSHandle) => {\n using guard = new PoolEntryGuard(\n driver.createSpecFrame(vm.importObjectViaJson(specs) as Record<string, PColumnSpec>),\n );\n host.addOnDestroy(guard.entry.unref);\n const entry = guard.keep();\n // TODO: add [Symbol.dispose] once QuickJS supports ES2024 explicit resource management\n const obj = vm.vm.newObject();\n vm.vm.newString(entry.key).consume((k) => vm.vm.setProp(obj, \"key\", k));\n vm.vm\n .newFunction(\"unref\", () => {\n entry.unref();\n })\n .consume((fn) => vm.vm.setProp(obj, \"unref\", fn));\n return obj;\n },\n\n listColumns: (handle: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.listColumns(vm.vm.getString(handle) as SpecFrameHandle)),\n\n discoverColumns: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.discoverColumns(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DiscoverColumnsRequest,\n ),\n ),\n\n buildQuery: (input: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.buildQuery(vm.importObjectViaJson(input) as BuildQueryInput),\n ),\n\n deleteColumn: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.deleteColumn(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DeleteColumnRequest,\n ),\n ),\n\n evaluateQuery: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.evaluateQuery(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as SpecQuery,\n ),\n ),\n\n expandAxes: (spec: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.expandAxes(vm.importObjectViaJson(spec) as AxesSpec)),\n\n collapseAxes: (ids: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.collapseAxes(vm.importObjectViaJson(ids) as AxesId)),\n\n findAxis: (spec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findAxis(\n vm.importObjectViaJson(spec) as AxesSpec,\n vm.importObjectViaJson(selector) as SingleAxisSelector,\n ),\n ),\n\n findTableColumn: (tableSpec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findTableColumn(\n vm.importObjectViaJson(tableSpec) as PTableColumnSpec[],\n vm.importObjectViaJson(selector) as PTableColumnId,\n ),\n ),\n };\n },\n\n PFrame: ({ host, vm }: ServiceInjectorContext) => ({\n createPFrame: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPFrame(\n vm.importObjectViaJson(def) as PFrameDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTable: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTable(\n vm.importObjectViaJson(def) as PTableDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTableV2: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTableV2(\n vm.importObjectViaJson(def) as PTableDefV2<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n }),\n };\n}\n"],"mappings":";;;AAiDA,SAAgB,sBAA0C;AACxD,QAAO;EACL,aAAa,EAAE,MAAM,SAAiC;GACpD,MAAM,SAAS,KAAK,gBAAgB,IAAI,SAAS,WAAW;AAC5D,OAAI,CAAC,OACH,OAAM,IAAI,0BACR,YAAY,SAAS,WAAW,uEACjC;AAEH,UAAO;IACL,kBAAkB,UAAyB;;;MACzC,MAAM,QAAA,YAAA,EAAQ,IAAI,eAChB,OAAO,gBAAgB,GAAG,oBAAoB,MAAM,CAAgC,CACrF,CAAA;AACD,WAAK,aAAa,MAAM,MAAM,MAAM;MACpC,MAAM,QAAQ,MAAM,MAAM;MAE1B,MAAM,MAAM,GAAG,GAAG,WAAW;AAC7B,SAAG,GAAG,UAAU,MAAM,IAAI,CAAC,SAAS,MAAM,GAAG,GAAG,QAAQ,KAAK,OAAO,EAAE,CAAC;AACvE,SAAG,GACA,YAAY,eAAe;AAC1B,aAAM,OAAO;QACb,CACD,SAAS,OAAO,GAAG,GAAG,QAAQ,KAAK,SAAS,GAAG,CAAC;AACnD,aAAO;;;;;;;IAGT,cAAc,WACZ,GAAG,oBAAoB,OAAO,YAAY,GAAG,GAAG,UAAU,OAAO,CAAoB,CAAC;IAExF,kBAAkB,QAAuB,YACvC,GAAG,oBACD,OAAO,gBACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,UACX,GAAG,oBACD,OAAO,WAAW,GAAG,oBAAoB,MAAM,CAAoB,CACpE;IAEH,eAAe,QAAuB,YACpC,GAAG,oBACD,OAAO,aACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,gBAAgB,QAAuB,YACrC,GAAG,oBACD,OAAO,cACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,SACX,GAAG,oBAAoB,OAAO,WAAW,GAAG,oBAAoB,KAAK,CAAa,CAAC;IAErF,eAAe,QACb,GAAG,oBAAoB,OAAO,aAAa,GAAG,oBAAoB,IAAI,CAAW,CAAC;IAEpF,WAAW,MAAqB,aAC9B,GAAG,kBACD,OAAO,SACL,GAAG,oBAAoB,KAAK,EAC5B,GAAG,oBAAoB,SAAS,CACjC,CACF;IAEH,kBAAkB,WAA0B,aAC1C,GAAG,kBACD,OAAO,gBACL,GAAG,oBAAoB,UAAU,EACjC,GAAG,oBAAoB,SAAS,CACjC,CACF;IACJ;;EAGH,SAAS,EAAE,MAAM,UAAkC;GACjD,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,iBAAiB,QACf,GAAG,kBACD,KAAK,eACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GACJ;EACF"}
1
+ {"version":3,"file":"service_injectors.js","names":[],"sources":["../../src/js_render/service_injectors.ts"],"sourcesContent":["import type { QuickJSHandle, VmFunctionImplementation } from \"quickjs-emscripten\";\nimport type { InferServiceModel, ServiceBrand } from \"@milaboratories/pl-model-common\";\nimport { Services, ServiceNotRegisteredError } from \"@milaboratories/pl-model-common\";\nimport type {\n AxesId,\n AxesSpec,\n DataInfo,\n PColumn,\n PColumnSpec,\n PColumnValues,\n PTableColumnId,\n PTableColumnSpec,\n SingleAxisSelector,\n BuildQueryInput,\n DeleteColumnRequest,\n DiscoverColumnsRequest,\n PFrameDef,\n SpecQuery,\n PTableDef,\n PTableDefV2,\n SpecFrameHandle,\n} from \"@milaboratories/pl-model-common\";\nimport { PoolEntryGuard } from \"@milaboratories/pl-model-common\";\nimport type { JsExecutionContext } from \"./context\";\nimport type { ComputableContextHelper } from \"./computable_context\";\n\ntype VmMethod = VmFunctionImplementation<QuickJSHandle>;\n\nexport type ServiceInjectorContext = {\n host: ComputableContextHelper;\n vm: JsExecutionContext;\n};\n\n// Each injector returns a record of method name -> VM function implementation.\n// The framework automatically registers them with serviceFnKey(serviceId, methodName).\nexport type ServiceInjector<Methods extends string = string> = (\n ctx: ServiceInjectorContext,\n) => Record<Methods, VmMethod>;\n\n// Type-safe injector for a specific service — must return all methods from the model interface.\ntype ServiceInjectorFor<S extends keyof typeof Services> = ServiceInjector<\n string & keyof InferServiceModel<ServiceBrand<(typeof Services)[S]>>\n>;\n\n// Complete, type-checked injector map.\n// Adding a service to Services without an entry here is a compile-time error.\n// Missing a method from the interface is also a compile-time error.\ntype ServiceInjectorMap = { [K in keyof typeof Services]: ServiceInjectorFor<K> };\n\nexport function getServiceInjectors(): ServiceInjectorMap {\n return {\n PFrameSpec: ({ host, vm }: ServiceInjectorContext) => {\n const driver = host.serviceRegistry.get(Services.PFrameSpec);\n if (!driver)\n throw new ServiceNotRegisteredError(\n `Service \"${Services.PFrameSpec}\" has no factory in ModelServiceRegistry. Provide a non-null factory.`,\n );\n\n return {\n createSpecFrame: (specs: QuickJSHandle) => {\n using guard = new PoolEntryGuard(\n driver.createSpecFrame(vm.importObjectViaJson(specs) as Record<string, PColumnSpec>),\n );\n host.addOnDestroy(guard.entry.unref);\n const entry = guard.keep();\n // TODO: add [Symbol.dispose] once QuickJS supports ES2024 explicit resource management\n const obj = vm.vm.newObject();\n vm.vm.newString(entry.key).consume((k) => vm.vm.setProp(obj, \"key\", k));\n vm.vm\n .newFunction(\"unref\", () => {\n entry.unref();\n })\n .consume((fn) => vm.vm.setProp(obj, \"unref\", fn));\n return obj;\n },\n\n listColumns: (handle: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.listColumns(vm.vm.getString(handle) as SpecFrameHandle)),\n\n discoverColumns: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.discoverColumns(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DiscoverColumnsRequest,\n ),\n ),\n\n buildQuery: (input: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.buildQuery(vm.importObjectViaJson(input) as BuildQueryInput),\n ),\n\n deleteColumn: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.deleteColumn(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as DeleteColumnRequest,\n ),\n ),\n\n evaluateQuery: (handle: QuickJSHandle, request: QuickJSHandle) =>\n vm.exportObjectViaJson(\n driver.evaluateQuery(\n vm.vm.getString(handle) as SpecFrameHandle,\n vm.importObjectViaJson(request) as SpecQuery,\n ),\n ),\n\n expandAxes: (spec: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.expandAxes(vm.importObjectViaJson(spec) as AxesSpec)),\n\n collapseAxes: (ids: QuickJSHandle) =>\n vm.exportObjectViaJson(driver.collapseAxes(vm.importObjectViaJson(ids) as AxesId)),\n\n findAxis: (spec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findAxis(\n vm.importObjectViaJson(spec) as AxesSpec,\n vm.importObjectViaJson(selector) as SingleAxisSelector,\n ),\n ),\n\n findTableColumn: (tableSpec: QuickJSHandle, selector: QuickJSHandle) =>\n vm.exportSingleValue(\n driver.findTableColumn(\n vm.importObjectViaJson(tableSpec) as PTableColumnSpec[],\n vm.importObjectViaJson(selector) as PTableColumnId,\n ),\n ),\n };\n },\n\n // Dialog has no model-side surface — workflow scripts cannot open save dialogs.\n // Declared as an empty injector to satisfy the exhaustive ServiceInjectorMap.\n Dialog: () => ({}) as Record<never, VmMethod>,\n\n PFrame: ({ host, vm }: ServiceInjectorContext) => ({\n createPFrame: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPFrame(\n vm.importObjectViaJson(def) as PFrameDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTable: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTable(\n vm.importObjectViaJson(def) as PTableDef<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n\n createPTableV2: (def: QuickJSHandle) =>\n vm.exportSingleValue(\n host.createPTableV2(\n vm.importObjectViaJson(def) as PTableDefV2<\n PColumn<string | PColumnValues | DataInfo<string>>\n >,\n ),\n ),\n }),\n };\n}\n"],"mappings":";;;AAiDA,SAAgB,sBAA0C;AACxD,QAAO;EACL,aAAa,EAAE,MAAM,SAAiC;GACpD,MAAM,SAAS,KAAK,gBAAgB,IAAI,SAAS,WAAW;AAC5D,OAAI,CAAC,OACH,OAAM,IAAI,0BACR,YAAY,SAAS,WAAW,uEACjC;AAEH,UAAO;IACL,kBAAkB,UAAyB;;;MACzC,MAAM,QAAA,YAAA,EAAQ,IAAI,eAChB,OAAO,gBAAgB,GAAG,oBAAoB,MAAM,CAAgC,CACrF,CAAA;AACD,WAAK,aAAa,MAAM,MAAM,MAAM;MACpC,MAAM,QAAQ,MAAM,MAAM;MAE1B,MAAM,MAAM,GAAG,GAAG,WAAW;AAC7B,SAAG,GAAG,UAAU,MAAM,IAAI,CAAC,SAAS,MAAM,GAAG,GAAG,QAAQ,KAAK,OAAO,EAAE,CAAC;AACvE,SAAG,GACA,YAAY,eAAe;AAC1B,aAAM,OAAO;QACb,CACD,SAAS,OAAO,GAAG,GAAG,QAAQ,KAAK,SAAS,GAAG,CAAC;AACnD,aAAO;;;;;;;IAGT,cAAc,WACZ,GAAG,oBAAoB,OAAO,YAAY,GAAG,GAAG,UAAU,OAAO,CAAoB,CAAC;IAExF,kBAAkB,QAAuB,YACvC,GAAG,oBACD,OAAO,gBACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,UACX,GAAG,oBACD,OAAO,WAAW,GAAG,oBAAoB,MAAM,CAAoB,CACpE;IAEH,eAAe,QAAuB,YACpC,GAAG,oBACD,OAAO,aACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,gBAAgB,QAAuB,YACrC,GAAG,oBACD,OAAO,cACL,GAAG,GAAG,UAAU,OAAO,EACvB,GAAG,oBAAoB,QAAQ,CAChC,CACF;IAEH,aAAa,SACX,GAAG,oBAAoB,OAAO,WAAW,GAAG,oBAAoB,KAAK,CAAa,CAAC;IAErF,eAAe,QACb,GAAG,oBAAoB,OAAO,aAAa,GAAG,oBAAoB,IAAI,CAAW,CAAC;IAEpF,WAAW,MAAqB,aAC9B,GAAG,kBACD,OAAO,SACL,GAAG,oBAAoB,KAAK,EAC5B,GAAG,oBAAoB,SAAS,CACjC,CACF;IAEH,kBAAkB,WAA0B,aAC1C,GAAG,kBACD,OAAO,gBACL,GAAG,oBAAoB,UAAU,EACjC,GAAG,oBAAoB,SAAS,CACjC,CACF;IACJ;;EAKH,eAAe,EAAE;EAEjB,SAAS,EAAE,MAAM,UAAkC;GACjD,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,eAAe,QACb,GAAG,kBACD,KAAK,aACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GAEH,iBAAiB,QACf,GAAG,kBACD,KAAK,eACH,GAAG,oBAAoB,IAAI,CAG5B,CACF;GACJ;EACF"}
@@ -17,37 +17,67 @@ function outputRef(blockId, name, requireEnrichments) {
17
17
  function isBlockOutputReference(obj) {
18
18
  return typeof obj === "object" && obj !== null && "__isRef" in obj && obj.__isRef === true && "blockId" in obj && "name" in obj;
19
19
  }
20
+ /** Extracts all resource ids referenced by args object. */
21
+ function inferAllReferencedBlocks(args, allowed) {
22
+ const result = {
23
+ upstreams: /* @__PURE__ */ new Set(),
24
+ upstreamsRequiringEnrichments: /* @__PURE__ */ new Set(),
25
+ missingReferences: false
26
+ };
27
+ addAllReferencedBlocks(result, args, allowed);
28
+ return result;
29
+ }
20
30
  function addAllReferencedBlocks(result, node, allowed) {
21
31
  const type = typeof node;
22
32
  switch (type) {
23
33
  case "function":
24
34
  case "bigint":
25
35
  case "number":
26
- case "string":
27
36
  case "boolean":
28
37
  case "symbol":
29
38
  case "undefined": return;
39
+ case "string":
40
+ unwrapEmbeddedRef(node, (parsed) => addAllReferencedBlocks(result, parsed, allowed));
41
+ return;
30
42
  case "object":
31
43
  if (node === null) return;
32
- if (isBlockOutputReference(node)) if (allowed === void 0 || allowed.has(node.blockId)) {
33
- result.upstreams.add(node.blockId);
34
- if (node.requireEnrichments) result.upstreamsRequiringEnrichments.add(node.blockId);
35
- } else result.missingReferences = true;
44
+ if (isBlockOutputReference(node)) recordRef(result, node.blockId, node.requireEnrichments === true, allowed);
36
45
  else if (Array.isArray(node)) for (const child of node) addAllReferencedBlocks(result, child, allowed);
37
46
  else for (const [, child] of Object.entries(node)) addAllReferencedBlocks(result, child, allowed);
38
47
  return;
39
48
  default: (0, _milaboratories_ts_helpers.assertNever)(type);
40
49
  }
41
50
  }
42
- /** Extracts all resource ids referenced by args object. */
43
- function inferAllReferencedBlocks(args, allowed) {
44
- const result = {
45
- upstreams: /* @__PURE__ */ new Set(),
46
- upstreamsRequiringEnrichments: /* @__PURE__ */ new Set(),
47
- missingReferences: false
48
- };
49
- addAllReferencedBlocks(result, args, allowed);
50
- return result;
51
+ /**
52
+ * Detect a PlRef carried inside a string and hand the decoded value to `onParsed`.
53
+ *
54
+ * A PlRef-as-string is canonical `{...}` optionally wrapped by N `JSON.stringify`
55
+ * passes. Each pass adds a symmetric prefix/suffix made only of `"` and `\`
56
+ * chars (the escape padding) of equal length. The regex below is the strict
57
+ * shape gate — non-ref strings fail at the very first character, so we never
58
+ * scan their body. One pass is peeled per call; deeper nesting is unwrapped
59
+ * via recursion in the caller.
60
+ */
61
+ const EMBEDDED_REF_RE = /^(?<pre>[\\"]*)\{[\s\S]*?__isRef[\s\S]*\}(?<suf>[\\"]*)$/;
62
+ function unwrapEmbeddedRef(s, onParsed) {
63
+ const c0 = s.charCodeAt(0);
64
+ if (c0 !== 123 && c0 !== 34) return;
65
+ const m = EMBEDDED_REF_RE.exec(s);
66
+ if (m === null) return;
67
+ if (m.groups.pre.length !== m.groups.suf.length) return;
68
+ let parsed;
69
+ try {
70
+ parsed = JSON.parse(s);
71
+ } catch {
72
+ return;
73
+ }
74
+ if (parsed !== s) onParsed(parsed);
75
+ }
76
+ function recordRef(result, blockId, requireEnrichments, allowed) {
77
+ if (allowed === void 0 || allowed.has(blockId)) {
78
+ result.upstreams.add(blockId);
79
+ if (requireEnrichments) result.upstreamsRequiringEnrichments.add(blockId);
80
+ } else result.missingReferences = true;
51
81
  }
52
82
  //#endregion
53
83
  exports.inferAllReferencedBlocks = inferAllReferencedBlocks;
@@ -1 +1 @@
1
- {"version":3,"file":"args.cjs","names":[],"sources":["../../src/model/args.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/ts-helpers\";\nimport type { PlRef } from \"@platforma-sdk/model\";\n\nexport function outputRef(blockId: string, name: string, requireEnrichments?: boolean): PlRef {\n if (requireEnrichments) return { __isRef: true, blockId, name, requireEnrichments };\n else return { __isRef: true, blockId, name };\n}\n\nexport function isBlockOutputReference(obj: unknown): obj is PlRef {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"__isRef\" in obj &&\n obj.__isRef === true &&\n \"blockId\" in obj &&\n \"name\" in obj\n );\n}\n\nfunction addAllReferencedBlocks(result: BlockUpstreams, node: unknown, allowed?: Set<string>) {\n const type = typeof node;\n switch (type) {\n case \"function\":\n case \"bigint\":\n case \"number\":\n case \"string\":\n case \"boolean\":\n case \"symbol\":\n case \"undefined\":\n return;\n\n case \"object\":\n if (node === null) return;\n\n if (isBlockOutputReference(node)) {\n if (allowed === undefined || allowed.has(node.blockId)) {\n result.upstreams.add(node.blockId);\n if (node.requireEnrichments) result.upstreamsRequiringEnrichments.add(node.blockId);\n } else result.missingReferences = true;\n } else if (Array.isArray(node)) {\n for (const child of node) addAllReferencedBlocks(result, child, allowed);\n } else {\n for (const [, child] of Object.entries(node as object))\n addAllReferencedBlocks(result, child, allowed);\n }\n\n return;\n\n default:\n assertNever(type);\n }\n}\n\nexport interface BlockUpstreams {\n /** All direct block dependencies */\n upstreams: Set<string>;\n /** Direct block dependencies which enrichments are also required by current block */\n upstreamsRequiringEnrichments: Set<string>;\n /** True if not-allowed references was encountered */\n missingReferences: boolean;\n}\n\n/** Extracts all resource ids referenced by args object. */\nexport function inferAllReferencedBlocks(args: unknown, allowed?: Set<string>): BlockUpstreams {\n const result = {\n upstreams: new Set<string>(),\n upstreamsRequiringEnrichments: new Set<string>(),\n missingReferences: false,\n };\n addAllReferencedBlocks(result, args, allowed);\n return result;\n}\n"],"mappings":";;;AAGA,SAAgB,UAAU,SAAiB,MAAc,oBAAqC;AAC5F,KAAI,mBAAoB,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;EAAoB;KAC9E,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;;AAG9C,SAAgB,uBAAuB,KAA4B;AACjE,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,aAAa,OACb,IAAI,YAAY,QAChB,aAAa,OACb,UAAU;;AAId,SAAS,uBAAuB,QAAwB,MAAe,SAAuB;CAC5F,MAAM,OAAO,OAAO;AACpB,SAAQ,MAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,YACH;EAEF,KAAK;AACH,OAAI,SAAS,KAAM;AAEnB,OAAI,uBAAuB,KAAK,CAC9B,KAAI,YAAY,KAAA,KAAa,QAAQ,IAAI,KAAK,QAAQ,EAAE;AACtD,WAAO,UAAU,IAAI,KAAK,QAAQ;AAClC,QAAI,KAAK,mBAAoB,QAAO,8BAA8B,IAAI,KAAK,QAAQ;SAC9E,QAAO,oBAAoB;YACzB,MAAM,QAAQ,KAAK,CAC5B,MAAK,MAAM,SAAS,KAAM,wBAAuB,QAAQ,OAAO,QAAQ;OAExE,MAAK,MAAM,GAAG,UAAU,OAAO,QAAQ,KAAe,CACpD,wBAAuB,QAAQ,OAAO,QAAQ;AAGlD;EAEF,QACE,EAAA,GAAA,2BAAA,aAAY,KAAK;;;;AAcvB,SAAgB,yBAAyB,MAAe,SAAuC;CAC7F,MAAM,SAAS;EACb,2BAAW,IAAI,KAAa;EAC5B,+CAA+B,IAAI,KAAa;EAChD,mBAAmB;EACpB;AACD,wBAAuB,QAAQ,MAAM,QAAQ;AAC7C,QAAO"}
1
+ {"version":3,"file":"args.cjs","names":[],"sources":["../../src/model/args.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/ts-helpers\";\nimport type { PlRef } from \"@platforma-sdk/model\";\n\nexport function outputRef(blockId: string, name: string, requireEnrichments?: boolean): PlRef {\n if (requireEnrichments) return { __isRef: true, blockId, name, requireEnrichments };\n else return { __isRef: true, blockId, name };\n}\n\nexport function isBlockOutputReference(obj: unknown): obj is PlRef {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"__isRef\" in obj &&\n obj.__isRef === true &&\n \"blockId\" in obj &&\n \"name\" in obj\n );\n}\n\nexport interface BlockUpstreams {\n /** All direct block dependencies */\n upstreams: Set<string>;\n /** Direct block dependencies which enrichments are also required by current block */\n upstreamsRequiringEnrichments: Set<string>;\n /** True if not-allowed references was encountered */\n missingReferences: boolean;\n}\n\n/** Extracts all resource ids referenced by args object. */\nexport function inferAllReferencedBlocks(args: unknown, allowed?: Set<string>): BlockUpstreams {\n const result = {\n upstreams: new Set<string>(),\n upstreamsRequiringEnrichments: new Set<string>(),\n missingReferences: false,\n };\n addAllReferencedBlocks(result, args, allowed);\n return result;\n}\n\nfunction addAllReferencedBlocks(result: BlockUpstreams, node: unknown, allowed?: Set<string>) {\n const type = typeof node;\n switch (type) {\n case \"function\":\n case \"bigint\":\n case \"number\":\n case \"boolean\":\n case \"symbol\":\n case \"undefined\":\n return;\n case \"string\": {\n unwrapEmbeddedRef(node as string, (parsed) =>\n addAllReferencedBlocks(result, parsed, allowed),\n );\n return;\n }\n case \"object\": {\n if (node === null) return;\n if (isBlockOutputReference(node)) {\n recordRef(result, node.blockId, node.requireEnrichments === true, allowed);\n } else if (Array.isArray(node)) {\n for (const child of node) addAllReferencedBlocks(result, child, allowed);\n } else {\n for (const [, child] of Object.entries(node as object))\n addAllReferencedBlocks(result, child, allowed);\n }\n\n return;\n }\n default:\n assertNever(type);\n }\n}\n\n/**\n * Detect a PlRef carried inside a string and hand the decoded value to `onParsed`.\n *\n * A PlRef-as-string is canonical `{...}` optionally wrapped by N `JSON.stringify`\n * passes. Each pass adds a symmetric prefix/suffix made only of `\"` and `\\`\n * chars (the escape padding) of equal length. The regex below is the strict\n * shape gate — non-ref strings fail at the very first character, so we never\n * scan their body. One pass is peeled per call; deeper nesting is unwrapped\n * via recursion in the caller.\n */\nconst EMBEDDED_REF_RE = /^(?<pre>[\\\\\"]*)\\{[\\s\\S]*?__isRef[\\s\\S]*\\}(?<suf>[\\\\\"]*)$/;\n\nfunction unwrapEmbeddedRef(s: string, onParsed: (value: unknown) => void) {\n const c0 = s.charCodeAt(0);\n if (c0 !== 0x7b /* { */ && c0 !== 0x22 /* \" */) return;\n const m = EMBEDDED_REF_RE.exec(s);\n if (m === null) return;\n if (m.groups!.pre.length !== m.groups!.suf.length) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(s);\n } catch {\n return;\n }\n if (parsed !== s) onParsed(parsed);\n}\n\nfunction recordRef(\n result: BlockUpstreams,\n blockId: string,\n requireEnrichments: boolean,\n allowed?: Set<string>,\n) {\n if (allowed === undefined || allowed.has(blockId)) {\n result.upstreams.add(blockId);\n if (requireEnrichments) result.upstreamsRequiringEnrichments.add(blockId);\n } else result.missingReferences = true;\n}\n"],"mappings":";;;AAGA,SAAgB,UAAU,SAAiB,MAAc,oBAAqC;AAC5F,KAAI,mBAAoB,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;EAAoB;KAC9E,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;;AAG9C,SAAgB,uBAAuB,KAA4B;AACjE,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,aAAa,OACb,IAAI,YAAY,QAChB,aAAa,OACb,UAAU;;;AAcd,SAAgB,yBAAyB,MAAe,SAAuC;CAC7F,MAAM,SAAS;EACb,2BAAW,IAAI,KAAa;EAC5B,+CAA+B,IAAI,KAAa;EAChD,mBAAmB;EACpB;AACD,wBAAuB,QAAQ,MAAM,QAAQ;AAC7C,QAAO;;AAGT,SAAS,uBAAuB,QAAwB,MAAe,SAAuB;CAC5F,MAAM,OAAO,OAAO;AACpB,SAAQ,MAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,YACH;EACF,KAAK;AACH,qBAAkB,OAAiB,WACjC,uBAAuB,QAAQ,QAAQ,QAAQ,CAChD;AACD;EAEF,KAAK;AACH,OAAI,SAAS,KAAM;AACnB,OAAI,uBAAuB,KAAK,CAC9B,WAAU,QAAQ,KAAK,SAAS,KAAK,uBAAuB,MAAM,QAAQ;YACjE,MAAM,QAAQ,KAAK,CAC5B,MAAK,MAAM,SAAS,KAAM,wBAAuB,QAAQ,OAAO,QAAQ;OAExE,MAAK,MAAM,GAAG,UAAU,OAAO,QAAQ,KAAe,CACpD,wBAAuB,QAAQ,OAAO,QAAQ;AAGlD;EAEF,QACE,EAAA,GAAA,2BAAA,aAAY,KAAK;;;;;;;;;;;;;AAcvB,MAAM,kBAAkB;AAExB,SAAS,kBAAkB,GAAW,UAAoC;CACxE,MAAM,KAAK,EAAE,WAAW,EAAE;AAC1B,KAAI,OAAO,OAAgB,OAAO,GAAc;CAChD,MAAM,IAAI,gBAAgB,KAAK,EAAE;AACjC,KAAI,MAAM,KAAM;AAChB,KAAI,EAAE,OAAQ,IAAI,WAAW,EAAE,OAAQ,IAAI,OAAQ;CACnD,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,EAAE;SAChB;AACN;;AAEF,KAAI,WAAW,EAAG,UAAS,OAAO;;AAGpC,SAAS,UACP,QACA,SACA,oBACA,SACA;AACA,KAAI,YAAY,KAAA,KAAa,QAAQ,IAAI,QAAQ,EAAE;AACjD,SAAO,UAAU,IAAI,QAAQ;AAC7B,MAAI,mBAAoB,QAAO,8BAA8B,IAAI,QAAQ;OACpE,QAAO,oBAAoB"}
@@ -16,37 +16,67 @@ function outputRef(blockId, name, requireEnrichments) {
16
16
  function isBlockOutputReference(obj) {
17
17
  return typeof obj === "object" && obj !== null && "__isRef" in obj && obj.__isRef === true && "blockId" in obj && "name" in obj;
18
18
  }
19
+ /** Extracts all resource ids referenced by args object. */
20
+ function inferAllReferencedBlocks(args, allowed) {
21
+ const result = {
22
+ upstreams: /* @__PURE__ */ new Set(),
23
+ upstreamsRequiringEnrichments: /* @__PURE__ */ new Set(),
24
+ missingReferences: false
25
+ };
26
+ addAllReferencedBlocks(result, args, allowed);
27
+ return result;
28
+ }
19
29
  function addAllReferencedBlocks(result, node, allowed) {
20
30
  const type = typeof node;
21
31
  switch (type) {
22
32
  case "function":
23
33
  case "bigint":
24
34
  case "number":
25
- case "string":
26
35
  case "boolean":
27
36
  case "symbol":
28
37
  case "undefined": return;
38
+ case "string":
39
+ unwrapEmbeddedRef(node, (parsed) => addAllReferencedBlocks(result, parsed, allowed));
40
+ return;
29
41
  case "object":
30
42
  if (node === null) return;
31
- if (isBlockOutputReference(node)) if (allowed === void 0 || allowed.has(node.blockId)) {
32
- result.upstreams.add(node.blockId);
33
- if (node.requireEnrichments) result.upstreamsRequiringEnrichments.add(node.blockId);
34
- } else result.missingReferences = true;
43
+ if (isBlockOutputReference(node)) recordRef(result, node.blockId, node.requireEnrichments === true, allowed);
35
44
  else if (Array.isArray(node)) for (const child of node) addAllReferencedBlocks(result, child, allowed);
36
45
  else for (const [, child] of Object.entries(node)) addAllReferencedBlocks(result, child, allowed);
37
46
  return;
38
47
  default: assertNever(type);
39
48
  }
40
49
  }
41
- /** Extracts all resource ids referenced by args object. */
42
- function inferAllReferencedBlocks(args, allowed) {
43
- const result = {
44
- upstreams: /* @__PURE__ */ new Set(),
45
- upstreamsRequiringEnrichments: /* @__PURE__ */ new Set(),
46
- missingReferences: false
47
- };
48
- addAllReferencedBlocks(result, args, allowed);
49
- return result;
50
+ /**
51
+ * Detect a PlRef carried inside a string and hand the decoded value to `onParsed`.
52
+ *
53
+ * A PlRef-as-string is canonical `{...}` optionally wrapped by N `JSON.stringify`
54
+ * passes. Each pass adds a symmetric prefix/suffix made only of `"` and `\`
55
+ * chars (the escape padding) of equal length. The regex below is the strict
56
+ * shape gate — non-ref strings fail at the very first character, so we never
57
+ * scan their body. One pass is peeled per call; deeper nesting is unwrapped
58
+ * via recursion in the caller.
59
+ */
60
+ const EMBEDDED_REF_RE = /^(?<pre>[\\"]*)\{[\s\S]*?__isRef[\s\S]*\}(?<suf>[\\"]*)$/;
61
+ function unwrapEmbeddedRef(s, onParsed) {
62
+ const c0 = s.charCodeAt(0);
63
+ if (c0 !== 123 && c0 !== 34) return;
64
+ const m = EMBEDDED_REF_RE.exec(s);
65
+ if (m === null) return;
66
+ if (m.groups.pre.length !== m.groups.suf.length) return;
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(s);
70
+ } catch {
71
+ return;
72
+ }
73
+ if (parsed !== s) onParsed(parsed);
74
+ }
75
+ function recordRef(result, blockId, requireEnrichments, allowed) {
76
+ if (allowed === void 0 || allowed.has(blockId)) {
77
+ result.upstreams.add(blockId);
78
+ if (requireEnrichments) result.upstreamsRequiringEnrichments.add(blockId);
79
+ } else result.missingReferences = true;
50
80
  }
51
81
  //#endregion
52
82
  export { inferAllReferencedBlocks, outputRef };
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","names":[],"sources":["../../src/model/args.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/ts-helpers\";\nimport type { PlRef } from \"@platforma-sdk/model\";\n\nexport function outputRef(blockId: string, name: string, requireEnrichments?: boolean): PlRef {\n if (requireEnrichments) return { __isRef: true, blockId, name, requireEnrichments };\n else return { __isRef: true, blockId, name };\n}\n\nexport function isBlockOutputReference(obj: unknown): obj is PlRef {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"__isRef\" in obj &&\n obj.__isRef === true &&\n \"blockId\" in obj &&\n \"name\" in obj\n );\n}\n\nfunction addAllReferencedBlocks(result: BlockUpstreams, node: unknown, allowed?: Set<string>) {\n const type = typeof node;\n switch (type) {\n case \"function\":\n case \"bigint\":\n case \"number\":\n case \"string\":\n case \"boolean\":\n case \"symbol\":\n case \"undefined\":\n return;\n\n case \"object\":\n if (node === null) return;\n\n if (isBlockOutputReference(node)) {\n if (allowed === undefined || allowed.has(node.blockId)) {\n result.upstreams.add(node.blockId);\n if (node.requireEnrichments) result.upstreamsRequiringEnrichments.add(node.blockId);\n } else result.missingReferences = true;\n } else if (Array.isArray(node)) {\n for (const child of node) addAllReferencedBlocks(result, child, allowed);\n } else {\n for (const [, child] of Object.entries(node as object))\n addAllReferencedBlocks(result, child, allowed);\n }\n\n return;\n\n default:\n assertNever(type);\n }\n}\n\nexport interface BlockUpstreams {\n /** All direct block dependencies */\n upstreams: Set<string>;\n /** Direct block dependencies which enrichments are also required by current block */\n upstreamsRequiringEnrichments: Set<string>;\n /** True if not-allowed references was encountered */\n missingReferences: boolean;\n}\n\n/** Extracts all resource ids referenced by args object. */\nexport function inferAllReferencedBlocks(args: unknown, allowed?: Set<string>): BlockUpstreams {\n const result = {\n upstreams: new Set<string>(),\n upstreamsRequiringEnrichments: new Set<string>(),\n missingReferences: false,\n };\n addAllReferencedBlocks(result, args, allowed);\n return result;\n}\n"],"mappings":";;AAGA,SAAgB,UAAU,SAAiB,MAAc,oBAAqC;AAC5F,KAAI,mBAAoB,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;EAAoB;KAC9E,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;;AAG9C,SAAgB,uBAAuB,KAA4B;AACjE,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,aAAa,OACb,IAAI,YAAY,QAChB,aAAa,OACb,UAAU;;AAId,SAAS,uBAAuB,QAAwB,MAAe,SAAuB;CAC5F,MAAM,OAAO,OAAO;AACpB,SAAQ,MAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,YACH;EAEF,KAAK;AACH,OAAI,SAAS,KAAM;AAEnB,OAAI,uBAAuB,KAAK,CAC9B,KAAI,YAAY,KAAA,KAAa,QAAQ,IAAI,KAAK,QAAQ,EAAE;AACtD,WAAO,UAAU,IAAI,KAAK,QAAQ;AAClC,QAAI,KAAK,mBAAoB,QAAO,8BAA8B,IAAI,KAAK,QAAQ;SAC9E,QAAO,oBAAoB;YACzB,MAAM,QAAQ,KAAK,CAC5B,MAAK,MAAM,SAAS,KAAM,wBAAuB,QAAQ,OAAO,QAAQ;OAExE,MAAK,MAAM,GAAG,UAAU,OAAO,QAAQ,KAAe,CACpD,wBAAuB,QAAQ,OAAO,QAAQ;AAGlD;EAEF,QACE,aAAY,KAAK;;;;AAcvB,SAAgB,yBAAyB,MAAe,SAAuC;CAC7F,MAAM,SAAS;EACb,2BAAW,IAAI,KAAa;EAC5B,+CAA+B,IAAI,KAAa;EAChD,mBAAmB;EACpB;AACD,wBAAuB,QAAQ,MAAM,QAAQ;AAC7C,QAAO"}
1
+ {"version":3,"file":"args.js","names":[],"sources":["../../src/model/args.ts"],"sourcesContent":["import { assertNever } from \"@milaboratories/ts-helpers\";\nimport type { PlRef } from \"@platforma-sdk/model\";\n\nexport function outputRef(blockId: string, name: string, requireEnrichments?: boolean): PlRef {\n if (requireEnrichments) return { __isRef: true, blockId, name, requireEnrichments };\n else return { __isRef: true, blockId, name };\n}\n\nexport function isBlockOutputReference(obj: unknown): obj is PlRef {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"__isRef\" in obj &&\n obj.__isRef === true &&\n \"blockId\" in obj &&\n \"name\" in obj\n );\n}\n\nexport interface BlockUpstreams {\n /** All direct block dependencies */\n upstreams: Set<string>;\n /** Direct block dependencies which enrichments are also required by current block */\n upstreamsRequiringEnrichments: Set<string>;\n /** True if not-allowed references was encountered */\n missingReferences: boolean;\n}\n\n/** Extracts all resource ids referenced by args object. */\nexport function inferAllReferencedBlocks(args: unknown, allowed?: Set<string>): BlockUpstreams {\n const result = {\n upstreams: new Set<string>(),\n upstreamsRequiringEnrichments: new Set<string>(),\n missingReferences: false,\n };\n addAllReferencedBlocks(result, args, allowed);\n return result;\n}\n\nfunction addAllReferencedBlocks(result: BlockUpstreams, node: unknown, allowed?: Set<string>) {\n const type = typeof node;\n switch (type) {\n case \"function\":\n case \"bigint\":\n case \"number\":\n case \"boolean\":\n case \"symbol\":\n case \"undefined\":\n return;\n case \"string\": {\n unwrapEmbeddedRef(node as string, (parsed) =>\n addAllReferencedBlocks(result, parsed, allowed),\n );\n return;\n }\n case \"object\": {\n if (node === null) return;\n if (isBlockOutputReference(node)) {\n recordRef(result, node.blockId, node.requireEnrichments === true, allowed);\n } else if (Array.isArray(node)) {\n for (const child of node) addAllReferencedBlocks(result, child, allowed);\n } else {\n for (const [, child] of Object.entries(node as object))\n addAllReferencedBlocks(result, child, allowed);\n }\n\n return;\n }\n default:\n assertNever(type);\n }\n}\n\n/**\n * Detect a PlRef carried inside a string and hand the decoded value to `onParsed`.\n *\n * A PlRef-as-string is canonical `{...}` optionally wrapped by N `JSON.stringify`\n * passes. Each pass adds a symmetric prefix/suffix made only of `\"` and `\\`\n * chars (the escape padding) of equal length. The regex below is the strict\n * shape gate — non-ref strings fail at the very first character, so we never\n * scan their body. One pass is peeled per call; deeper nesting is unwrapped\n * via recursion in the caller.\n */\nconst EMBEDDED_REF_RE = /^(?<pre>[\\\\\"]*)\\{[\\s\\S]*?__isRef[\\s\\S]*\\}(?<suf>[\\\\\"]*)$/;\n\nfunction unwrapEmbeddedRef(s: string, onParsed: (value: unknown) => void) {\n const c0 = s.charCodeAt(0);\n if (c0 !== 0x7b /* { */ && c0 !== 0x22 /* \" */) return;\n const m = EMBEDDED_REF_RE.exec(s);\n if (m === null) return;\n if (m.groups!.pre.length !== m.groups!.suf.length) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(s);\n } catch {\n return;\n }\n if (parsed !== s) onParsed(parsed);\n}\n\nfunction recordRef(\n result: BlockUpstreams,\n blockId: string,\n requireEnrichments: boolean,\n allowed?: Set<string>,\n) {\n if (allowed === undefined || allowed.has(blockId)) {\n result.upstreams.add(blockId);\n if (requireEnrichments) result.upstreamsRequiringEnrichments.add(blockId);\n } else result.missingReferences = true;\n}\n"],"mappings":";;AAGA,SAAgB,UAAU,SAAiB,MAAc,oBAAqC;AAC5F,KAAI,mBAAoB,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;EAAoB;KAC9E,QAAO;EAAE,SAAS;EAAM;EAAS;EAAM;;AAG9C,SAAgB,uBAAuB,KAA4B;AACjE,QACE,OAAO,QAAQ,YACf,QAAQ,QACR,aAAa,OACb,IAAI,YAAY,QAChB,aAAa,OACb,UAAU;;;AAcd,SAAgB,yBAAyB,MAAe,SAAuC;CAC7F,MAAM,SAAS;EACb,2BAAW,IAAI,KAAa;EAC5B,+CAA+B,IAAI,KAAa;EAChD,mBAAmB;EACpB;AACD,wBAAuB,QAAQ,MAAM,QAAQ;AAC7C,QAAO;;AAGT,SAAS,uBAAuB,QAAwB,MAAe,SAAuB;CAC5F,MAAM,OAAO,OAAO;AACpB,SAAQ,MAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,YACH;EACF,KAAK;AACH,qBAAkB,OAAiB,WACjC,uBAAuB,QAAQ,QAAQ,QAAQ,CAChD;AACD;EAEF,KAAK;AACH,OAAI,SAAS,KAAM;AACnB,OAAI,uBAAuB,KAAK,CAC9B,WAAU,QAAQ,KAAK,SAAS,KAAK,uBAAuB,MAAM,QAAQ;YACjE,MAAM,QAAQ,KAAK,CAC5B,MAAK,MAAM,SAAS,KAAM,wBAAuB,QAAQ,OAAO,QAAQ;OAExE,MAAK,MAAM,GAAG,UAAU,OAAO,QAAQ,KAAe,CACpD,wBAAuB,QAAQ,OAAO,QAAQ;AAGlD;EAEF,QACE,aAAY,KAAK;;;;;;;;;;;;;AAcvB,MAAM,kBAAkB;AAExB,SAAS,kBAAkB,GAAW,UAAoC;CACxE,MAAM,KAAK,EAAE,WAAW,EAAE;AAC1B,KAAI,OAAO,OAAgB,OAAO,GAAc;CAChD,MAAM,IAAI,gBAAgB,KAAK,EAAE;AACjC,KAAI,MAAM,KAAM;AAChB,KAAI,EAAE,OAAQ,IAAI,WAAW,EAAE,OAAQ,IAAI,OAAQ;CACnD,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,EAAE;SAChB;AACN;;AAEF,KAAI,WAAW,EAAG,UAAS,OAAO;;AAGpC,SAAS,UACP,QACA,SACA,oBACA,SACA;AACA,KAAI,YAAY,KAAA,KAAa,QAAQ,IAAI,QAAQ,EAAE;AACjD,SAAO,UAAU,IAAI,QAAQ;AAC7B,MAAI,mBAAoB,QAAO,8BAA8B,IAAI,QAAQ;OACpE,QAAO,oBAAoB"}
@@ -12,7 +12,8 @@ let _milaboratories_pf_spec_driver = require("@milaboratories/pf-spec-driver");
12
12
  function createModelServiceRegistry(options) {
13
13
  return new _milaboratories_pl_model_common.ModelServiceRegistry(_milaboratories_pl_model_common.Services, {
14
14
  PFrameSpec: () => new _milaboratories_pf_spec_driver.SpecDriver({ logger: options.logger }),
15
- PFrame: null
15
+ PFrame: null,
16
+ Dialog: null
16
17
  });
17
18
  }
18
19
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"service_factories.cjs","names":["ModelServiceRegistry","Services","SpecDriver"],"sources":["../src/service_factories.ts"],"sourcesContent":["/**\n * Model service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * model-side driver instance, or null if the service has no model-side driver\n * (e.g. node services whose driver is provided by the desktop app).\n */\n\nimport { ModelServiceRegistry, Services } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport { type MiLogger } from \"@milaboratories/ts-helpers\";\n\nexport type ModelServiceOptions = {\n logger: MiLogger;\n};\n\nexport function createModelServiceRegistry(options: ModelServiceOptions) {\n return new ModelServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver({ logger: options.logger }),\n PFrame: null,\n });\n}\n"],"mappings":";;;;;;;;;;;AAgBA,SAAgB,2BAA2B,SAA8B;AACvE,QAAO,IAAIA,gCAAAA,qBAAqBC,gCAAAA,UAAU;EACxC,kBAAkB,IAAIC,+BAAAA,WAAW,EAAE,QAAQ,QAAQ,QAAQ,CAAC;EAC5D,QAAQ;EACT,CAAC"}
1
+ {"version":3,"file":"service_factories.cjs","names":["ModelServiceRegistry","Services","SpecDriver"],"sources":["../src/service_factories.ts"],"sourcesContent":["/**\n * Model service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * model-side driver instance, or null if the service has no model-side driver\n * (e.g. node services whose driver is provided by the desktop app).\n */\n\nimport { ModelServiceRegistry, Services } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport { type MiLogger } from \"@milaboratories/ts-helpers\";\n\nexport type ModelServiceOptions = {\n logger: MiLogger;\n};\n\nexport function createModelServiceRegistry(options: ModelServiceOptions) {\n return new ModelServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver({ logger: options.logger }),\n PFrame: null,\n Dialog: null,\n });\n}\n"],"mappings":";;;;;;;;;;;AAgBA,SAAgB,2BAA2B,SAA8B;AACvE,QAAO,IAAIA,gCAAAA,qBAAqBC,gCAAAA,UAAU;EACxC,kBAAkB,IAAIC,+BAAAA,WAAW,EAAE,QAAQ,QAAQ,QAAQ,CAAC;EAC5D,QAAQ;EACR,QAAQ;EACT,CAAC"}
@@ -11,7 +11,8 @@ import { SpecDriver } from "@milaboratories/pf-spec-driver";
11
11
  function createModelServiceRegistry(options) {
12
12
  return new ModelServiceRegistry(Services, {
13
13
  PFrameSpec: () => new SpecDriver({ logger: options.logger }),
14
- PFrame: null
14
+ PFrame: null,
15
+ Dialog: null
15
16
  });
16
17
  }
17
18
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"service_factories.js","names":[],"sources":["../src/service_factories.ts"],"sourcesContent":["/**\n * Model service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * model-side driver instance, or null if the service has no model-side driver\n * (e.g. node services whose driver is provided by the desktop app).\n */\n\nimport { ModelServiceRegistry, Services } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport { type MiLogger } from \"@milaboratories/ts-helpers\";\n\nexport type ModelServiceOptions = {\n logger: MiLogger;\n};\n\nexport function createModelServiceRegistry(options: ModelServiceOptions) {\n return new ModelServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver({ logger: options.logger }),\n PFrame: null,\n });\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAgB,2BAA2B,SAA8B;AACvE,QAAO,IAAI,qBAAqB,UAAU;EACxC,kBAAkB,IAAI,WAAW,EAAE,QAAQ,QAAQ,QAAQ,CAAC;EAC5D,QAAQ;EACT,CAAC"}
1
+ {"version":3,"file":"service_factories.js","names":[],"sources":["../src/service_factories.ts"],"sourcesContent":["/**\n * Model service factories — add a factory for each new service here.\n *\n * Each entry maps a Services key to a factory function that creates the\n * model-side driver instance, or null if the service has no model-side driver\n * (e.g. node services whose driver is provided by the desktop app).\n */\n\nimport { ModelServiceRegistry, Services } from \"@milaboratories/pl-model-common\";\nimport { SpecDriver } from \"@milaboratories/pf-spec-driver\";\nimport { type MiLogger } from \"@milaboratories/ts-helpers\";\n\nexport type ModelServiceOptions = {\n logger: MiLogger;\n};\n\nexport function createModelServiceRegistry(options: ModelServiceOptions) {\n return new ModelServiceRegistry(Services, {\n PFrameSpec: () => new SpecDriver({ logger: options.logger }),\n PFrame: null,\n Dialog: null,\n });\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAgB,2BAA2B,SAA8B;AACvE,QAAO,IAAI,qBAAqB,UAAU;EACxC,kBAAkB,IAAI,WAAW,EAAE,QAAQ,QAAQ,QAAQ,CAAC;EAC5D,QAAQ;EACR,QAAQ;EACT,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-middle-layer",
3
- "version": "1.55.21",
3
+ "version": "1.55.23",
4
4
  "description": "Pl Middle Layer",
5
5
  "keywords": [],
6
6
  "license": "UNLICENSED",
@@ -30,24 +30,24 @@
30
30
  "utility-types": "^3.11.0",
31
31
  "yaml": "^2.8.0",
32
32
  "zod": "~3.25.76",
33
- "@milaboratories/pf-driver": "1.3.11",
34
33
  "@milaboratories/computable": "2.9.2",
35
- "@milaboratories/pf-spec-driver": "1.3.3",
36
- "@milaboratories/pl-client": "3.1.7",
37
- "@milaboratories/pl-errors": "1.3.6",
34
+ "@milaboratories/pf-driver": "1.4.0",
38
35
  "@milaboratories/helpers": "1.14.1",
39
- "@milaboratories/pl-deployments": "2.17.6",
36
+ "@milaboratories/pf-spec-driver": "1.3.4",
37
+ "@milaboratories/pl-client": "3.1.8",
38
+ "@milaboratories/pl-drivers": "1.12.19",
39
+ "@milaboratories/pl-errors": "1.3.7",
40
+ "@milaboratories/pl-deployments": "2.17.7",
40
41
  "@milaboratories/pl-http": "1.2.4",
41
- "@milaboratories/pl-drivers": "1.12.18",
42
- "@milaboratories/pl-model-backend": "1.2.14",
43
- "@milaboratories/pl-tree": "1.9.16",
44
- "@milaboratories/pl-model-common": "1.35.0",
45
- "@milaboratories/pl-model-middle-layer": "1.18.4",
46
- "@milaboratories/resolve-helper": "1.1.3",
42
+ "@milaboratories/pl-model-common": "1.36.0",
43
+ "@milaboratories/pl-model-backend": "1.2.15",
44
+ "@milaboratories/pl-model-middle-layer": "1.18.5",
45
+ "@milaboratories/pl-tree": "1.9.17",
47
46
  "@milaboratories/ts-helpers": "1.8.1",
48
- "@platforma-sdk/model": "1.67.0",
49
- "@platforma-sdk/block-tools": "2.7.13",
50
- "@platforma-sdk/workflow-tengo": "5.15.0"
47
+ "@milaboratories/resolve-helper": "1.1.3",
48
+ "@platforma-sdk/block-tools": "2.7.14",
49
+ "@platforma-sdk/model": "1.68.0",
50
+ "@platforma-sdk/workflow-tengo": "5.15.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "~24.5.2",
@@ -130,6 +130,10 @@ export function getServiceInjectors(): ServiceInjectorMap {
130
130
  };
131
131
  },
132
132
 
133
+ // Dialog has no model-side surface — workflow scripts cannot open save dialogs.
134
+ // Declared as an empty injector to satisfy the exhaustive ServiceInjectorMap.
135
+ Dialog: () => ({}) as Record<never, VmMethod>,
136
+
133
137
  PFrame: ({ host, vm }: ServiceInjectorContext) => ({
134
138
  createPFrame: (def: QuickJSHandle) =>
135
139
  vm.exportSingleValue(
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
+ import canonicalize from "canonicalize";
2
3
  import { inferAllReferencedBlocks, outputRef } from "./args";
3
4
 
4
5
  describe("inferAllReferencedBlocks", () => {
@@ -29,4 +30,106 @@ describe("inferAllReferencedBlocks", () => {
29
30
  expect(result.upstreams).toContain("block1");
30
31
  expect(result.upstreams.size).toBe(1);
31
32
  });
33
+
34
+ it("extracts PlRef embedded as canonicalized JSON string (global-form PObjectId)", () => {
35
+ const id = canonicalize(outputRef("blockA", "exportName"))!;
36
+
37
+ const result = inferAllReferencedBlocks({ columnId: id });
38
+
39
+ expect(result.upstreams).toContain("blockA");
40
+ expect(result.upstreams.size).toBe(1);
41
+ expect(result.upstreamsRequiringEnrichments.size).toBe(0);
42
+ expect(result.missingReferences).toBe(false);
43
+ });
44
+
45
+ it("propagates requireEnrichments from canonicalized PlRef string", () => {
46
+ const id = canonicalize(outputRef("blockA", "exportName", true))!;
47
+
48
+ const result = inferAllReferencedBlocks(id);
49
+
50
+ expect(result.upstreams).toContain("blockA");
51
+ expect(result.upstreamsRequiringEnrichments).toContain("blockA");
52
+ });
53
+
54
+ it("finds multiple PlRef strings inside an args object", () => {
55
+ const a = canonicalize(outputRef("blockA", "n1"))!;
56
+ const b = canonicalize(outputRef("blockB", "n2", true))!;
57
+
58
+ const result = inferAllReferencedBlocks({ first: a, second: b });
59
+
60
+ expect(result.upstreams).toEqual(new Set(["blockA", "blockB"]));
61
+ expect(result.upstreamsRequiringEnrichments).toEqual(new Set(["blockB"]));
62
+ });
63
+
64
+ it("respects allowed set for embedded refs", () => {
65
+ const id = canonicalize(outputRef("blockX", "n"))!;
66
+
67
+ const result = inferAllReferencedBlocks({ id }, new Set(["blockY"]));
68
+
69
+ expect(result.upstreams.size).toBe(0);
70
+ expect(result.missingReferences).toBe(true);
71
+ });
72
+
73
+ it("decodes JSON escapes inside blockId", () => {
74
+ const id = canonicalize(outputRef('weird"id\\with\nescapes', "n"))!;
75
+
76
+ const result = inferAllReferencedBlocks({ id });
77
+
78
+ expect(result.upstreams).toContain('weird"id\\with\nescapes');
79
+ });
80
+
81
+ it("ignores strings without __isRef marker", () => {
82
+ const result = inferAllReferencedBlocks({
83
+ a: "plain text",
84
+ b: '{"some":"json","without":"ref"}',
85
+ });
86
+
87
+ expect(result.upstreams.size).toBe(0);
88
+ expect(result.missingReferences).toBe(false);
89
+ });
90
+
91
+ it("detects PlRef string regardless of JSON.stringify nesting depth (depth 0..10)", () => {
92
+ const id = canonicalize(outputRef("blockA", "n", true))!;
93
+
94
+ for (let depth = 0; depth <= 10; depth++) {
95
+ let value: string = id;
96
+ for (let i = 0; i < depth; i++) value = JSON.stringify(value);
97
+
98
+ const result = inferAllReferencedBlocks({ raw: value });
99
+
100
+ expect(result.upstreams, `depth=${depth}`).toContain("blockA");
101
+ expect(result.upstreamsRequiringEnrichments, `depth=${depth}`).toContain("blockA");
102
+ }
103
+ });
104
+
105
+ it("detects PlRef when the entire args container is stringified multiple times", () => {
106
+ const ref = outputRef("blockZ", "n");
107
+ const container: { col: unknown } = { col: ref };
108
+
109
+ let value: unknown = container;
110
+ for (let i = 0; i < 5; i++) value = JSON.stringify(value);
111
+
112
+ const result = inferAllReferencedBlocks({ wrapper: value });
113
+
114
+ expect(result.upstreams).toContain("blockZ");
115
+ });
116
+
117
+ it("detects PlRef when args itself is the multi-stringified ref string", () => {
118
+ let value: string = canonicalize(outputRef("blockTop", "n"))!;
119
+ for (let i = 0; i < 4; i++) value = JSON.stringify(value);
120
+
121
+ const result = inferAllReferencedBlocks(value);
122
+
123
+ expect(result.upstreams).toContain("blockTop");
124
+ });
125
+
126
+ it("ignores malformed __isRef occurrences", () => {
127
+ const result = inferAllReferencedBlocks({
128
+ a: '{"__isRef":true,"blockId"', // truncated
129
+ b: '"__isRef":false', // wrong value
130
+ c: "__isRef without quotes",
131
+ });
132
+
133
+ expect(result.upstreams.size).toBe(0);
134
+ });
32
135
  });
package/src/model/args.ts CHANGED
@@ -17,26 +17,46 @@ export function isBlockOutputReference(obj: unknown): obj is PlRef {
17
17
  );
18
18
  }
19
19
 
20
+ export interface BlockUpstreams {
21
+ /** All direct block dependencies */
22
+ upstreams: Set<string>;
23
+ /** Direct block dependencies which enrichments are also required by current block */
24
+ upstreamsRequiringEnrichments: Set<string>;
25
+ /** True if not-allowed references was encountered */
26
+ missingReferences: boolean;
27
+ }
28
+
29
+ /** Extracts all resource ids referenced by args object. */
30
+ export function inferAllReferencedBlocks(args: unknown, allowed?: Set<string>): BlockUpstreams {
31
+ const result = {
32
+ upstreams: new Set<string>(),
33
+ upstreamsRequiringEnrichments: new Set<string>(),
34
+ missingReferences: false,
35
+ };
36
+ addAllReferencedBlocks(result, args, allowed);
37
+ return result;
38
+ }
39
+
20
40
  function addAllReferencedBlocks(result: BlockUpstreams, node: unknown, allowed?: Set<string>) {
21
41
  const type = typeof node;
22
42
  switch (type) {
23
43
  case "function":
24
44
  case "bigint":
25
45
  case "number":
26
- case "string":
27
46
  case "boolean":
28
47
  case "symbol":
29
48
  case "undefined":
30
49
  return;
31
-
32
- case "object":
50
+ case "string": {
51
+ unwrapEmbeddedRef(node as string, (parsed) =>
52
+ addAllReferencedBlocks(result, parsed, allowed),
53
+ );
54
+ return;
55
+ }
56
+ case "object": {
33
57
  if (node === null) return;
34
-
35
58
  if (isBlockOutputReference(node)) {
36
- if (allowed === undefined || allowed.has(node.blockId)) {
37
- result.upstreams.add(node.blockId);
38
- if (node.requireEnrichments) result.upstreamsRequiringEnrichments.add(node.blockId);
39
- } else result.missingReferences = true;
59
+ recordRef(result, node.blockId, node.requireEnrichments === true, allowed);
40
60
  } else if (Array.isArray(node)) {
41
61
  for (const child of node) addAllReferencedBlocks(result, child, allowed);
42
62
  } else {
@@ -45,28 +65,47 @@ function addAllReferencedBlocks(result: BlockUpstreams, node: unknown, allowed?:
45
65
  }
46
66
 
47
67
  return;
48
-
68
+ }
49
69
  default:
50
70
  assertNever(type);
51
71
  }
52
72
  }
53
73
 
54
- export interface BlockUpstreams {
55
- /** All direct block dependencies */
56
- upstreams: Set<string>;
57
- /** Direct block dependencies which enrichments are also required by current block */
58
- upstreamsRequiringEnrichments: Set<string>;
59
- /** True if not-allowed references was encountered */
60
- missingReferences: boolean;
74
+ /**
75
+ * Detect a PlRef carried inside a string and hand the decoded value to `onParsed`.
76
+ *
77
+ * A PlRef-as-string is canonical `{...}` optionally wrapped by N `JSON.stringify`
78
+ * passes. Each pass adds a symmetric prefix/suffix made only of `"` and `\`
79
+ * chars (the escape padding) of equal length. The regex below is the strict
80
+ * shape gate — non-ref strings fail at the very first character, so we never
81
+ * scan their body. One pass is peeled per call; deeper nesting is unwrapped
82
+ * via recursion in the caller.
83
+ */
84
+ const EMBEDDED_REF_RE = /^(?<pre>[\\"]*)\{[\s\S]*?__isRef[\s\S]*\}(?<suf>[\\"]*)$/;
85
+
86
+ function unwrapEmbeddedRef(s: string, onParsed: (value: unknown) => void) {
87
+ const c0 = s.charCodeAt(0);
88
+ if (c0 !== 0x7b /* { */ && c0 !== 0x22 /* " */) return;
89
+ const m = EMBEDDED_REF_RE.exec(s);
90
+ if (m === null) return;
91
+ if (m.groups!.pre.length !== m.groups!.suf.length) return;
92
+ let parsed: unknown;
93
+ try {
94
+ parsed = JSON.parse(s);
95
+ } catch {
96
+ return;
97
+ }
98
+ if (parsed !== s) onParsed(parsed);
61
99
  }
62
100
 
63
- /** Extracts all resource ids referenced by args object. */
64
- export function inferAllReferencedBlocks(args: unknown, allowed?: Set<string>): BlockUpstreams {
65
- const result = {
66
- upstreams: new Set<string>(),
67
- upstreamsRequiringEnrichments: new Set<string>(),
68
- missingReferences: false,
69
- };
70
- addAllReferencedBlocks(result, args, allowed);
71
- return result;
101
+ function recordRef(
102
+ result: BlockUpstreams,
103
+ blockId: string,
104
+ requireEnrichments: boolean,
105
+ allowed?: Set<string>,
106
+ ) {
107
+ if (allowed === undefined || allowed.has(blockId)) {
108
+ result.upstreams.add(blockId);
109
+ if (requireEnrichments) result.upstreamsRequiringEnrichments.add(blockId);
110
+ } else result.missingReferences = true;
72
111
  }
@@ -18,5 +18,6 @@ export function createModelServiceRegistry(options: ModelServiceOptions) {
18
18
  return new ModelServiceRegistry(Services, {
19
19
  PFrameSpec: () => new SpecDriver({ logger: options.logger }),
20
20
  PFrame: null,
21
+ Dialog: null,
21
22
  });
22
23
  }