@milaboratories/pl-middle-layer 1.51.0 → 1.52.1
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/dist/cfg_render/executor.cjs +2 -2
- package/dist/cfg_render/executor.js +1 -1
- package/dist/debug/index.cjs +4 -1
- package/dist/debug/index.cjs.map +1 -1
- package/dist/debug/index.js +4 -1
- package/dist/debug/index.js.map +1 -1
- package/dist/index.cjs +14 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/js_render/computable_context.cjs +12 -2
- package/dist/js_render/computable_context.cjs.map +1 -1
- package/dist/js_render/computable_context.js +12 -2
- package/dist/js_render/computable_context.js.map +1 -1
- package/dist/js_render/context.cjs +36 -3
- package/dist/js_render/context.cjs.map +1 -1
- package/dist/js_render/context.js +36 -3
- package/dist/js_render/context.js.map +1 -1
- package/dist/js_render/index.cjs +5 -1
- package/dist/js_render/index.cjs.map +1 -1
- package/dist/js_render/index.js +5 -1
- package/dist/js_render/index.js.map +1 -1
- package/dist/middle_layer/project.cjs +8 -5
- package/dist/middle_layer/project.cjs.map +1 -1
- package/dist/middle_layer/project.js +8 -5
- package/dist/middle_layer/project.js.map +1 -1
- package/dist/middle_layer/project_overview.cjs +28 -22
- package/dist/middle_layer/project_overview.cjs.map +1 -1
- package/dist/middle_layer/project_overview.js +28 -22
- package/dist/middle_layer/project_overview.js.map +1 -1
- package/dist/model/block_pack_spec.cjs.map +1 -1
- package/dist/model/block_pack_spec.d.ts +2 -2
- package/dist/model/block_pack_spec.js.map +1 -1
- package/dist/model/template_spec.d.ts +7 -2
- package/dist/mutator/block-pack/block_pack.cjs +20 -1
- package/dist/mutator/block-pack/block_pack.cjs.map +1 -1
- package/dist/mutator/block-pack/block_pack.d.ts +4 -0
- package/dist/mutator/block-pack/block_pack.js +19 -1
- package/dist/mutator/block-pack/block_pack.js.map +1 -1
- package/dist/mutator/template/template_cache.cjs +515 -0
- package/dist/mutator/template/template_cache.cjs.map +1 -0
- package/dist/mutator/template/template_cache.d.ts +78 -0
- package/dist/mutator/template/template_cache.js +502 -0
- package/dist/mutator/template/template_cache.js.map +1 -0
- package/dist/mutator/template/template_loading.cjs +3 -1
- package/dist/mutator/template/template_loading.cjs.map +1 -1
- package/dist/mutator/template/template_loading.js +3 -1
- package/dist/mutator/template/template_loading.js.map +1 -1
- package/package.json +17 -17
- package/src/debug/index.ts +6 -0
- package/src/index.ts +1 -0
- package/src/js_render/computable_context.ts +13 -2
- package/src/js_render/context.ts +58 -5
- package/src/js_render/index.ts +8 -1
- package/src/middle_layer/project.ts +12 -8
- package/src/middle_layer/project_overview.ts +6 -0
- package/src/model/block_pack_spec.ts +2 -2
- package/src/model/template_spec.ts +11 -1
- package/src/mutator/block-pack/block_pack.ts +35 -1
- package/src/mutator/template/template_cache.test.ts +373 -0
- package/src/mutator/template/template_cache.ts +763 -0
- package/src/mutator/template/template_loading.ts +3 -0
|
@@ -30,7 +30,8 @@ async function prepareTemplateSpec(tpl) {
|
|
|
30
30
|
content: await node_fs.default.promises.readFile(tpl.path)
|
|
31
31
|
};
|
|
32
32
|
case "from-registry":
|
|
33
|
-
case "explicit":
|
|
33
|
+
case "explicit":
|
|
34
|
+
case "cached": return tpl;
|
|
34
35
|
case "prepared": return tpl;
|
|
35
36
|
default: return (0, _milaboratories_ts_helpers.assertNever)(tpl);
|
|
36
37
|
}
|
|
@@ -49,6 +50,7 @@ function loadTemplate(tx, spec) {
|
|
|
49
50
|
case "from-registry": return loadTemplateFromRegistry(tx, spec);
|
|
50
51
|
case "explicit": return require_direct_template_loader.loadTemplateFromExplicitDirect(tx, spec);
|
|
51
52
|
case "prepared": return require_direct_template_loader.loadTemplateFromPrepared(tx, spec);
|
|
53
|
+
case "cached": return spec.resourceId;
|
|
52
54
|
default: return (0, _milaboratories_ts_helpers.assertNever)(spec);
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template_loading.cjs","names":["fs","Pl","loadTemplateFromExplicitDirect","loadTemplateFromPrepared"],"sources":["../../../src/mutator/template/template_loading.ts"],"sourcesContent":["import type { AnyRef, PlTransaction, ResourceType } from \"@milaboratories/pl-client\";\nimport { field, Pl } from \"@milaboratories/pl-client\";\nimport fs from \"node:fs\";\nimport type {\n TemplateFromRegistry,\n TemplateSpecAny,\n TemplateSpecPrepared,\n} from \"../../model/template_spec\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { loadTemplateFromExplicitDirect, loadTemplateFromPrepared } from \"./direct_template_loader\";\n\n//\n// Resource schema\n//\n\nexport const TengoTemplateGet: ResourceType = { name: \"TengoTemplateGet\", version: \"1\" };\nexport const TengoTemplateGetRegistry = \"registry\";\nexport const TengoTemplateGetTemplateURI = \"templateURI\";\nexport const TengoTemplateGetTemplate = \"template\";\n\nexport const TengoTemplatePack: ResourceType = { name: \"TengoTemplatePack\", version: \"1\" };\nexport const TengoTemplatePackConvert: ResourceType = {\n name: \"TengoTemplatePackConvert\",\n version: \"1\",\n};\nexport const TengoTemplatePackConvertTemplatePack = \"templatePack\";\nexport const TengoTemplatePackConvertTemplate = \"template\";\n\nexport async function prepareTemplateSpec(tpl: TemplateSpecAny): Promise<TemplateSpecPrepared> {\n switch (tpl.type) {\n case \"from-file\":\n return {\n type: \"explicit\",\n content: await fs.promises.readFile(tpl.path),\n };\n case \"from-registry\":\n case \"explicit\":\n return tpl;\n case \"prepared\":\n return tpl;\n default:\n return assertNever(tpl);\n }\n}\n\nfunction loadTemplateFromRegistry(tx: PlTransaction, spec: TemplateFromRegistry): AnyRef {\n const getTemplate = tx.createStruct(TengoTemplateGet);\n const registry = field(getTemplate, TengoTemplateGetRegistry);\n const uri = field(getTemplate, TengoTemplateGetTemplateURI);\n const templateFromRegistry = field(getTemplate, TengoTemplateGetTemplate);\n\n // Note: it has a resource schema, so platforma creates fields by itself.\n\n tx.setField(registry, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.registry))));\n tx.setField(uri, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.path))));\n\n return templateFromRegistry;\n}\n\nexport function loadTemplate(tx: PlTransaction, spec: TemplateSpecPrepared): AnyRef {\n switch (spec.type) {\n case \"from-registry\":\n return loadTemplateFromRegistry(tx, spec);\n case \"explicit\":\n return loadTemplateFromExplicitDirect(tx, spec);\n case \"prepared\":\n return loadTemplateFromPrepared(tx, spec);\n default:\n return assertNever(spec);\n }\n}\n"],"mappings":";;;;;;;;AAeA,MAAa,mBAAiC;CAAE,MAAM;CAAoB,SAAS;CAAK;AACxF,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,2BAA2B;AAExC,MAAa,oBAAkC;CAAE,MAAM;CAAqB,SAAS;CAAK;AAC1F,MAAa,2BAAyC;CACpD,MAAM;CACN,SAAS;CACV;AACD,MAAa,uCAAuC;AACpD,MAAa,mCAAmC;AAEhD,eAAsB,oBAAoB,KAAqD;AAC7F,SAAQ,IAAI,MAAZ;EACE,KAAK,YACH,QAAO;GACL,MAAM;GACN,SAAS,MAAMA,gBAAG,SAAS,SAAS,IAAI,KAAK;GAC9C;EACH,KAAK;EACL,KAAK,
|
|
1
|
+
{"version":3,"file":"template_loading.cjs","names":["fs","Pl","loadTemplateFromExplicitDirect","loadTemplateFromPrepared"],"sources":["../../../src/mutator/template/template_loading.ts"],"sourcesContent":["import type { AnyRef, PlTransaction, ResourceType } from \"@milaboratories/pl-client\";\nimport { field, Pl } from \"@milaboratories/pl-client\";\nimport fs from \"node:fs\";\nimport type {\n TemplateFromRegistry,\n TemplateSpecAny,\n TemplateSpecPrepared,\n} from \"../../model/template_spec\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { loadTemplateFromExplicitDirect, loadTemplateFromPrepared } from \"./direct_template_loader\";\n\n//\n// Resource schema\n//\n\nexport const TengoTemplateGet: ResourceType = { name: \"TengoTemplateGet\", version: \"1\" };\nexport const TengoTemplateGetRegistry = \"registry\";\nexport const TengoTemplateGetTemplateURI = \"templateURI\";\nexport const TengoTemplateGetTemplate = \"template\";\n\nexport const TengoTemplatePack: ResourceType = { name: \"TengoTemplatePack\", version: \"1\" };\nexport const TengoTemplatePackConvert: ResourceType = {\n name: \"TengoTemplatePackConvert\",\n version: \"1\",\n};\nexport const TengoTemplatePackConvertTemplatePack = \"templatePack\";\nexport const TengoTemplatePackConvertTemplate = \"template\";\n\nexport async function prepareTemplateSpec(tpl: TemplateSpecAny): Promise<TemplateSpecPrepared> {\n switch (tpl.type) {\n case \"from-file\":\n return {\n type: \"explicit\",\n content: await fs.promises.readFile(tpl.path),\n };\n case \"from-registry\":\n case \"explicit\":\n case \"cached\":\n return tpl;\n case \"prepared\":\n return tpl;\n default:\n return assertNever(tpl);\n }\n}\n\nfunction loadTemplateFromRegistry(tx: PlTransaction, spec: TemplateFromRegistry): AnyRef {\n const getTemplate = tx.createStruct(TengoTemplateGet);\n const registry = field(getTemplate, TengoTemplateGetRegistry);\n const uri = field(getTemplate, TengoTemplateGetTemplateURI);\n const templateFromRegistry = field(getTemplate, TengoTemplateGetTemplate);\n\n // Note: it has a resource schema, so platforma creates fields by itself.\n\n tx.setField(registry, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.registry))));\n tx.setField(uri, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.path))));\n\n return templateFromRegistry;\n}\n\nexport function loadTemplate(tx: PlTransaction, spec: TemplateSpecPrepared): AnyRef {\n switch (spec.type) {\n case \"from-registry\":\n return loadTemplateFromRegistry(tx, spec);\n case \"explicit\":\n return loadTemplateFromExplicitDirect(tx, spec);\n case \"prepared\":\n return loadTemplateFromPrepared(tx, spec);\n case \"cached\":\n return spec.resourceId;\n default:\n return assertNever(spec);\n }\n}\n"],"mappings":";;;;;;;;AAeA,MAAa,mBAAiC;CAAE,MAAM;CAAoB,SAAS;CAAK;AACxF,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,2BAA2B;AAExC,MAAa,oBAAkC;CAAE,MAAM;CAAqB,SAAS;CAAK;AAC1F,MAAa,2BAAyC;CACpD,MAAM;CACN,SAAS;CACV;AACD,MAAa,uCAAuC;AACpD,MAAa,mCAAmC;AAEhD,eAAsB,oBAAoB,KAAqD;AAC7F,SAAQ,IAAI,MAAZ;EACE,KAAK,YACH,QAAO;GACL,MAAM;GACN,SAAS,MAAMA,gBAAG,SAAS,SAAS,IAAI,KAAK;GAC9C;EACH,KAAK;EACL,KAAK;EACL,KAAK,SACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,QACE,oDAAmB,IAAI;;;AAI7B,SAAS,yBAAyB,IAAmB,MAAoC;CACvF,MAAM,cAAc,GAAG,aAAa,iBAAiB;CACrD,MAAM,gDAAiB,aAAa,yBAAyB;CAC7D,MAAM,2CAAY,aAAa,4BAA4B;CAC3D,MAAM,4DAA6B,aAAa,yBAAyB;AAIzE,IAAG,SAAS,UAAU,GAAG,YAAYC,6BAAG,YAAY,OAAO,KAAK,KAAK,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC;AAChG,IAAG,SAAS,KAAK,GAAG,YAAYA,6BAAG,YAAY,OAAO,KAAK,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC;AAEvF,QAAO;;AAGT,SAAgB,aAAa,IAAmB,MAAoC;AAClF,SAAQ,KAAK,MAAb;EACE,KAAK,gBACH,QAAO,yBAAyB,IAAI,KAAK;EAC3C,KAAK,WACH,QAAOC,8DAA+B,IAAI,KAAK;EACjD,KAAK,WACH,QAAOC,wDAAyB,IAAI,KAAK;EAC3C,KAAK,SACH,QAAO,KAAK;EACd,QACE,oDAAmB,KAAK"}
|
|
@@ -28,7 +28,8 @@ async function prepareTemplateSpec(tpl) {
|
|
|
28
28
|
content: await fs.promises.readFile(tpl.path)
|
|
29
29
|
};
|
|
30
30
|
case "from-registry":
|
|
31
|
-
case "explicit":
|
|
31
|
+
case "explicit":
|
|
32
|
+
case "cached": return tpl;
|
|
32
33
|
case "prepared": return tpl;
|
|
33
34
|
default: return assertNever(tpl);
|
|
34
35
|
}
|
|
@@ -47,6 +48,7 @@ function loadTemplate(tx, spec) {
|
|
|
47
48
|
case "from-registry": return loadTemplateFromRegistry(tx, spec);
|
|
48
49
|
case "explicit": return loadTemplateFromExplicitDirect(tx, spec);
|
|
49
50
|
case "prepared": return loadTemplateFromPrepared(tx, spec);
|
|
51
|
+
case "cached": return spec.resourceId;
|
|
50
52
|
default: return assertNever(spec);
|
|
51
53
|
}
|
|
52
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template_loading.js","names":[],"sources":["../../../src/mutator/template/template_loading.ts"],"sourcesContent":["import type { AnyRef, PlTransaction, ResourceType } from \"@milaboratories/pl-client\";\nimport { field, Pl } from \"@milaboratories/pl-client\";\nimport fs from \"node:fs\";\nimport type {\n TemplateFromRegistry,\n TemplateSpecAny,\n TemplateSpecPrepared,\n} from \"../../model/template_spec\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { loadTemplateFromExplicitDirect, loadTemplateFromPrepared } from \"./direct_template_loader\";\n\n//\n// Resource schema\n//\n\nexport const TengoTemplateGet: ResourceType = { name: \"TengoTemplateGet\", version: \"1\" };\nexport const TengoTemplateGetRegistry = \"registry\";\nexport const TengoTemplateGetTemplateURI = \"templateURI\";\nexport const TengoTemplateGetTemplate = \"template\";\n\nexport const TengoTemplatePack: ResourceType = { name: \"TengoTemplatePack\", version: \"1\" };\nexport const TengoTemplatePackConvert: ResourceType = {\n name: \"TengoTemplatePackConvert\",\n version: \"1\",\n};\nexport const TengoTemplatePackConvertTemplatePack = \"templatePack\";\nexport const TengoTemplatePackConvertTemplate = \"template\";\n\nexport async function prepareTemplateSpec(tpl: TemplateSpecAny): Promise<TemplateSpecPrepared> {\n switch (tpl.type) {\n case \"from-file\":\n return {\n type: \"explicit\",\n content: await fs.promises.readFile(tpl.path),\n };\n case \"from-registry\":\n case \"explicit\":\n return tpl;\n case \"prepared\":\n return tpl;\n default:\n return assertNever(tpl);\n }\n}\n\nfunction loadTemplateFromRegistry(tx: PlTransaction, spec: TemplateFromRegistry): AnyRef {\n const getTemplate = tx.createStruct(TengoTemplateGet);\n const registry = field(getTemplate, TengoTemplateGetRegistry);\n const uri = field(getTemplate, TengoTemplateGetTemplateURI);\n const templateFromRegistry = field(getTemplate, TengoTemplateGetTemplate);\n\n // Note: it has a resource schema, so platforma creates fields by itself.\n\n tx.setField(registry, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.registry))));\n tx.setField(uri, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.path))));\n\n return templateFromRegistry;\n}\n\nexport function loadTemplate(tx: PlTransaction, spec: TemplateSpecPrepared): AnyRef {\n switch (spec.type) {\n case \"from-registry\":\n return loadTemplateFromRegistry(tx, spec);\n case \"explicit\":\n return loadTemplateFromExplicitDirect(tx, spec);\n case \"prepared\":\n return loadTemplateFromPrepared(tx, spec);\n default:\n return assertNever(spec);\n }\n}\n"],"mappings":";;;;;;AAeA,MAAa,mBAAiC;CAAE,MAAM;CAAoB,SAAS;CAAK;AACxF,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,2BAA2B;AAExC,MAAa,oBAAkC;CAAE,MAAM;CAAqB,SAAS;CAAK;AAC1F,MAAa,2BAAyC;CACpD,MAAM;CACN,SAAS;CACV;AACD,MAAa,uCAAuC;AACpD,MAAa,mCAAmC;AAEhD,eAAsB,oBAAoB,KAAqD;AAC7F,SAAQ,IAAI,MAAZ;EACE,KAAK,YACH,QAAO;GACL,MAAM;GACN,SAAS,MAAM,GAAG,SAAS,SAAS,IAAI,KAAK;GAC9C;EACH,KAAK;EACL,KAAK,
|
|
1
|
+
{"version":3,"file":"template_loading.js","names":[],"sources":["../../../src/mutator/template/template_loading.ts"],"sourcesContent":["import type { AnyRef, PlTransaction, ResourceType } from \"@milaboratories/pl-client\";\nimport { field, Pl } from \"@milaboratories/pl-client\";\nimport fs from \"node:fs\";\nimport type {\n TemplateFromRegistry,\n TemplateSpecAny,\n TemplateSpecPrepared,\n} from \"../../model/template_spec\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { loadTemplateFromExplicitDirect, loadTemplateFromPrepared } from \"./direct_template_loader\";\n\n//\n// Resource schema\n//\n\nexport const TengoTemplateGet: ResourceType = { name: \"TengoTemplateGet\", version: \"1\" };\nexport const TengoTemplateGetRegistry = \"registry\";\nexport const TengoTemplateGetTemplateURI = \"templateURI\";\nexport const TengoTemplateGetTemplate = \"template\";\n\nexport const TengoTemplatePack: ResourceType = { name: \"TengoTemplatePack\", version: \"1\" };\nexport const TengoTemplatePackConvert: ResourceType = {\n name: \"TengoTemplatePackConvert\",\n version: \"1\",\n};\nexport const TengoTemplatePackConvertTemplatePack = \"templatePack\";\nexport const TengoTemplatePackConvertTemplate = \"template\";\n\nexport async function prepareTemplateSpec(tpl: TemplateSpecAny): Promise<TemplateSpecPrepared> {\n switch (tpl.type) {\n case \"from-file\":\n return {\n type: \"explicit\",\n content: await fs.promises.readFile(tpl.path),\n };\n case \"from-registry\":\n case \"explicit\":\n case \"cached\":\n return tpl;\n case \"prepared\":\n return tpl;\n default:\n return assertNever(tpl);\n }\n}\n\nfunction loadTemplateFromRegistry(tx: PlTransaction, spec: TemplateFromRegistry): AnyRef {\n const getTemplate = tx.createStruct(TengoTemplateGet);\n const registry = field(getTemplate, TengoTemplateGetRegistry);\n const uri = field(getTemplate, TengoTemplateGetTemplateURI);\n const templateFromRegistry = field(getTemplate, TengoTemplateGetTemplate);\n\n // Note: it has a resource schema, so platforma creates fields by itself.\n\n tx.setField(registry, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.registry))));\n tx.setField(uri, tx.createValue(Pl.JsonString, Buffer.from(JSON.stringify(spec.path))));\n\n return templateFromRegistry;\n}\n\nexport function loadTemplate(tx: PlTransaction, spec: TemplateSpecPrepared): AnyRef {\n switch (spec.type) {\n case \"from-registry\":\n return loadTemplateFromRegistry(tx, spec);\n case \"explicit\":\n return loadTemplateFromExplicitDirect(tx, spec);\n case \"prepared\":\n return loadTemplateFromPrepared(tx, spec);\n case \"cached\":\n return spec.resourceId;\n default:\n return assertNever(spec);\n }\n}\n"],"mappings":";;;;;;AAeA,MAAa,mBAAiC;CAAE,MAAM;CAAoB,SAAS;CAAK;AACxF,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAC3C,MAAa,2BAA2B;AAExC,MAAa,oBAAkC;CAAE,MAAM;CAAqB,SAAS;CAAK;AAC1F,MAAa,2BAAyC;CACpD,MAAM;CACN,SAAS;CACV;AACD,MAAa,uCAAuC;AACpD,MAAa,mCAAmC;AAEhD,eAAsB,oBAAoB,KAAqD;AAC7F,SAAQ,IAAI,MAAZ;EACE,KAAK,YACH,QAAO;GACL,MAAM;GACN,SAAS,MAAM,GAAG,SAAS,SAAS,IAAI,KAAK;GAC9C;EACH,KAAK;EACL,KAAK;EACL,KAAK,SACH,QAAO;EACT,KAAK,WACH,QAAO;EACT,QACE,QAAO,YAAY,IAAI;;;AAI7B,SAAS,yBAAyB,IAAmB,MAAoC;CACvF,MAAM,cAAc,GAAG,aAAa,iBAAiB;CACrD,MAAM,WAAW,MAAM,aAAa,yBAAyB;CAC7D,MAAM,MAAM,MAAM,aAAa,4BAA4B;CAC3D,MAAM,uBAAuB,MAAM,aAAa,yBAAyB;AAIzE,IAAG,SAAS,UAAU,GAAG,YAAY,GAAG,YAAY,OAAO,KAAK,KAAK,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC;AAChG,IAAG,SAAS,KAAK,GAAG,YAAY,GAAG,YAAY,OAAO,KAAK,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC;AAEvF,QAAO;;AAGT,SAAgB,aAAa,IAAmB,MAAoC;AAClF,SAAQ,KAAK,MAAb;EACE,KAAK,gBACH,QAAO,yBAAyB,IAAI,KAAK;EAC3C,KAAK,WACH,QAAO,+BAA+B,IAAI,KAAK;EACjD,KAAK,WACH,QAAO,yBAAyB,IAAI,KAAK;EAC3C,KAAK,SACH,QAAO,KAAK;EACd,QACE,QAAO,YAAY,KAAK"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-middle-layer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.1",
|
|
4
4
|
"description": "Pl Middle Layer",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -30,22 +30,22 @@
|
|
|
30
30
|
"utility-types": "^3.11.0",
|
|
31
31
|
"yaml": "^2.8.0",
|
|
32
32
|
"zod": "~3.23.8",
|
|
33
|
-
"@milaboratories/pf-driver": "1.1.
|
|
34
|
-
"@milaboratories/
|
|
35
|
-
"@milaboratories/pl-
|
|
36
|
-
"@milaboratories/pl-
|
|
37
|
-
"@milaboratories/pl-
|
|
38
|
-
"@milaboratories/pl-deployments": "2.16.0",
|
|
39
|
-
"@milaboratories/pl-model-middle-layer": "1.14.0",
|
|
40
|
-
"@milaboratories/computable": "2.8.6",
|
|
41
|
-
"@milaboratories/pl-model-common": "1.27.0",
|
|
33
|
+
"@milaboratories/pf-driver": "1.1.1",
|
|
34
|
+
"@milaboratories/computable": "2.9.0",
|
|
35
|
+
"@milaboratories/pl-client": "2.18.1",
|
|
36
|
+
"@milaboratories/pl-deployments": "2.16.1",
|
|
37
|
+
"@milaboratories/pl-errors": "1.2.1",
|
|
42
38
|
"@milaboratories/pl-http": "1.2.4",
|
|
43
|
-
"@milaboratories/
|
|
44
|
-
"@milaboratories/pl-
|
|
39
|
+
"@milaboratories/pl-drivers": "1.12.2",
|
|
40
|
+
"@milaboratories/pl-model-common": "1.28.0",
|
|
41
|
+
"@milaboratories/pl-model-backend": "1.2.1",
|
|
42
|
+
"@milaboratories/pl-model-middle-layer": "1.15.0",
|
|
43
|
+
"@milaboratories/pl-tree": "1.9.2",
|
|
45
44
|
"@milaboratories/ts-helpers": "1.7.3",
|
|
46
|
-
"@
|
|
47
|
-
"@platforma-sdk/block-tools": "2.7.
|
|
48
|
-
"@platforma-sdk/workflow-tengo": "5.11.0"
|
|
45
|
+
"@milaboratories/resolve-helper": "1.1.3",
|
|
46
|
+
"@platforma-sdk/block-tools": "2.7.1",
|
|
47
|
+
"@platforma-sdk/workflow-tengo": "5.11.0",
|
|
48
|
+
"@platforma-sdk/model": "1.60.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "~24.5.2",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"semver": "^7.7.2",
|
|
55
55
|
"typescript": "~5.9.3",
|
|
56
56
|
"vitest": "^4.0.18",
|
|
57
|
-
"@milaboratories/ts-builder": "1.3.0",
|
|
58
57
|
"@milaboratories/ts-configs": "1.2.2",
|
|
59
|
-
"@milaboratories/build-configs": "1.5.2"
|
|
58
|
+
"@milaboratories/build-configs": "1.5.2",
|
|
59
|
+
"@milaboratories/ts-builder": "1.3.0"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
62
|
"node": ">=22.19.0"
|
package/src/debug/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export type MlDebugFlags = {
|
|
2
2
|
logTreeStats?: "cumulative" | "per-request";
|
|
3
3
|
logProjectMutationStat: boolean;
|
|
4
|
+
logTemplateCacheStat: boolean;
|
|
4
5
|
dumpInitialTreeState: boolean;
|
|
5
6
|
logOutputStatus?: "any" | "unstable-only";
|
|
6
7
|
logOutputRecalculations?: boolean;
|
|
8
|
+
logProjectOverviewStat: boolean;
|
|
9
|
+
logJsExecStat: boolean;
|
|
7
10
|
};
|
|
8
11
|
|
|
9
12
|
let flags: MlDebugFlags | undefined = undefined;
|
|
@@ -12,7 +15,10 @@ export function getDebugFlags() {
|
|
|
12
15
|
flags = {
|
|
13
16
|
dumpInitialTreeState: process.env.MI_DUMP_INITIAL_TREE_STATE !== undefined,
|
|
14
17
|
logProjectMutationStat: process.env.MI_LOG_PROJECT_MUTATION_STAT !== undefined,
|
|
18
|
+
logTemplateCacheStat: process.env.MI_LOG_TEMPLATE_CACHE_STAT !== undefined,
|
|
15
19
|
logOutputRecalculations: process.env.MI_LOG_OUTPUT_RECALCULATIONS !== undefined,
|
|
20
|
+
logProjectOverviewStat: process.env.MI_LOG_PROJECT_OVERVIEW_STAT !== undefined,
|
|
21
|
+
logJsExecStat: process.env.MI_LOG_JS_EXEC_STAT !== undefined,
|
|
16
22
|
};
|
|
17
23
|
if (process.env.MI_LOG_OUTPUT_STATUS)
|
|
18
24
|
flags.logOutputStatus =
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export type { InternalLsDriver } from "@milaboratories/pl-drivers";
|
|
|
19
19
|
|
|
20
20
|
// for tests etc..
|
|
21
21
|
export * from "./mutator/template/template_loading";
|
|
22
|
+
export * from "./mutator/template/template_cache";
|
|
22
23
|
export * from "./mutator/template/render_template";
|
|
23
24
|
export * from "./model/template_spec";
|
|
24
25
|
|
|
@@ -66,7 +66,15 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
66
66
|
private readonly accessors = new Map<string, PlTreeNodeAccessor | undefined>();
|
|
67
67
|
private readonly specDriver = new SpecDriver();
|
|
68
68
|
|
|
69
|
-
private
|
|
69
|
+
private _meta: Map<string, Block> | undefined;
|
|
70
|
+
private get meta(): Map<string, Block> {
|
|
71
|
+
if (this._meta === undefined) {
|
|
72
|
+
if (this.computableCtx === undefined)
|
|
73
|
+
throw new Error("blockMeta can't be resolved in this context");
|
|
74
|
+
this._meta = this.blockCtx.blockMeta(this.computableCtx);
|
|
75
|
+
}
|
|
76
|
+
return this._meta;
|
|
77
|
+
}
|
|
70
78
|
|
|
71
79
|
constructor(
|
|
72
80
|
private readonly parent: JsExecutionContext,
|
|
@@ -76,7 +84,6 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
76
84
|
computableCtx: ComputableCtx,
|
|
77
85
|
) {
|
|
78
86
|
this.computableCtx = computableCtx;
|
|
79
|
-
this.meta = blockCtx.blockMeta(computableCtx);
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
public resetComputableCtx() {
|
|
@@ -533,11 +540,15 @@ export class ComputableContextHelper implements JsRenderInternal.GlobalCfgRender
|
|
|
533
540
|
// QuickJS strips all fields from errors apart from 'name' and 'message'.
|
|
534
541
|
// That's why here we need to store them, and rethrow them when we exit
|
|
535
542
|
// from QuickJS code.
|
|
543
|
+
const t0 = performance.now();
|
|
536
544
|
try {
|
|
537
545
|
return (fn as any)(...args);
|
|
538
546
|
} catch (e: unknown) {
|
|
539
547
|
const newErr = parent.errorRepo.setAndRecreateForQuickJS(e);
|
|
540
548
|
throw vm.newError(newErr);
|
|
549
|
+
} finally {
|
|
550
|
+
parent.stats.ctxMethodCalls++;
|
|
551
|
+
parent.stats.ctxMethodMs += performance.now() - t0;
|
|
541
552
|
}
|
|
542
553
|
};
|
|
543
554
|
|
package/src/js_render/context.ts
CHANGED
|
@@ -39,6 +39,26 @@ export type ComputableEnv = {
|
|
|
39
39
|
computableCtx: ComputableCtx;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
/** Execution stats accumulated during the lifetime of a JsExecutionContext. */
|
|
43
|
+
export type JsExecStats = {
|
|
44
|
+
bundleEvalMs: number;
|
|
45
|
+
bundleBytes: number;
|
|
46
|
+
|
|
47
|
+
callbackMs: number;
|
|
48
|
+
callbackCount: number;
|
|
49
|
+
|
|
50
|
+
serInMs: number;
|
|
51
|
+
serInBytes: number;
|
|
52
|
+
serInCalls: number;
|
|
53
|
+
|
|
54
|
+
serOutMs: number;
|
|
55
|
+
serOutBytes: number;
|
|
56
|
+
serOutCalls: number;
|
|
57
|
+
|
|
58
|
+
ctxMethodCalls: number;
|
|
59
|
+
ctxMethodMs: number;
|
|
60
|
+
};
|
|
61
|
+
|
|
42
62
|
export class JsExecutionContext {
|
|
43
63
|
private readonly callbackRegistry: QuickJSHandle;
|
|
44
64
|
private readonly fnJSONStringify: QuickJSHandle;
|
|
@@ -48,6 +68,21 @@ export class JsExecutionContext {
|
|
|
48
68
|
|
|
49
69
|
public readonly computableHelper: ComputableContextHelper | undefined;
|
|
50
70
|
|
|
71
|
+
public readonly stats: JsExecStats = {
|
|
72
|
+
bundleEvalMs: 0,
|
|
73
|
+
bundleBytes: 0,
|
|
74
|
+
callbackMs: 0,
|
|
75
|
+
callbackCount: 0,
|
|
76
|
+
serInMs: 0,
|
|
77
|
+
serInBytes: 0,
|
|
78
|
+
serInCalls: 0,
|
|
79
|
+
serOutMs: 0,
|
|
80
|
+
serOutBytes: 0,
|
|
81
|
+
serOutCalls: 0,
|
|
82
|
+
ctxMethodCalls: 0,
|
|
83
|
+
ctxMethodMs: 0,
|
|
84
|
+
};
|
|
85
|
+
|
|
51
86
|
/**
|
|
52
87
|
* Creates a new JS execution context.
|
|
53
88
|
*
|
|
@@ -111,6 +146,7 @@ export class JsExecutionContext {
|
|
|
111
146
|
// }
|
|
112
147
|
|
|
113
148
|
public evaluateBundle(code: string) {
|
|
149
|
+
const t0 = performance.now();
|
|
114
150
|
try {
|
|
115
151
|
this.deadlineSetter({
|
|
116
152
|
currentExecutionTarget: "evaluateBundle",
|
|
@@ -122,10 +158,13 @@ export class JsExecutionContext {
|
|
|
122
158
|
throw err;
|
|
123
159
|
} finally {
|
|
124
160
|
this.deadlineSetter(undefined);
|
|
161
|
+
this.stats.bundleEvalMs += performance.now() - t0;
|
|
162
|
+
this.stats.bundleBytes += code.length;
|
|
125
163
|
}
|
|
126
164
|
}
|
|
127
165
|
|
|
128
166
|
public runCallback(cbName: string, ...args: unknown[]): QuickJSHandle {
|
|
167
|
+
const t0 = performance.now();
|
|
129
168
|
try {
|
|
130
169
|
this.deadlineSetter({ currentExecutionTarget: cbName, deadline: Date.now() + 10000 });
|
|
131
170
|
return Scope.withScope((localScope) => {
|
|
@@ -150,6 +189,8 @@ export class JsExecutionContext {
|
|
|
150
189
|
throw original;
|
|
151
190
|
} finally {
|
|
152
191
|
this.deadlineSetter(undefined);
|
|
192
|
+
this.stats.callbackMs += performance.now() - t0;
|
|
193
|
+
this.stats.callbackCount++;
|
|
153
194
|
}
|
|
154
195
|
}
|
|
155
196
|
|
|
@@ -210,11 +251,16 @@ export class JsExecutionContext {
|
|
|
210
251
|
}
|
|
211
252
|
|
|
212
253
|
public exportObjectViaJson(obj: unknown, scope: Scope | undefined): QuickJSHandle {
|
|
254
|
+
const t0 = performance.now();
|
|
255
|
+
const json = JSON.stringify(obj);
|
|
256
|
+
this.stats.serInBytes += json.length;
|
|
257
|
+
this.stats.serInCalls++;
|
|
213
258
|
const result = this.vm
|
|
214
|
-
.newString(
|
|
215
|
-
.consume((
|
|
216
|
-
this.vm.unwrapResult(this.vm.callFunction(this.fnJSONParse, this.vm.undefined,
|
|
259
|
+
.newString(json)
|
|
260
|
+
.consume((jsonHandle) =>
|
|
261
|
+
this.vm.unwrapResult(this.vm.callFunction(this.fnJSONParse, this.vm.undefined, jsonHandle)),
|
|
217
262
|
);
|
|
263
|
+
this.stats.serInMs += performance.now() - t0;
|
|
218
264
|
return scope !== undefined ? scope.manage(result) : result;
|
|
219
265
|
}
|
|
220
266
|
|
|
@@ -233,13 +279,20 @@ export class JsExecutionContext {
|
|
|
233
279
|
}
|
|
234
280
|
|
|
235
281
|
public importObjectViaJson(handle: QuickJSHandle): unknown {
|
|
282
|
+
const t0 = performance.now();
|
|
236
283
|
const text = this.vm
|
|
237
284
|
.unwrapResult(this.vm.callFunction(this.fnJSONStringify, this.vm.undefined, handle))
|
|
238
285
|
.consume((strHandle) => this.vm.getString(strHandle));
|
|
239
|
-
|
|
286
|
+
this.stats.serOutBytes += text.length;
|
|
287
|
+
this.stats.serOutCalls++;
|
|
288
|
+
if (text === "undefined") {
|
|
240
289
|
// special case with futures
|
|
290
|
+
this.stats.serOutMs += performance.now() - t0;
|
|
241
291
|
return undefined;
|
|
242
|
-
|
|
292
|
+
}
|
|
293
|
+
const result = JSON.parse(text);
|
|
294
|
+
this.stats.serOutMs += performance.now() - t0;
|
|
295
|
+
return result;
|
|
243
296
|
}
|
|
244
297
|
|
|
245
298
|
private injectCtx() {
|
package/src/js_render/index.ts
CHANGED
|
@@ -135,6 +135,8 @@ export function computableFromRF(
|
|
|
135
135
|
|
|
136
136
|
if (Object.keys(toBeResolved).length === 0) {
|
|
137
137
|
const importedResult = rCtx.importObjectUniversal(result);
|
|
138
|
+
if (getDebugFlags().logJsExecStat)
|
|
139
|
+
console.log(`[jsExec] ${key}: ${JSON.stringify(rCtx.stats)}`);
|
|
138
140
|
logOutputStatus(
|
|
139
141
|
fh.handle,
|
|
140
142
|
importedResult,
|
|
@@ -161,6 +163,8 @@ export function computableFromRF(
|
|
|
161
163
|
|
|
162
164
|
// logging
|
|
163
165
|
recalculationCounter++;
|
|
166
|
+
if (getDebugFlags().logJsExecStat)
|
|
167
|
+
console.log(`[jsExec] ${key} #${recalculationCounter}: ${JSON.stringify(rCtx.stats)}`);
|
|
164
168
|
logOutputStatus(fh.handle, renderedResult, stable, recalculationCounter, unstableMarker);
|
|
165
169
|
|
|
166
170
|
return renderedResult;
|
|
@@ -208,7 +212,10 @@ export function executeSingleLambda(
|
|
|
208
212
|
rCtx.evaluateBundle(code.content);
|
|
209
213
|
|
|
210
214
|
// Running the lambda with arguments (e.g., state for args(), args for enrichmentTargets())
|
|
211
|
-
|
|
215
|
+
const importedResult = rCtx.importObjectUniversal(rCtx.runCallback(fh.handle, ...args));
|
|
216
|
+
if (getDebugFlags().logJsExecStat)
|
|
217
|
+
console.log(`[jsExec] ${fh.handle}: ${JSON.stringify(rCtx.stats)}`);
|
|
218
|
+
return importedResult;
|
|
212
219
|
} finally {
|
|
213
220
|
scope.dispose();
|
|
214
221
|
}
|
|
@@ -47,6 +47,7 @@ import canonicalize from "canonicalize";
|
|
|
47
47
|
import type { ProjectOverviewLight } from "./project_overview_light";
|
|
48
48
|
import { projectOverviewLight } from "./project_overview_light";
|
|
49
49
|
import { applyProjectMigrations } from "../mutator/migration";
|
|
50
|
+
import { cacheBlockPackTemplate } from "../mutator/template/template_cache";
|
|
50
51
|
|
|
51
52
|
type BlockStateComputables = {
|
|
52
53
|
readonly fullState: Computable<BlockStateInternalV3>;
|
|
@@ -210,18 +211,20 @@ export class Project {
|
|
|
210
211
|
blockId: string = randomUUID(),
|
|
211
212
|
): Promise<string> {
|
|
212
213
|
const preparedBp = await this.env.bpPreparer.prepare(blockPackSpec);
|
|
213
|
-
const
|
|
214
|
-
const blockCfg = extractConfig(blockCfgContainer); // full content of this var should never be persisted
|
|
214
|
+
const blockCfg = extractConfig(preparedBp.config);
|
|
215
215
|
|
|
216
216
|
this.env.runtimeCapabilities.throwIfIncompatible(blockCfg.featureFlags);
|
|
217
217
|
|
|
218
|
+
// Pre-materialize template via cache (separate transaction(s))
|
|
219
|
+
const cachedBp = await cacheBlockPackTemplate(this.env.pl, preparedBp);
|
|
220
|
+
|
|
218
221
|
// Build NewBlockSpec based on model API version
|
|
219
222
|
const newBlockSpec =
|
|
220
223
|
blockCfg.modelAPIVersion === BLOCK_STORAGE_FACADE_VERSION
|
|
221
|
-
? { storageMode: "fromModel" as const, blockPack:
|
|
224
|
+
? { storageMode: "fromModel" as const, blockPack: cachedBp }
|
|
222
225
|
: {
|
|
223
226
|
storageMode: "legacy" as const,
|
|
224
|
-
blockPack:
|
|
227
|
+
blockPack: cachedBp,
|
|
225
228
|
legacyState: canonicalize({
|
|
226
229
|
args: blockCfg.initialArgs,
|
|
227
230
|
uiState: blockCfg.initialUiState,
|
|
@@ -301,12 +304,13 @@ export class Project {
|
|
|
301
304
|
author?: AuthorMarker,
|
|
302
305
|
): Promise<void> {
|
|
303
306
|
const preparedBp = await this.env.bpPreparer.prepare(blockPackSpec);
|
|
304
|
-
const blockCfg = extractConfig(
|
|
305
|
-
await this.env.bpPreparer.getBlockConfigContainer(blockPackSpec),
|
|
306
|
-
);
|
|
307
|
+
const blockCfg = extractConfig(preparedBp.config);
|
|
307
308
|
|
|
308
309
|
this.env.runtimeCapabilities.throwIfIncompatible(blockCfg.featureFlags);
|
|
309
310
|
|
|
311
|
+
// Pre-materialize template via cache (separate transaction(s))
|
|
312
|
+
const cachedBp = await cacheBlockPackTemplate(this.env.pl, preparedBp);
|
|
313
|
+
|
|
310
314
|
// resetState signals to mutator to reset storage
|
|
311
315
|
// For v2+ blocks: mutator gets initial storage directly via getInitialStorageInVM
|
|
312
316
|
// For v1 blocks: we pass the legacy state format
|
|
@@ -323,7 +327,7 @@ export class Project {
|
|
|
323
327
|
this.env.pl,
|
|
324
328
|
this.rid,
|
|
325
329
|
author,
|
|
326
|
-
(mut) => mut.migrateBlockPack(blockId,
|
|
330
|
+
(mut) => mut.migrateBlockPack(blockId, cachedBp, resetState),
|
|
327
331
|
{ name: "updateBlockPack", lockId: this.projectLockId },
|
|
328
332
|
);
|
|
329
333
|
await this.projectTree.refreshState();
|
|
@@ -30,6 +30,7 @@ import type { NavigationStates } from "./navigation_states";
|
|
|
30
30
|
import { getBlockPackInfo } from "./util";
|
|
31
31
|
import { resourceIdToString, type ResourceId } from "@milaboratories/pl-client";
|
|
32
32
|
import { omitBy, isEqual } from "es-toolkit";
|
|
33
|
+
import { getDebugFlags } from "../debug";
|
|
33
34
|
|
|
34
35
|
type BlockInfo = {
|
|
35
36
|
argsRid?: ResourceId;
|
|
@@ -346,6 +347,11 @@ export function projectOverview(
|
|
|
346
347
|
}),
|
|
347
348
|
};
|
|
348
349
|
},
|
|
350
|
+
onRecalculation: getDebugFlags().logProjectOverviewStat
|
|
351
|
+
? (stats) => {
|
|
352
|
+
console.log(`[projectOverview] ${JSON.stringify(stats)}`);
|
|
353
|
+
}
|
|
354
|
+
: undefined,
|
|
349
355
|
},
|
|
350
356
|
).withStableType();
|
|
351
357
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExplicitTemplate, PreparedTemplate } from "./template_spec";
|
|
1
|
+
import type { CachedTemplate, ExplicitTemplate, PreparedTemplate } from "./template_spec";
|
|
2
2
|
import type { ResourceType } from "@milaboratories/pl-client";
|
|
3
3
|
import type { BlockConfigContainer } from "@platforma-sdk/model";
|
|
4
4
|
import type { BlockPackSpec } from "@milaboratories/pl-model-middle-layer";
|
|
@@ -48,7 +48,7 @@ export interface BlockPackExplicit {
|
|
|
48
48
|
/** Block-pack spec that can be materialized in pl. */
|
|
49
49
|
export type BlockPackSpecPrepared = {
|
|
50
50
|
type: "prepared";
|
|
51
|
-
template: PreparedTemplate;
|
|
51
|
+
template: PreparedTemplate | CachedTemplate;
|
|
52
52
|
config: BlockConfigContainer;
|
|
53
53
|
frontend: FrontendSpec;
|
|
54
54
|
source: BlockPackSpec;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ResourceId } from "@milaboratories/pl-client";
|
|
1
2
|
import type { CompiledTemplateV3, TemplateData } from "@milaboratories/pl-model-backend";
|
|
2
3
|
|
|
3
4
|
export interface TemplateFromRegistry {
|
|
@@ -16,10 +17,19 @@ export interface PreparedTemplate {
|
|
|
16
17
|
data: TemplateData | CompiledTemplateV3;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
export interface CachedTemplate {
|
|
21
|
+
readonly type: "cached";
|
|
22
|
+
readonly resourceId: ResourceId;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
export interface TemplateFromFile {
|
|
20
26
|
readonly type: "from-file";
|
|
21
27
|
path: string;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
export type TemplateSpecPrepared =
|
|
30
|
+
export type TemplateSpecPrepared =
|
|
31
|
+
| TemplateFromRegistry
|
|
32
|
+
| ExplicitTemplate
|
|
33
|
+
| PreparedTemplate
|
|
34
|
+
| CachedTemplate;
|
|
25
35
|
export type TemplateSpecAny = TemplateSpecPrepared | TemplateFromFile;
|
|
@@ -4,6 +4,7 @@ import { loadTemplate } from "../template/template_loading";
|
|
|
4
4
|
import type { BlockPackExplicit, BlockPackSpecAny, BlockPackSpecPrepared } from "../../model";
|
|
5
5
|
import type { Signer } from "@milaboratories/ts-helpers";
|
|
6
6
|
import { assertNever } from "@milaboratories/ts-helpers";
|
|
7
|
+
import type { Branded } from "@milaboratories/pl-model-common";
|
|
7
8
|
import fs from "node:fs";
|
|
8
9
|
import type { Dispatcher } from "undici";
|
|
9
10
|
import { request } from "undici";
|
|
@@ -16,10 +17,13 @@ import { resolveDevPacket } from "../../dev_env";
|
|
|
16
17
|
import { getDevV2PacketMtime } from "../../block_registry";
|
|
17
18
|
import type { V2RegistryProvider } from "../../block_registry/registry-v2-provider";
|
|
18
19
|
import { LRUCache } from "lru-cache";
|
|
20
|
+
import canonicalize from "canonicalize";
|
|
19
21
|
import type { BlockPackSpec } from "@milaboratories/pl-model-middle-layer";
|
|
20
22
|
import { WorkerManager } from "../../worker/WorkerManager";
|
|
21
23
|
import { z } from "zod";
|
|
22
24
|
|
|
25
|
+
type PreparedCacheKey = Branded<string, "PreparedCacheKey">;
|
|
26
|
+
|
|
23
27
|
export const BlockPackCustomType: ResourceType = { name: "BlockPackCustom", version: "1" };
|
|
24
28
|
export const BlockPackTemplateField = "template";
|
|
25
29
|
export const BlockPackFrontendField = "frontend";
|
|
@@ -65,6 +69,11 @@ export class BlockPackPreparer {
|
|
|
65
69
|
sizeCalculation: (value) => value.byteLength,
|
|
66
70
|
});
|
|
67
71
|
|
|
72
|
+
/** Cache of prepared block packs for registry specs (immutable by version). */
|
|
73
|
+
private readonly preparedCache = new LRUCache<PreparedCacheKey, BlockPackSpecPrepared>({
|
|
74
|
+
max: 50,
|
|
75
|
+
});
|
|
76
|
+
|
|
68
77
|
public async getBlockConfigContainer(spec: BlockPackSpecAny): Promise<BlockConfigContainer> {
|
|
69
78
|
switch (spec.type) {
|
|
70
79
|
case "explicit":
|
|
@@ -106,16 +115,35 @@ export class BlockPackPreparer {
|
|
|
106
115
|
}
|
|
107
116
|
}
|
|
108
117
|
|
|
118
|
+
/** Returns a stable cache key for registry specs (immutable by version). Dev specs return undefined. */
|
|
119
|
+
private specKey(spec: BlockPackSpecAny): PreparedCacheKey | undefined {
|
|
120
|
+
switch (spec.type) {
|
|
121
|
+
case "from-registry-v1":
|
|
122
|
+
return `v1:${spec.registryUrl}:${spec.id.organization}:${spec.id.name}:${spec.id.version}` as PreparedCacheKey;
|
|
123
|
+
case "from-registry-v2":
|
|
124
|
+
return `v2:${spec.registryUrl}:${canonicalize(spec.id)}` as PreparedCacheKey;
|
|
125
|
+
default:
|
|
126
|
+
return undefined; // dev, explicit, prepared — not cacheable
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
109
130
|
public async prepare(spec: BlockPackSpecAny): Promise<BlockPackSpecPrepared> {
|
|
110
131
|
if (spec.type === "prepared") {
|
|
111
132
|
return spec;
|
|
112
133
|
}
|
|
113
134
|
|
|
135
|
+
// Check prepare cache for registry specs
|
|
136
|
+
const key = this.specKey(spec);
|
|
137
|
+
if (key) {
|
|
138
|
+
const cached = this.preparedCache.get(key);
|
|
139
|
+
if (cached) return cached;
|
|
140
|
+
}
|
|
141
|
+
|
|
114
142
|
const explicit = await this.prepareWithoutUnpacking(spec);
|
|
115
143
|
|
|
116
144
|
await using workerManager = new WorkerManager();
|
|
117
145
|
|
|
118
|
-
|
|
146
|
+
const result: BlockPackSpecPrepared = {
|
|
119
147
|
...explicit,
|
|
120
148
|
type: "prepared",
|
|
121
149
|
template: {
|
|
@@ -123,6 +151,12 @@ export class BlockPackPreparer {
|
|
|
123
151
|
data: await workerManager.process("parseTemplate", explicit.template.content),
|
|
124
152
|
},
|
|
125
153
|
};
|
|
154
|
+
|
|
155
|
+
if (key) {
|
|
156
|
+
this.preparedCache.set(key, result);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return result;
|
|
126
160
|
}
|
|
127
161
|
|
|
128
162
|
private async prepareWithoutUnpacking(
|