@milaboratories/pl-middle-layer 1.58.4 → 1.59.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.
Files changed (85) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/middle_layer/middle_layer.cjs +71 -47
  3. package/dist/middle_layer/middle_layer.cjs.map +1 -1
  4. package/dist/middle_layer/middle_layer.d.ts +18 -18
  5. package/dist/middle_layer/middle_layer.d.ts.map +1 -1
  6. package/dist/middle_layer/middle_layer.js +72 -48
  7. package/dist/middle_layer/middle_layer.js.map +1 -1
  8. package/dist/middle_layer/project.cjs +8 -9
  9. package/dist/middle_layer/project.cjs.map +1 -1
  10. package/dist/middle_layer/project.d.ts +6 -5
  11. package/dist/middle_layer/project.d.ts.map +1 -1
  12. package/dist/middle_layer/project.js +9 -10
  13. package/dist/middle_layer/project.js.map +1 -1
  14. package/dist/middle_layer/project_list.cjs +3 -3
  15. package/dist/middle_layer/project_list.cjs.map +1 -1
  16. package/dist/middle_layer/project_list.d.ts +1 -1
  17. package/dist/middle_layer/project_list.js +4 -4
  18. package/dist/middle_layer/project_list.js.map +1 -1
  19. package/dist/middle_layer/project_overview.cjs.map +1 -1
  20. package/dist/middle_layer/project_overview.js.map +1 -1
  21. package/dist/middle_layer/util.cjs.map +1 -1
  22. package/dist/middle_layer/util.js.map +1 -1
  23. package/dist/model/index.d.ts +2 -2
  24. package/dist/model/project_helper.cjs.map +1 -1
  25. package/dist/model/project_helper.d.ts +3 -3
  26. package/dist/model/project_helper.d.ts.map +1 -1
  27. package/dist/model/project_helper.js.map +1 -1
  28. package/dist/model/project_model.cjs.map +1 -1
  29. package/dist/model/project_model.d.ts +6 -5
  30. package/dist/model/project_model.d.ts.map +1 -1
  31. package/dist/model/project_model.js.map +1 -1
  32. package/dist/model/template_spec.d.ts +2 -2
  33. package/dist/model/template_spec.d.ts.map +1 -1
  34. package/dist/mutator/migration.cjs +1 -1
  35. package/dist/mutator/migration.cjs.map +1 -1
  36. package/dist/mutator/migration.js +2 -2
  37. package/dist/mutator/migration.js.map +1 -1
  38. package/dist/mutator/project.cjs +6 -6
  39. package/dist/mutator/project.cjs.map +1 -1
  40. package/dist/mutator/project.d.ts +3 -3
  41. package/dist/mutator/project.d.ts.map +1 -1
  42. package/dist/mutator/project.js +7 -7
  43. package/dist/mutator/project.js.map +1 -1
  44. package/dist/mutator/template/template_cache.cjs +7 -7
  45. package/dist/mutator/template/template_cache.cjs.map +1 -1
  46. package/dist/mutator/template/template_cache.d.ts +8 -8
  47. package/dist/mutator/template/template_cache.d.ts.map +1 -1
  48. package/dist/mutator/template/template_cache.js +8 -8
  49. package/dist/mutator/template/template_cache.js.map +1 -1
  50. package/dist/network_check/template.cjs +5 -5
  51. package/dist/network_check/template.cjs.map +1 -1
  52. package/dist/network_check/template.js +6 -6
  53. package/dist/network_check/template.js.map +1 -1
  54. package/dist/pool/data.cjs +2 -1
  55. package/dist/pool/data.cjs.map +1 -1
  56. package/dist/pool/data.d.ts +1 -1
  57. package/dist/pool/data.d.ts.map +1 -1
  58. package/dist/pool/data.js +3 -2
  59. package/dist/pool/data.js.map +1 -1
  60. package/dist/pool/driver.cjs +3 -1
  61. package/dist/pool/driver.cjs.map +1 -1
  62. package/dist/pool/driver.d.ts.map +1 -1
  63. package/dist/pool/driver.js +3 -1
  64. package/dist/pool/driver.js.map +1 -1
  65. package/package.json +15 -15
  66. package/src/index.ts +1 -1
  67. package/src/middle_layer/middle_layer.ts +99 -61
  68. package/src/middle_layer/project.ts +14 -13
  69. package/src/middle_layer/project_list.ts +10 -8
  70. package/src/middle_layer/project_overview.ts +2 -2
  71. package/src/middle_layer/render.test.ts +9 -9
  72. package/src/middle_layer/util.ts +2 -2
  73. package/src/model/index.ts +1 -1
  74. package/src/model/project_helper.ts +2 -2
  75. package/src/model/project_model.ts +7 -4
  76. package/src/model/template_spec.ts +2 -2
  77. package/src/mutator/block-pack/block_pack.test.ts +7 -2
  78. package/src/mutator/migration.ts +7 -7
  79. package/src/mutator/project.ts +20 -19
  80. package/src/mutator/template/template_cache.test.ts +6 -6
  81. package/src/mutator/template/template_cache.ts +24 -21
  82. package/src/mutator/template/template_render.test.ts +7 -7
  83. package/src/network_check/template.ts +8 -8
  84. package/src/pool/data.ts +6 -4
  85. package/src/pool/driver.ts +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"template.cjs","names":["SdkTemplates","Pl","ImportFileHandleUploadData","path","os","fs","createRenderTemplate","loadTemplate","prepareTemplateSpec","ContinuePolling"],"sources":["../../src/network_check/template.ts"],"sourcesContent":["import type { FieldId, FieldRef, PlClient, ResourceData } from \"@milaboratories/pl-client\";\nimport {\n type PlTransaction,\n ContinuePolling,\n field,\n isNotNullResourceId,\n isNullResourceId,\n Pl,\n poll,\n toGlobalFieldId,\n} from \"@milaboratories/pl-client\";\nimport { createRenderTemplate } from \"../mutator/template/render_template\";\nimport { Templates as SdkTemplates } from \"@platforma-sdk/workflow-tengo\";\nimport type { TemplateSpecAny } from \"../model/template_spec\";\nimport { loadTemplate, prepareTemplateSpec } from \"../mutator/template/template_loading\";\nimport type { ClientDownload, LsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n ImportFileHandleUploadData,\n isSignMatch,\n isUpload,\n uploadBlob,\n type ClientUpload,\n type LsEntryWithAdditionalInfo,\n} from \"@milaboratories/pl-drivers\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { notEmpty, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { text } from \"node:stream/consumers\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport type { StorageEntry } from \"@milaboratories/pl-model-common\";\n\nexport interface TemplateReport {\n status: \"ok\" | \"warn\" | \"failed\";\n message: string;\n}\n\n/** Uploads `hello-world` template and checks the output is correct. */\nexport async function uploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<TemplateReport> {\n try {\n const gotGreeting = await runUploadTemplate(logger, pl, name);\n if (gotGreeting !== `Hello, ${name}`) {\n return {\n status: \"failed\",\n message: `Template uploading failed: expected: ${name}, got: ${gotGreeting}`,\n };\n }\n\n return { status: \"ok\", message: `Template uploading succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Template uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<string> {\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_template\"],\n true,\n (tx) => ({\n name: tx.createValue(Pl.JsonObject, JSON.stringify(name)),\n }),\n [\"greeting\"],\n );\n\n try {\n return JSON.parse(notEmpty((await getFieldValue(pl, outputs.greeting)).data?.toString()));\n } finally {\n if (outputs != undefined) {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n}\n\n/** Uploads a file to the backend and checks the output is a Blob resource. */\nexport async function uploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<TemplateReport> {\n try {\n const gotBlob = await runUploadFile(logger, signer, lsDriver, uploadClient, pl, filePath);\n\n if (gotBlob.type.name !== \"Blob\") {\n return { status: \"failed\", message: `File uploading failed: ${gotBlob.type.name}` };\n }\n\n return { status: \"ok\", message: `File uploading succeeded: ${gotBlob.type.name}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<ResourceInfo> {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_blob\"],\n true,\n (tx) => ({\n file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)),\n }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, result.progress);\n\n if (isUpload(progress)) {\n const uploadData = ImportFileHandleUploadData.parse(\n JSON.parse(notEmpty(progress.data?.toString())),\n );\n const isUploadSignMatch = isSignMatch(signer, uploadData.localPath, uploadData.pathSignature);\n\n if (isUploadSignMatch) {\n await uploadBlob(logger, uploadClient, progress, uploadData, () => false, {\n nPartsWithThisUploadSpeed: 10,\n nPartsToIncreaseUpload: 10,\n currentSpeed: 10,\n maxSpeed: 10,\n });\n }\n }\n\n return await getFieldValue(pl, result.file);\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Uploads a file to the backend and then tries to download it back. */\nexport async function downloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n fileContent: string,\n): Promise<TemplateReport> {\n try {\n const gotFileContent = await runDownloadFile(\n logger,\n pl,\n lsDriver,\n uploadClient,\n downloadClient,\n filePath,\n );\n\n if (gotFileContent !== fileContent) {\n return {\n status: \"failed\",\n message: `File downloading failed: expected: ${fileContent}, got: ${gotFileContent}`,\n };\n }\n return { status: \"ok\", message: `File downloading succeeded: ${gotFileContent}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File downloading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runDownloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n) {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.download_blob\"],\n true,\n (tx) => ({ file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)) }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, outputs.progress);\n\n await uploadBlob(\n logger,\n uploadClient,\n progress,\n ImportFileHandleUploadData.parse(JSON.parse(notEmpty(progress.data?.toString()))),\n () => false,\n {\n nPartsWithThisUploadSpeed: 1,\n nPartsToIncreaseUpload: 1,\n currentSpeed: 1,\n maxSpeed: 1,\n },\n );\n\n const fileInfo = await getFieldValue(pl, outputs.file);\n\n return await downloadClient.withBlobContent(\n fileInfo,\n {},\n {},\n async (content) => await text(content),\n );\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n}\n\n/** Runs Go's hello-world binary. */\nexport async function softwareCheck(pl: PlClient): Promise<TemplateReport> {\n try {\n const gotGreeting = await runSoftware(pl);\n\n if (gotGreeting !== \"Hello from go binary\\n\") {\n return { status: \"failed\", message: `Software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runSoftware(pl: PlClient): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world\"],\n true,\n (_: PlTransaction) => ({}),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Runs Python hello-world. */\nexport async function pythonSoftware(pl: PlClient, name: string): Promise<TemplateReport> {\n try {\n const gotGreeting = await runPythonSoftware(pl, name);\n\n if (gotGreeting !== `Hello, ${name}!\\n`) {\n return { status: \"failed\", message: `Python software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Python software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Python software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runPythonSoftware(pl: PlClient, name: string): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world_py\"],\n true,\n (tx) => ({ name: tx.createValue(Pl.JsonObject, JSON.stringify(name)) }),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Tries to download a file from every storage. */\nexport async function downloadFromEveryStorage(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n ops: {\n minLsRequests: number;\n bytesLimit: number;\n minFileSize: number;\n maxFileSize: number;\n nFilesToCheck: number;\n },\n): Promise<Record<string, TemplateReport>> {\n try {\n const storages = await lsDriver.getStorageList();\n const results: Record<string, TemplateReport> = {};\n\n for (const storage of storages) {\n const result = await chooseFile(\n lsDriver,\n storage,\n ops.nFilesToCheck,\n ops.minFileSize,\n ops.maxFileSize,\n ops.minLsRequests,\n );\n if (result.file === undefined) {\n results[storage.name] = {\n status: \"warn\",\n message:\n `No file between ${ops.minFileSize} and ${ops.maxFileSize} bytes ` +\n `found in storage ${storage.name}, checked ${result.nCheckedFiles} files, ` +\n `did ${result.nLsRequests} ls requests`,\n };\n continue;\n }\n\n logger.info(`Downloading file ${JSON.stringify(result)} from storage ${storage.name}`);\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.create_workdir_from_storage\"],\n true,\n (tx) => ({\n file: tx.createValue(\n Pl.JsonObject,\n JSON.stringify((result.file as { handle: string }).handle),\n ),\n }),\n [\"workdirTypeName\"],\n );\n\n try {\n const workdirTypeName = JSON.parse(\n Buffer.from((await getFieldValue(pl, outputs.workdirTypeName)).data!).toString(),\n ) as string;\n\n if (workdirTypeName?.startsWith(\"WorkingDirectory\")) {\n results[storage.name] = {\n status: \"ok\",\n message:\n `Workdir creation succeeded, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n } else {\n results[storage.name] = {\n status: \"failed\",\n message:\n `Workdir creation failed: ${workdirTypeName}, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n }\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n\n return results;\n } catch (e: unknown) {\n return {\n unknown: {\n status: \"failed\",\n message: `Download from every storage failed: error occurred: ${e}`,\n },\n };\n }\n}\n\n/** Chooses a random file from the storage in a size range.\n * If we couldn't find a normal-sized file, we'll return a small file to check at least something.\n */\nexport async function chooseFile(\n lsDriver: LsDriver,\n storage: StorageEntry,\n limit: number,\n minSize: number,\n maxSize: number,\n minLsRequests: number,\n): Promise<{\n file: LsEntryWithAdditionalInfo | undefined;\n nLsRequests: number;\n nCheckedFiles: number;\n}> {\n const files = listFilesSequence(lsDriver, storage, \"\", 0);\n\n // return small file in case we don't have many normal-sized files.\n // While we'll download only a small range of bytes from the file,\n // we don't want to return a big file in case the underlying S3 doesn't support range requests.\n let smallFile: LsEntryWithAdditionalInfo | undefined;\n let nCheckedFiles = 0;\n let maxNLsRequests = 0;\n\n for await (const { file, nLsRequests } of files) {\n maxNLsRequests = Math.max(maxNLsRequests, nLsRequests);\n\n if (nCheckedFiles >= limit && maxNLsRequests > minLsRequests) {\n // we reached a limit on both the number of files and the number of ls requests.\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n }\n nCheckedFiles++;\n if (minSize <= file.size && file.size <= maxSize) {\n return { file, nLsRequests: maxNLsRequests, nCheckedFiles };\n } else if (file.size < minSize) {\n smallFile = file;\n }\n }\n\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n}\n\n/** Deep-first search for files in the storage. */\nexport async function* listFilesSequence(\n lsDriver: LsDriver,\n storage: StorageEntry,\n parent: string,\n nLsRequests: number,\n): AsyncGenerator<{ file: LsEntryWithAdditionalInfo; nLsRequests: number }, void, unknown> {\n nLsRequests++;\n const files = await lsDriver.listRemoteFilesWithAdditionalInfo(storage.handle, parent);\n\n for (const file of files.entries) {\n if (file.type === \"file\" && file.fullPath.startsWith(parent)) {\n yield {\n file,\n nLsRequests,\n };\n } else if (file.type === \"dir\") {\n for await (const nestedFile of listFilesSequence(\n lsDriver,\n storage,\n file.fullPath,\n nLsRequests,\n )) {\n nLsRequests = Math.max(nestedFile.nLsRequests, nLsRequests);\n yield nestedFile;\n }\n }\n }\n}\n\n/** Creates a big temporary file with random content. */\nexport async function createBigTempFile(): Promise<{ filePath: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-big-temp-${Date.now()}.bin`);\n const fileSize = 20 * 1024 * 1024; // 20 MiB\n\n const fileContent = randomBytes(fileSize);\n\n await fs.appendFile(filePath, fileContent);\n\n return { filePath };\n}\n\n/** Creates a temporarly file we could use for uploading and downloading. */\nexport async function createTempFile(): Promise<{ filePath: string; fileContent: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-temp-${Date.now()}.txt`);\n\n const fileContent = \"Hello, world! \" + new Date().toISOString();\n await fs.writeFile(filePath, fileContent);\n\n return { filePath, fileContent };\n}\n\n/** Creates a template and RenderTemplate resources, gets all resources from outputs.\n * Throws a error if any of the outputs failed.\n */\nasync function runTemplate(\n client: PlClient,\n tpl: TemplateSpecAny,\n ephemeral: boolean,\n inputs: (tx: PlTransaction) => Pl.PlRecord,\n outputs: string[],\n): Promise<Record<string, FieldId>> {\n return await client.withWriteTx(\"TemplateRender\", async (tx) => {\n const preparedTemplate = await prepareTemplateSpec(tpl);\n const tplResource = loadTemplate(tx, preparedTemplate);\n\n const outputFields: Record<string, FieldRef> = createRenderTemplate(\n tx,\n tplResource,\n ephemeral,\n inputs(tx),\n outputs,\n );\n\n const outputsIds: Record<string, FieldId> = {};\n\n for (const output of outputs) {\n const fieldRef = field(client.clientRoot, output);\n tx.createField(fieldRef, \"Dynamic\", outputFields[output]);\n outputsIds[output] = await toGlobalFieldId(fieldRef);\n }\n\n await tx.commit();\n\n return outputsIds;\n });\n}\n\n/** Gets a resource from field's value or throws a error. */\nasync function getFieldValue(client: PlClient, fieldId: FieldId): Promise<ResourceData> {\n // We could also do polling with pl-tree, but it seemed like an overkill,\n // that's why we have a simple polling here.\n\n return await poll(client, async (tx) => {\n const field = await tx.tx.getField(fieldId);\n if (isNotNullResourceId(field.error)) {\n const err = await tx.tx.getResourceData(field.error, true);\n throw new Error(`getFieldValue of \"${fieldId.fieldName}\" field failed: ${err.data}`);\n }\n\n if (isNullResourceId(field.value)) {\n throw new ContinuePolling();\n }\n\n return await tx.tx.getResourceData(field.value, true);\n });\n}\n\nasync function deleteFields(client: PlClient, fieldIds: FieldId[]) {\n await client.withWriteTx(\"DeleteFields\", async (tx) => {\n for (const fieldId of fieldIds) {\n tx.resetField(fieldId);\n }\n await tx.commit();\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"template.cjs","names":["SdkTemplates","Pl","ImportFileHandleUploadData","path","os","fs","createRenderTemplate","loadTemplate","prepareTemplateSpec","ContinuePolling"],"sources":["../../src/network_check/template.ts"],"sourcesContent":["import type { FieldId, FieldRef, PlClient, ResourceData } from \"@milaboratories/pl-client\";\nimport {\n type PlTransaction,\n ContinuePolling,\n field,\n isNotNullSignedResourceId,\n isNullSignedResourceId,\n Pl,\n poll,\n toGlobalFieldId,\n} from \"@milaboratories/pl-client\";\nimport { createRenderTemplate } from \"../mutator/template/render_template\";\nimport { Templates as SdkTemplates } from \"@platforma-sdk/workflow-tengo\";\nimport type { TemplateSpecAny } from \"../model/template_spec\";\nimport { loadTemplate, prepareTemplateSpec } from \"../mutator/template/template_loading\";\nimport type { ClientDownload, LsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n ImportFileHandleUploadData,\n isSignMatch,\n isUpload,\n uploadBlob,\n type ClientUpload,\n type LsEntryWithAdditionalInfo,\n} from \"@milaboratories/pl-drivers\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { notEmpty, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { text } from \"node:stream/consumers\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport type { StorageEntry } from \"@milaboratories/pl-model-common\";\n\nexport interface TemplateReport {\n status: \"ok\" | \"warn\" | \"failed\";\n message: string;\n}\n\n/** Uploads `hello-world` template and checks the output is correct. */\nexport async function uploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<TemplateReport> {\n try {\n const gotGreeting = await runUploadTemplate(logger, pl, name);\n if (gotGreeting !== `Hello, ${name}`) {\n return {\n status: \"failed\",\n message: `Template uploading failed: expected: ${name}, got: ${gotGreeting}`,\n };\n }\n\n return { status: \"ok\", message: `Template uploading succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Template uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<string> {\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_template\"],\n true,\n (tx) => ({\n name: tx.createValue(Pl.JsonObject, JSON.stringify(name)),\n }),\n [\"greeting\"],\n );\n\n try {\n return JSON.parse(notEmpty((await getFieldValue(pl, outputs.greeting)).data?.toString()));\n } finally {\n if (outputs != undefined) {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n}\n\n/** Uploads a file to the backend and checks the output is a Blob resource. */\nexport async function uploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<TemplateReport> {\n try {\n const gotBlob = await runUploadFile(logger, signer, lsDriver, uploadClient, pl, filePath);\n\n if (gotBlob.type.name !== \"Blob\") {\n return { status: \"failed\", message: `File uploading failed: ${gotBlob.type.name}` };\n }\n\n return { status: \"ok\", message: `File uploading succeeded: ${gotBlob.type.name}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<ResourceInfo> {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_blob\"],\n true,\n (tx) => ({\n file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)),\n }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, result.progress);\n\n if (isUpload(progress)) {\n const uploadData = ImportFileHandleUploadData.parse(\n JSON.parse(notEmpty(progress.data?.toString())),\n );\n const isUploadSignMatch = isSignMatch(signer, uploadData.localPath, uploadData.pathSignature);\n\n if (isUploadSignMatch) {\n await uploadBlob(logger, uploadClient, progress, uploadData, () => false, {\n nPartsWithThisUploadSpeed: 10,\n nPartsToIncreaseUpload: 10,\n currentSpeed: 10,\n maxSpeed: 10,\n });\n }\n }\n\n return await getFieldValue(pl, result.file);\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Uploads a file to the backend and then tries to download it back. */\nexport async function downloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n fileContent: string,\n): Promise<TemplateReport> {\n try {\n const gotFileContent = await runDownloadFile(\n logger,\n pl,\n lsDriver,\n uploadClient,\n downloadClient,\n filePath,\n );\n\n if (gotFileContent !== fileContent) {\n return {\n status: \"failed\",\n message: `File downloading failed: expected: ${fileContent}, got: ${gotFileContent}`,\n };\n }\n return { status: \"ok\", message: `File downloading succeeded: ${gotFileContent}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File downloading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runDownloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n) {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.download_blob\"],\n true,\n (tx) => ({ file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)) }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, outputs.progress);\n\n await uploadBlob(\n logger,\n uploadClient,\n progress,\n ImportFileHandleUploadData.parse(JSON.parse(notEmpty(progress.data?.toString()))),\n () => false,\n {\n nPartsWithThisUploadSpeed: 1,\n nPartsToIncreaseUpload: 1,\n currentSpeed: 1,\n maxSpeed: 1,\n },\n );\n\n const fileInfo = await getFieldValue(pl, outputs.file);\n\n return await downloadClient.withBlobContent(\n fileInfo,\n {},\n {},\n async (content) => await text(content),\n );\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n}\n\n/** Runs Go's hello-world binary. */\nexport async function softwareCheck(pl: PlClient): Promise<TemplateReport> {\n try {\n const gotGreeting = await runSoftware(pl);\n\n if (gotGreeting !== \"Hello from go binary\\n\") {\n return { status: \"failed\", message: `Software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runSoftware(pl: PlClient): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world\"],\n true,\n (_: PlTransaction) => ({}),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Runs Python hello-world. */\nexport async function pythonSoftware(pl: PlClient, name: string): Promise<TemplateReport> {\n try {\n const gotGreeting = await runPythonSoftware(pl, name);\n\n if (gotGreeting !== `Hello, ${name}!\\n`) {\n return { status: \"failed\", message: `Python software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Python software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Python software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runPythonSoftware(pl: PlClient, name: string): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world_py\"],\n true,\n (tx) => ({ name: tx.createValue(Pl.JsonObject, JSON.stringify(name)) }),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Tries to download a file from every storage. */\nexport async function downloadFromEveryStorage(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n ops: {\n minLsRequests: number;\n bytesLimit: number;\n minFileSize: number;\n maxFileSize: number;\n nFilesToCheck: number;\n },\n): Promise<Record<string, TemplateReport>> {\n try {\n const storages = await lsDriver.getStorageList();\n const results: Record<string, TemplateReport> = {};\n\n for (const storage of storages) {\n const result = await chooseFile(\n lsDriver,\n storage,\n ops.nFilesToCheck,\n ops.minFileSize,\n ops.maxFileSize,\n ops.minLsRequests,\n );\n if (result.file === undefined) {\n results[storage.id] = {\n status: \"warn\",\n message:\n `No file between ${ops.minFileSize} and ${ops.maxFileSize} bytes ` +\n `found in storage ${storage.name}, checked ${result.nCheckedFiles} files, ` +\n `did ${result.nLsRequests} ls requests`,\n };\n continue;\n }\n\n logger.info(`Downloading file ${JSON.stringify(result)} from storage ${storage.name}`);\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.create_workdir_from_storage\"],\n true,\n (tx) => ({\n file: tx.createValue(\n Pl.JsonObject,\n JSON.stringify((result.file as { handle: string }).handle),\n ),\n }),\n [\"workdirTypeName\"],\n );\n\n try {\n const workdirTypeName = JSON.parse(\n Buffer.from((await getFieldValue(pl, outputs.workdirTypeName)).data!).toString(),\n ) as string;\n\n if (workdirTypeName?.startsWith(\"WorkingDirectory\")) {\n results[storage.id] = {\n status: \"ok\",\n message:\n `Workdir creation succeeded, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n } else {\n results[storage.id] = {\n status: \"failed\",\n message:\n `Workdir creation failed: ${workdirTypeName}, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n }\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n\n return results;\n } catch (e: unknown) {\n return {\n unknown: {\n status: \"failed\",\n message: `Download from every storage failed: error occurred: ${e}`,\n },\n };\n }\n}\n\n/** Chooses a random file from the storage in a size range.\n * If we couldn't find a normal-sized file, we'll return a small file to check at least something.\n */\nexport async function chooseFile(\n lsDriver: LsDriver,\n storage: StorageEntry,\n limit: number,\n minSize: number,\n maxSize: number,\n minLsRequests: number,\n): Promise<{\n file: LsEntryWithAdditionalInfo | undefined;\n nLsRequests: number;\n nCheckedFiles: number;\n}> {\n const files = listFilesSequence(lsDriver, storage, \"\", 0);\n\n // return small file in case we don't have many normal-sized files.\n // While we'll download only a small range of bytes from the file,\n // we don't want to return a big file in case the underlying S3 doesn't support range requests.\n let smallFile: LsEntryWithAdditionalInfo | undefined;\n let nCheckedFiles = 0;\n let maxNLsRequests = 0;\n\n for await (const { file, nLsRequests } of files) {\n maxNLsRequests = Math.max(maxNLsRequests, nLsRequests);\n\n if (nCheckedFiles >= limit && maxNLsRequests > minLsRequests) {\n // we reached a limit on both the number of files and the number of ls requests.\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n }\n nCheckedFiles++;\n if (minSize <= file.size && file.size <= maxSize) {\n return { file, nLsRequests: maxNLsRequests, nCheckedFiles };\n } else if (file.size < minSize) {\n smallFile = file;\n }\n }\n\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n}\n\n/** Deep-first search for files in the storage. */\nexport async function* listFilesSequence(\n lsDriver: LsDriver,\n storage: StorageEntry,\n parent: string,\n nLsRequests: number,\n): AsyncGenerator<{ file: LsEntryWithAdditionalInfo; nLsRequests: number }, void, unknown> {\n nLsRequests++;\n const files = await lsDriver.listRemoteFilesWithAdditionalInfo(storage.handle, parent);\n\n for (const file of files.entries) {\n if (file.type === \"file\" && file.fullPath.startsWith(parent)) {\n yield {\n file,\n nLsRequests,\n };\n } else if (file.type === \"dir\") {\n for await (const nestedFile of listFilesSequence(\n lsDriver,\n storage,\n file.fullPath,\n nLsRequests,\n )) {\n nLsRequests = Math.max(nestedFile.nLsRequests, nLsRequests);\n yield nestedFile;\n }\n }\n }\n}\n\n/** Creates a big temporary file with random content. */\nexport async function createBigTempFile(): Promise<{ filePath: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-big-temp-${Date.now()}.bin`);\n const fileSize = 20 * 1024 * 1024; // 20 MiB\n\n const fileContent = randomBytes(fileSize);\n\n await fs.appendFile(filePath, fileContent);\n\n return { filePath };\n}\n\n/** Creates a temporarly file we could use for uploading and downloading. */\nexport async function createTempFile(): Promise<{ filePath: string; fileContent: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-temp-${Date.now()}.txt`);\n\n const fileContent = \"Hello, world! \" + new Date().toISOString();\n await fs.writeFile(filePath, fileContent);\n\n return { filePath, fileContent };\n}\n\n/** Creates a template and RenderTemplate resources, gets all resources from outputs.\n * Throws a error if any of the outputs failed.\n */\nasync function runTemplate(\n client: PlClient,\n tpl: TemplateSpecAny,\n ephemeral: boolean,\n inputs: (tx: PlTransaction) => Pl.PlRecord,\n outputs: string[],\n): Promise<Record<string, FieldId>> {\n return await client.withWriteTx(\"TemplateRender\", async (tx) => {\n const preparedTemplate = await prepareTemplateSpec(tpl);\n const tplResource = loadTemplate(tx, preparedTemplate);\n\n const outputFields: Record<string, FieldRef> = createRenderTemplate(\n tx,\n tplResource,\n ephemeral,\n inputs(tx),\n outputs,\n );\n\n const outputsIds: Record<string, FieldId> = {};\n\n for (const output of outputs) {\n const fieldRef = field(client.clientRoot, output) as FieldId;\n tx.createField(fieldRef, \"Dynamic\", outputFields[output]);\n outputsIds[output] = await toGlobalFieldId(fieldRef);\n }\n\n await tx.commit();\n\n return outputsIds;\n });\n}\n\n/** Gets a resource from field's value or throws a error. */\nasync function getFieldValue(client: PlClient, fieldId: FieldId): Promise<ResourceData> {\n // We could also do polling with pl-tree, but it seemed like an overkill,\n // that's why we have a simple polling here.\n\n return await poll(client, async (tx) => {\n const field = await tx.tx.getField(fieldId);\n if (isNotNullSignedResourceId(field.error)) {\n const err = await tx.tx.getResourceData(field.error, true);\n throw new Error(`getFieldValue of \"${fieldId.fieldName}\" field failed: ${err.data}`);\n }\n\n if (isNullSignedResourceId(field.value)) {\n throw new ContinuePolling();\n }\n\n return await tx.tx.getResourceData(field.value, true);\n });\n}\n\nasync function deleteFields(client: PlClient, fieldIds: FieldId[]) {\n await client.withWriteTx(\"DeleteFields\", async (tx) => {\n for (const fieldId of fieldIds) {\n tx.resetField(fieldId);\n }\n await tx.commit();\n });\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { loadTemplate, prepareTemplateSpec } from "../mutator/template/template_
3
3
  import path from "node:path";
4
4
  import { notEmpty } from "@milaboratories/ts-helpers";
5
5
  import fs from "node:fs/promises";
6
- import { ContinuePolling, Pl, field, isNotNullResourceId, isNullResourceId, poll, toGlobalFieldId } from "@milaboratories/pl-client";
6
+ import { ContinuePolling, Pl, field, isNotNullSignedResourceId, isNullSignedResourceId, poll, toGlobalFieldId } from "@milaboratories/pl-client";
7
7
  import { randomBytes } from "node:crypto";
8
8
  import { Templates } from "@platforma-sdk/workflow-tengo";
9
9
  import { ImportFileHandleUploadData, isSignMatch, isUpload, uploadBlob } from "@milaboratories/pl-drivers";
@@ -173,7 +173,7 @@ async function downloadFromEveryStorage(logger, pl, lsDriver, ops) {
173
173
  for (const storage of storages) {
174
174
  const result = await chooseFile(lsDriver, storage, ops.nFilesToCheck, ops.minFileSize, ops.maxFileSize, ops.minLsRequests);
175
175
  if (result.file === void 0) {
176
- results[storage.name] = {
176
+ results[storage.id] = {
177
177
  status: "warn",
178
178
  message: `No file between ${ops.minFileSize} and ${ops.maxFileSize} bytes found in storage ${storage.name}, checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`
179
179
  };
@@ -183,11 +183,11 @@ async function downloadFromEveryStorage(logger, pl, lsDriver, ops) {
183
183
  const outputs = await runTemplate(pl, Templates["check_network.create_workdir_from_storage"], true, (tx) => ({ file: tx.createValue(Pl.JsonObject, JSON.stringify(result.file.handle)) }), ["workdirTypeName"]);
184
184
  try {
185
185
  const workdirTypeName = JSON.parse(Buffer.from((await getFieldValue(pl, outputs.workdirTypeName)).data).toString());
186
- if (workdirTypeName?.startsWith("WorkingDirectory")) results[storage.name] = {
186
+ if (workdirTypeName?.startsWith("WorkingDirectory")) results[storage.id] = {
187
187
  status: "ok",
188
188
  message: `Workdir creation succeeded, size of file: ${result.file?.size}, checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`
189
189
  };
190
- else results[storage.name] = {
190
+ else results[storage.id] = {
191
191
  status: "failed",
192
192
  message: `Workdir creation failed: ${workdirTypeName}, size of file: ${result.file?.size}, checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`
193
193
  };
@@ -282,11 +282,11 @@ async function runTemplate(client, tpl, ephemeral, inputs, outputs) {
282
282
  async function getFieldValue(client, fieldId) {
283
283
  return await poll(client, async (tx) => {
284
284
  const field = await tx.tx.getField(fieldId);
285
- if (isNotNullResourceId(field.error)) {
285
+ if (isNotNullSignedResourceId(field.error)) {
286
286
  const err = await tx.tx.getResourceData(field.error, true);
287
287
  throw new Error(`getFieldValue of "${fieldId.fieldName}" field failed: ${err.data}`);
288
288
  }
289
- if (isNullResourceId(field.value)) throw new ContinuePolling();
289
+ if (isNullSignedResourceId(field.value)) throw new ContinuePolling();
290
290
  return await tx.tx.getResourceData(field.value, true);
291
291
  });
292
292
  }
@@ -1 +1 @@
1
- {"version":3,"file":"template.js","names":["SdkTemplates"],"sources":["../../src/network_check/template.ts"],"sourcesContent":["import type { FieldId, FieldRef, PlClient, ResourceData } from \"@milaboratories/pl-client\";\nimport {\n type PlTransaction,\n ContinuePolling,\n field,\n isNotNullResourceId,\n isNullResourceId,\n Pl,\n poll,\n toGlobalFieldId,\n} from \"@milaboratories/pl-client\";\nimport { createRenderTemplate } from \"../mutator/template/render_template\";\nimport { Templates as SdkTemplates } from \"@platforma-sdk/workflow-tengo\";\nimport type { TemplateSpecAny } from \"../model/template_spec\";\nimport { loadTemplate, prepareTemplateSpec } from \"../mutator/template/template_loading\";\nimport type { ClientDownload, LsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n ImportFileHandleUploadData,\n isSignMatch,\n isUpload,\n uploadBlob,\n type ClientUpload,\n type LsEntryWithAdditionalInfo,\n} from \"@milaboratories/pl-drivers\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { notEmpty, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { text } from \"node:stream/consumers\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport type { StorageEntry } from \"@milaboratories/pl-model-common\";\n\nexport interface TemplateReport {\n status: \"ok\" | \"warn\" | \"failed\";\n message: string;\n}\n\n/** Uploads `hello-world` template and checks the output is correct. */\nexport async function uploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<TemplateReport> {\n try {\n const gotGreeting = await runUploadTemplate(logger, pl, name);\n if (gotGreeting !== `Hello, ${name}`) {\n return {\n status: \"failed\",\n message: `Template uploading failed: expected: ${name}, got: ${gotGreeting}`,\n };\n }\n\n return { status: \"ok\", message: `Template uploading succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Template uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<string> {\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_template\"],\n true,\n (tx) => ({\n name: tx.createValue(Pl.JsonObject, JSON.stringify(name)),\n }),\n [\"greeting\"],\n );\n\n try {\n return JSON.parse(notEmpty((await getFieldValue(pl, outputs.greeting)).data?.toString()));\n } finally {\n if (outputs != undefined) {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n}\n\n/** Uploads a file to the backend and checks the output is a Blob resource. */\nexport async function uploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<TemplateReport> {\n try {\n const gotBlob = await runUploadFile(logger, signer, lsDriver, uploadClient, pl, filePath);\n\n if (gotBlob.type.name !== \"Blob\") {\n return { status: \"failed\", message: `File uploading failed: ${gotBlob.type.name}` };\n }\n\n return { status: \"ok\", message: `File uploading succeeded: ${gotBlob.type.name}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<ResourceInfo> {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_blob\"],\n true,\n (tx) => ({\n file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)),\n }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, result.progress);\n\n if (isUpload(progress)) {\n const uploadData = ImportFileHandleUploadData.parse(\n JSON.parse(notEmpty(progress.data?.toString())),\n );\n const isUploadSignMatch = isSignMatch(signer, uploadData.localPath, uploadData.pathSignature);\n\n if (isUploadSignMatch) {\n await uploadBlob(logger, uploadClient, progress, uploadData, () => false, {\n nPartsWithThisUploadSpeed: 10,\n nPartsToIncreaseUpload: 10,\n currentSpeed: 10,\n maxSpeed: 10,\n });\n }\n }\n\n return await getFieldValue(pl, result.file);\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Uploads a file to the backend and then tries to download it back. */\nexport async function downloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n fileContent: string,\n): Promise<TemplateReport> {\n try {\n const gotFileContent = await runDownloadFile(\n logger,\n pl,\n lsDriver,\n uploadClient,\n downloadClient,\n filePath,\n );\n\n if (gotFileContent !== fileContent) {\n return {\n status: \"failed\",\n message: `File downloading failed: expected: ${fileContent}, got: ${gotFileContent}`,\n };\n }\n return { status: \"ok\", message: `File downloading succeeded: ${gotFileContent}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File downloading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runDownloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n) {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.download_blob\"],\n true,\n (tx) => ({ file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)) }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, outputs.progress);\n\n await uploadBlob(\n logger,\n uploadClient,\n progress,\n ImportFileHandleUploadData.parse(JSON.parse(notEmpty(progress.data?.toString()))),\n () => false,\n {\n nPartsWithThisUploadSpeed: 1,\n nPartsToIncreaseUpload: 1,\n currentSpeed: 1,\n maxSpeed: 1,\n },\n );\n\n const fileInfo = await getFieldValue(pl, outputs.file);\n\n return await downloadClient.withBlobContent(\n fileInfo,\n {},\n {},\n async (content) => await text(content),\n );\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n}\n\n/** Runs Go's hello-world binary. */\nexport async function softwareCheck(pl: PlClient): Promise<TemplateReport> {\n try {\n const gotGreeting = await runSoftware(pl);\n\n if (gotGreeting !== \"Hello from go binary\\n\") {\n return { status: \"failed\", message: `Software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runSoftware(pl: PlClient): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world\"],\n true,\n (_: PlTransaction) => ({}),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Runs Python hello-world. */\nexport async function pythonSoftware(pl: PlClient, name: string): Promise<TemplateReport> {\n try {\n const gotGreeting = await runPythonSoftware(pl, name);\n\n if (gotGreeting !== `Hello, ${name}!\\n`) {\n return { status: \"failed\", message: `Python software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Python software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Python software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runPythonSoftware(pl: PlClient, name: string): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world_py\"],\n true,\n (tx) => ({ name: tx.createValue(Pl.JsonObject, JSON.stringify(name)) }),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Tries to download a file from every storage. */\nexport async function downloadFromEveryStorage(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n ops: {\n minLsRequests: number;\n bytesLimit: number;\n minFileSize: number;\n maxFileSize: number;\n nFilesToCheck: number;\n },\n): Promise<Record<string, TemplateReport>> {\n try {\n const storages = await lsDriver.getStorageList();\n const results: Record<string, TemplateReport> = {};\n\n for (const storage of storages) {\n const result = await chooseFile(\n lsDriver,\n storage,\n ops.nFilesToCheck,\n ops.minFileSize,\n ops.maxFileSize,\n ops.minLsRequests,\n );\n if (result.file === undefined) {\n results[storage.name] = {\n status: \"warn\",\n message:\n `No file between ${ops.minFileSize} and ${ops.maxFileSize} bytes ` +\n `found in storage ${storage.name}, checked ${result.nCheckedFiles} files, ` +\n `did ${result.nLsRequests} ls requests`,\n };\n continue;\n }\n\n logger.info(`Downloading file ${JSON.stringify(result)} from storage ${storage.name}`);\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.create_workdir_from_storage\"],\n true,\n (tx) => ({\n file: tx.createValue(\n Pl.JsonObject,\n JSON.stringify((result.file as { handle: string }).handle),\n ),\n }),\n [\"workdirTypeName\"],\n );\n\n try {\n const workdirTypeName = JSON.parse(\n Buffer.from((await getFieldValue(pl, outputs.workdirTypeName)).data!).toString(),\n ) as string;\n\n if (workdirTypeName?.startsWith(\"WorkingDirectory\")) {\n results[storage.name] = {\n status: \"ok\",\n message:\n `Workdir creation succeeded, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n } else {\n results[storage.name] = {\n status: \"failed\",\n message:\n `Workdir creation failed: ${workdirTypeName}, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n }\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n\n return results;\n } catch (e: unknown) {\n return {\n unknown: {\n status: \"failed\",\n message: `Download from every storage failed: error occurred: ${e}`,\n },\n };\n }\n}\n\n/** Chooses a random file from the storage in a size range.\n * If we couldn't find a normal-sized file, we'll return a small file to check at least something.\n */\nexport async function chooseFile(\n lsDriver: LsDriver,\n storage: StorageEntry,\n limit: number,\n minSize: number,\n maxSize: number,\n minLsRequests: number,\n): Promise<{\n file: LsEntryWithAdditionalInfo | undefined;\n nLsRequests: number;\n nCheckedFiles: number;\n}> {\n const files = listFilesSequence(lsDriver, storage, \"\", 0);\n\n // return small file in case we don't have many normal-sized files.\n // While we'll download only a small range of bytes from the file,\n // we don't want to return a big file in case the underlying S3 doesn't support range requests.\n let smallFile: LsEntryWithAdditionalInfo | undefined;\n let nCheckedFiles = 0;\n let maxNLsRequests = 0;\n\n for await (const { file, nLsRequests } of files) {\n maxNLsRequests = Math.max(maxNLsRequests, nLsRequests);\n\n if (nCheckedFiles >= limit && maxNLsRequests > minLsRequests) {\n // we reached a limit on both the number of files and the number of ls requests.\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n }\n nCheckedFiles++;\n if (minSize <= file.size && file.size <= maxSize) {\n return { file, nLsRequests: maxNLsRequests, nCheckedFiles };\n } else if (file.size < minSize) {\n smallFile = file;\n }\n }\n\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n}\n\n/** Deep-first search for files in the storage. */\nexport async function* listFilesSequence(\n lsDriver: LsDriver,\n storage: StorageEntry,\n parent: string,\n nLsRequests: number,\n): AsyncGenerator<{ file: LsEntryWithAdditionalInfo; nLsRequests: number }, void, unknown> {\n nLsRequests++;\n const files = await lsDriver.listRemoteFilesWithAdditionalInfo(storage.handle, parent);\n\n for (const file of files.entries) {\n if (file.type === \"file\" && file.fullPath.startsWith(parent)) {\n yield {\n file,\n nLsRequests,\n };\n } else if (file.type === \"dir\") {\n for await (const nestedFile of listFilesSequence(\n lsDriver,\n storage,\n file.fullPath,\n nLsRequests,\n )) {\n nLsRequests = Math.max(nestedFile.nLsRequests, nLsRequests);\n yield nestedFile;\n }\n }\n }\n}\n\n/** Creates a big temporary file with random content. */\nexport async function createBigTempFile(): Promise<{ filePath: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-big-temp-${Date.now()}.bin`);\n const fileSize = 20 * 1024 * 1024; // 20 MiB\n\n const fileContent = randomBytes(fileSize);\n\n await fs.appendFile(filePath, fileContent);\n\n return { filePath };\n}\n\n/** Creates a temporarly file we could use for uploading and downloading. */\nexport async function createTempFile(): Promise<{ filePath: string; fileContent: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-temp-${Date.now()}.txt`);\n\n const fileContent = \"Hello, world! \" + new Date().toISOString();\n await fs.writeFile(filePath, fileContent);\n\n return { filePath, fileContent };\n}\n\n/** Creates a template and RenderTemplate resources, gets all resources from outputs.\n * Throws a error if any of the outputs failed.\n */\nasync function runTemplate(\n client: PlClient,\n tpl: TemplateSpecAny,\n ephemeral: boolean,\n inputs: (tx: PlTransaction) => Pl.PlRecord,\n outputs: string[],\n): Promise<Record<string, FieldId>> {\n return await client.withWriteTx(\"TemplateRender\", async (tx) => {\n const preparedTemplate = await prepareTemplateSpec(tpl);\n const tplResource = loadTemplate(tx, preparedTemplate);\n\n const outputFields: Record<string, FieldRef> = createRenderTemplate(\n tx,\n tplResource,\n ephemeral,\n inputs(tx),\n outputs,\n );\n\n const outputsIds: Record<string, FieldId> = {};\n\n for (const output of outputs) {\n const fieldRef = field(client.clientRoot, output);\n tx.createField(fieldRef, \"Dynamic\", outputFields[output]);\n outputsIds[output] = await toGlobalFieldId(fieldRef);\n }\n\n await tx.commit();\n\n return outputsIds;\n });\n}\n\n/** Gets a resource from field's value or throws a error. */\nasync function getFieldValue(client: PlClient, fieldId: FieldId): Promise<ResourceData> {\n // We could also do polling with pl-tree, but it seemed like an overkill,\n // that's why we have a simple polling here.\n\n return await poll(client, async (tx) => {\n const field = await tx.tx.getField(fieldId);\n if (isNotNullResourceId(field.error)) {\n const err = await tx.tx.getResourceData(field.error, true);\n throw new Error(`getFieldValue of \"${fieldId.fieldName}\" field failed: ${err.data}`);\n }\n\n if (isNullResourceId(field.value)) {\n throw new ContinuePolling();\n }\n\n return await tx.tx.getResourceData(field.value, true);\n });\n}\n\nasync function deleteFields(client: PlClient, fieldIds: FieldId[]) {\n await client.withWriteTx(\"DeleteFields\", async (tx) => {\n for (const fieldId of fieldIds) {\n tx.resetField(fieldId);\n }\n await tx.commit();\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"template.js","names":["SdkTemplates"],"sources":["../../src/network_check/template.ts"],"sourcesContent":["import type { FieldId, FieldRef, PlClient, ResourceData } from \"@milaboratories/pl-client\";\nimport {\n type PlTransaction,\n ContinuePolling,\n field,\n isNotNullSignedResourceId,\n isNullSignedResourceId,\n Pl,\n poll,\n toGlobalFieldId,\n} from \"@milaboratories/pl-client\";\nimport { createRenderTemplate } from \"../mutator/template/render_template\";\nimport { Templates as SdkTemplates } from \"@platforma-sdk/workflow-tengo\";\nimport type { TemplateSpecAny } from \"../model/template_spec\";\nimport { loadTemplate, prepareTemplateSpec } from \"../mutator/template/template_loading\";\nimport type { ClientDownload, LsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n ImportFileHandleUploadData,\n isSignMatch,\n isUpload,\n uploadBlob,\n type ClientUpload,\n type LsEntryWithAdditionalInfo,\n} from \"@milaboratories/pl-drivers\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { notEmpty, type MiLogger } from \"@milaboratories/ts-helpers\";\nimport type { ResourceInfo } from \"@milaboratories/pl-tree\";\nimport { text } from \"node:stream/consumers\";\nimport path from \"node:path\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport { randomBytes } from \"node:crypto\";\nimport type { StorageEntry } from \"@milaboratories/pl-model-common\";\n\nexport interface TemplateReport {\n status: \"ok\" | \"warn\" | \"failed\";\n message: string;\n}\n\n/** Uploads `hello-world` template and checks the output is correct. */\nexport async function uploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<TemplateReport> {\n try {\n const gotGreeting = await runUploadTemplate(logger, pl, name);\n if (gotGreeting !== `Hello, ${name}`) {\n return {\n status: \"failed\",\n message: `Template uploading failed: expected: ${name}, got: ${gotGreeting}`,\n };\n }\n\n return { status: \"ok\", message: `Template uploading succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Template uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadTemplate(\n logger: MiLogger,\n pl: PlClient,\n name: string,\n): Promise<string> {\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_template\"],\n true,\n (tx) => ({\n name: tx.createValue(Pl.JsonObject, JSON.stringify(name)),\n }),\n [\"greeting\"],\n );\n\n try {\n return JSON.parse(notEmpty((await getFieldValue(pl, outputs.greeting)).data?.toString()));\n } finally {\n if (outputs != undefined) {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n}\n\n/** Uploads a file to the backend and checks the output is a Blob resource. */\nexport async function uploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<TemplateReport> {\n try {\n const gotBlob = await runUploadFile(logger, signer, lsDriver, uploadClient, pl, filePath);\n\n if (gotBlob.type.name !== \"Blob\") {\n return { status: \"failed\", message: `File uploading failed: ${gotBlob.type.name}` };\n }\n\n return { status: \"ok\", message: `File uploading succeeded: ${gotBlob.type.name}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File uploading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runUploadFile(\n logger: MiLogger,\n signer: Signer,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n pl: PlClient,\n filePath: string,\n): Promise<ResourceInfo> {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.upload_blob\"],\n true,\n (tx) => ({\n file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)),\n }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, result.progress);\n\n if (isUpload(progress)) {\n const uploadData = ImportFileHandleUploadData.parse(\n JSON.parse(notEmpty(progress.data?.toString())),\n );\n const isUploadSignMatch = isSignMatch(signer, uploadData.localPath, uploadData.pathSignature);\n\n if (isUploadSignMatch) {\n await uploadBlob(logger, uploadClient, progress, uploadData, () => false, {\n nPartsWithThisUploadSpeed: 10,\n nPartsToIncreaseUpload: 10,\n currentSpeed: 10,\n maxSpeed: 10,\n });\n }\n }\n\n return await getFieldValue(pl, result.file);\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Uploads a file to the backend and then tries to download it back. */\nexport async function downloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n fileContent: string,\n): Promise<TemplateReport> {\n try {\n const gotFileContent = await runDownloadFile(\n logger,\n pl,\n lsDriver,\n uploadClient,\n downloadClient,\n filePath,\n );\n\n if (gotFileContent !== fileContent) {\n return {\n status: \"failed\",\n message: `File downloading failed: expected: ${fileContent}, got: ${gotFileContent}`,\n };\n }\n return { status: \"ok\", message: `File downloading succeeded: ${gotFileContent}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `File downloading failed: error occurred: ${e}` };\n }\n}\n\nexport async function runDownloadFile(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n uploadClient: ClientUpload,\n downloadClient: ClientDownload,\n filePath: string,\n) {\n const handle = await lsDriver.getLocalFileHandle(filePath);\n\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.download_blob\"],\n true,\n (tx) => ({ file: tx.createValue(Pl.JsonObject, JSON.stringify(handle)) }),\n [\"progress\", \"file\"],\n );\n\n try {\n const progress = await getFieldValue(pl, outputs.progress);\n\n await uploadBlob(\n logger,\n uploadClient,\n progress,\n ImportFileHandleUploadData.parse(JSON.parse(notEmpty(progress.data?.toString()))),\n () => false,\n {\n nPartsWithThisUploadSpeed: 1,\n nPartsToIncreaseUpload: 1,\n currentSpeed: 1,\n maxSpeed: 1,\n },\n );\n\n const fileInfo = await getFieldValue(pl, outputs.file);\n\n return await downloadClient.withBlobContent(\n fileInfo,\n {},\n {},\n async (content) => await text(content),\n );\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n}\n\n/** Runs Go's hello-world binary. */\nexport async function softwareCheck(pl: PlClient): Promise<TemplateReport> {\n try {\n const gotGreeting = await runSoftware(pl);\n\n if (gotGreeting !== \"Hello from go binary\\n\") {\n return { status: \"failed\", message: `Software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runSoftware(pl: PlClient): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world\"],\n true,\n (_: PlTransaction) => ({}),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Runs Python hello-world. */\nexport async function pythonSoftware(pl: PlClient, name: string): Promise<TemplateReport> {\n try {\n const gotGreeting = await runPythonSoftware(pl, name);\n\n if (gotGreeting !== `Hello, ${name}!\\n`) {\n return { status: \"failed\", message: `Python software check failed: got: ${gotGreeting}` };\n }\n return { status: \"ok\", message: `Python software check succeeded: ${gotGreeting}` };\n } catch (e: unknown) {\n return { status: \"failed\", message: `Python software check failed: error occurred: ${e}` };\n }\n}\n\nexport async function runPythonSoftware(pl: PlClient, name: string): Promise<string> {\n const result = await runTemplate(\n pl,\n SdkTemplates[\"check_network.run_hello_world_py\"],\n true,\n (tx) => ({ name: tx.createValue(Pl.JsonObject, JSON.stringify(name)) }),\n [\"greeting\"],\n );\n\n try {\n return notEmpty((await getFieldValue(pl, result.greeting)).data?.toString());\n } finally {\n await deleteFields(pl, Object.values(result));\n }\n}\n\n/** Tries to download a file from every storage. */\nexport async function downloadFromEveryStorage(\n logger: MiLogger,\n pl: PlClient,\n lsDriver: LsDriver,\n ops: {\n minLsRequests: number;\n bytesLimit: number;\n minFileSize: number;\n maxFileSize: number;\n nFilesToCheck: number;\n },\n): Promise<Record<string, TemplateReport>> {\n try {\n const storages = await lsDriver.getStorageList();\n const results: Record<string, TemplateReport> = {};\n\n for (const storage of storages) {\n const result = await chooseFile(\n lsDriver,\n storage,\n ops.nFilesToCheck,\n ops.minFileSize,\n ops.maxFileSize,\n ops.minLsRequests,\n );\n if (result.file === undefined) {\n results[storage.id] = {\n status: \"warn\",\n message:\n `No file between ${ops.minFileSize} and ${ops.maxFileSize} bytes ` +\n `found in storage ${storage.name}, checked ${result.nCheckedFiles} files, ` +\n `did ${result.nLsRequests} ls requests`,\n };\n continue;\n }\n\n logger.info(`Downloading file ${JSON.stringify(result)} from storage ${storage.name}`);\n const outputs = await runTemplate(\n pl,\n SdkTemplates[\"check_network.create_workdir_from_storage\"],\n true,\n (tx) => ({\n file: tx.createValue(\n Pl.JsonObject,\n JSON.stringify((result.file as { handle: string }).handle),\n ),\n }),\n [\"workdirTypeName\"],\n );\n\n try {\n const workdirTypeName = JSON.parse(\n Buffer.from((await getFieldValue(pl, outputs.workdirTypeName)).data!).toString(),\n ) as string;\n\n if (workdirTypeName?.startsWith(\"WorkingDirectory\")) {\n results[storage.id] = {\n status: \"ok\",\n message:\n `Workdir creation succeeded, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n } else {\n results[storage.id] = {\n status: \"failed\",\n message:\n `Workdir creation failed: ${workdirTypeName}, size of file: ${result.file?.size}, ` +\n `checked ${result.nCheckedFiles} files, did ${result.nLsRequests} ls requests`,\n };\n }\n } finally {\n await deleteFields(pl, Object.values(outputs));\n }\n }\n\n return results;\n } catch (e: unknown) {\n return {\n unknown: {\n status: \"failed\",\n message: `Download from every storage failed: error occurred: ${e}`,\n },\n };\n }\n}\n\n/** Chooses a random file from the storage in a size range.\n * If we couldn't find a normal-sized file, we'll return a small file to check at least something.\n */\nexport async function chooseFile(\n lsDriver: LsDriver,\n storage: StorageEntry,\n limit: number,\n minSize: number,\n maxSize: number,\n minLsRequests: number,\n): Promise<{\n file: LsEntryWithAdditionalInfo | undefined;\n nLsRequests: number;\n nCheckedFiles: number;\n}> {\n const files = listFilesSequence(lsDriver, storage, \"\", 0);\n\n // return small file in case we don't have many normal-sized files.\n // While we'll download only a small range of bytes from the file,\n // we don't want to return a big file in case the underlying S3 doesn't support range requests.\n let smallFile: LsEntryWithAdditionalInfo | undefined;\n let nCheckedFiles = 0;\n let maxNLsRequests = 0;\n\n for await (const { file, nLsRequests } of files) {\n maxNLsRequests = Math.max(maxNLsRequests, nLsRequests);\n\n if (nCheckedFiles >= limit && maxNLsRequests > minLsRequests) {\n // we reached a limit on both the number of files and the number of ls requests.\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n }\n nCheckedFiles++;\n if (minSize <= file.size && file.size <= maxSize) {\n return { file, nLsRequests: maxNLsRequests, nCheckedFiles };\n } else if (file.size < minSize) {\n smallFile = file;\n }\n }\n\n return { file: smallFile, nLsRequests: maxNLsRequests, nCheckedFiles };\n}\n\n/** Deep-first search for files in the storage. */\nexport async function* listFilesSequence(\n lsDriver: LsDriver,\n storage: StorageEntry,\n parent: string,\n nLsRequests: number,\n): AsyncGenerator<{ file: LsEntryWithAdditionalInfo; nLsRequests: number }, void, unknown> {\n nLsRequests++;\n const files = await lsDriver.listRemoteFilesWithAdditionalInfo(storage.handle, parent);\n\n for (const file of files.entries) {\n if (file.type === \"file\" && file.fullPath.startsWith(parent)) {\n yield {\n file,\n nLsRequests,\n };\n } else if (file.type === \"dir\") {\n for await (const nestedFile of listFilesSequence(\n lsDriver,\n storage,\n file.fullPath,\n nLsRequests,\n )) {\n nLsRequests = Math.max(nestedFile.nLsRequests, nLsRequests);\n yield nestedFile;\n }\n }\n }\n}\n\n/** Creates a big temporary file with random content. */\nexport async function createBigTempFile(): Promise<{ filePath: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-big-temp-${Date.now()}.bin`);\n const fileSize = 20 * 1024 * 1024; // 20 MiB\n\n const fileContent = randomBytes(fileSize);\n\n await fs.appendFile(filePath, fileContent);\n\n return { filePath };\n}\n\n/** Creates a temporarly file we could use for uploading and downloading. */\nexport async function createTempFile(): Promise<{ filePath: string; fileContent: string }> {\n const filePath = path.join(os.tmpdir(), `check-network-temp-${Date.now()}.txt`);\n\n const fileContent = \"Hello, world! \" + new Date().toISOString();\n await fs.writeFile(filePath, fileContent);\n\n return { filePath, fileContent };\n}\n\n/** Creates a template and RenderTemplate resources, gets all resources from outputs.\n * Throws a error if any of the outputs failed.\n */\nasync function runTemplate(\n client: PlClient,\n tpl: TemplateSpecAny,\n ephemeral: boolean,\n inputs: (tx: PlTransaction) => Pl.PlRecord,\n outputs: string[],\n): Promise<Record<string, FieldId>> {\n return await client.withWriteTx(\"TemplateRender\", async (tx) => {\n const preparedTemplate = await prepareTemplateSpec(tpl);\n const tplResource = loadTemplate(tx, preparedTemplate);\n\n const outputFields: Record<string, FieldRef> = createRenderTemplate(\n tx,\n tplResource,\n ephemeral,\n inputs(tx),\n outputs,\n );\n\n const outputsIds: Record<string, FieldId> = {};\n\n for (const output of outputs) {\n const fieldRef = field(client.clientRoot, output) as FieldId;\n tx.createField(fieldRef, \"Dynamic\", outputFields[output]);\n outputsIds[output] = await toGlobalFieldId(fieldRef);\n }\n\n await tx.commit();\n\n return outputsIds;\n });\n}\n\n/** Gets a resource from field's value or throws a error. */\nasync function getFieldValue(client: PlClient, fieldId: FieldId): Promise<ResourceData> {\n // We could also do polling with pl-tree, but it seemed like an overkill,\n // that's why we have a simple polling here.\n\n return await poll(client, async (tx) => {\n const field = await tx.tx.getField(fieldId);\n if (isNotNullSignedResourceId(field.error)) {\n const err = await tx.tx.getResourceData(field.error, true);\n throw new Error(`getFieldValue of \"${fieldId.fieldName}\" field failed: ${err.data}`);\n }\n\n if (isNullSignedResourceId(field.value)) {\n throw new ContinuePolling();\n }\n\n return await tx.tx.getResourceData(field.value, true);\n });\n}\n\nasync function deleteFields(client: PlClient, fieldIds: FieldId[]) {\n await client.withWriteTx(\"DeleteFields\", async (tx) => {\n for (const fieldId of fieldIds) {\n tx.resetField(fieldId);\n }\n await tx.commit();\n });\n}\n"],"mappings":"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"}
@@ -227,7 +227,8 @@ function traverseParquetChunkResource(resource) {
227
227
  function deriveLegacyPObjectId(spec, data) {
228
228
  const hash = (0, node_crypto.createHash)("sha256");
229
229
  hash.update((0, canonicalize.default)(spec));
230
- hash.update(String(!(0, _milaboratories_pl_client.isNullResourceId)(data.originalId) ? data.originalId : data.id));
230
+ const rid = !(0, _milaboratories_pl_client.isNullSignedResourceId)(data.originalId) ? data.originalId : data.id;
231
+ hash.update(String((0, _milaboratories_pl_client.anyResourceIdToBigint)(rid)));
231
232
  return hash.digest().toString("hex");
232
233
  }
233
234
  function deriveGlobalPObjectId(blockId, exportName) {
@@ -1 +1 @@
1
- {"version":3,"file":"data.cjs","names":["OnDemandBlobResourceSnapshot","PFrameDriverError"],"sources":["../../src/pool/data.ts"],"sourcesContent":["import {\n PFrameDriverError,\n type BinaryChunk,\n type ParquetChunk,\n type ParquetChunkMapping,\n type ParquetChunkMetadata,\n type PColumnValue,\n type PlRef,\n type PObjectId,\n type PObjectSpec,\n} from \"@platforma-sdk/model\";\nimport { makeResourceSnapshot, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport canonicalize from \"canonicalize\";\nimport {\n isNullResourceId,\n resourceIdToString,\n resourceType,\n resourceTypeToString,\n resourceTypesEqual,\n type ResourceId,\n type ResourceType,\n} from \"@milaboratories/pl-client\";\nimport type { Writable } from \"utility-types\";\nimport { createHash } from \"node:crypto\";\nimport type { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { OnDemandBlobResourceSnapshot } from \"@milaboratories/pl-drivers\";\n\n/**\n * Tree-independent reference to a blob resource used by the PFrame data flow.\n *\n * The earlier design carried a {@link PlTreeEntry} all the way into the blob pools;\n * resolution then went back through the originating tree, so when that tree dropped\n * the resource (e.g., a project recalculated with new settings), shared pool entries\n * — held alive by other projects — would start failing with \"resource not found in\n * the tree\" even though the underlying blob was still valid backend-side.\n *\n * BlobResourceRef captures the snapshot at parse time, where the tree is guaranteed\n * to resolve. The pools then key by rid (`resourceInfo.id`) and call into the\n * download driver with the snapshot directly — independent of any specific tree.\n */\nexport class BlobResourceRef {\n constructor(\n public readonly resourceInfo: { readonly id: ResourceId; readonly type: ResourceType },\n /** Present only for on-demand (remote) blobs; needed for size and signed handle. */\n public readonly onDemandSnapshot: OnDemandBlobResourceSnapshot | undefined,\n ) {}\n\n toJSON(): string {\n return resourceIdToString(this.resourceInfo.id);\n }\n}\n\nexport function makeLocalBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(accessor.resourceInfo, undefined);\n}\n\nfunction makeRemoteBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(\n accessor.resourceInfo,\n makeResourceSnapshot(accessor, OnDemandBlobResourceSnapshot),\n );\n}\n\nexport const PColumnDataJsonPartitioned = resourceType(\"PColumnData/JsonPartitioned\", \"1\");\nexport const PColumnDataJsonSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/JsonPartitioned\",\n \"1\",\n);\nexport const PColumnDataBinaryPartitioned = resourceType(\"PColumnData/BinaryPartitioned\", \"1\");\nexport const PColumnDataBinarySuperPartitioned = resourceType(\n \"PColumnData/Partitioned/BinaryPartitioned\",\n \"1\",\n);\nexport const PColumnDataParquetPartitioned = resourceType(\"PColumnData/ParquetPartitioned\", \"1\");\nexport const PColumnDataParquetSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/ParquetPartitioned\",\n \"1\",\n);\nexport const PColumnDataJson = resourceType(\"PColumnData/Json\", \"1\");\n\nexport const ParquetChunkResourceType = resourceType(\"ParquetChunk\", \"1\");\n\nexport type PColumnDataJsonResourceValue = {\n keyLength: number;\n data: Record<string, PColumnValue>;\n};\n\nexport type PColumnDataPartitionedResourceValue = {\n partitionKeyLength: number;\n};\n\nexport type PColumnDataSuperPartitionedResourceValue = {\n superPartitionKeyLength: number;\n partitionKeyLength: number;\n};\n\nconst BinaryPartitionedIndexFieldSuffix = \".index\";\nconst BinaryPartitionedValuesFieldSuffix = \".values\";\n\nexport function parseDataInfoResource(\n data: PlTreeNodeAccessor,\n): PFrameInternal.DataInfo<BlobResourceRef> {\n if (!data.getIsReadyOrError()) throw new PFrameDriverError(\"Data not ready.\");\n\n const resourceData = data.getDataAsJson();\n if (resourceData === undefined)\n throw new PFrameDriverError(\"unexpected data info structure, no resource data\");\n\n if (resourceTypesEqual(data.resourceType, PColumnDataJson)) {\n const dataContent = resourceData as PColumnDataJsonResourceValue;\n\n return {\n type: \"Json\",\n keyLength: dataContent.keyLength,\n data: dataContent.data,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts = Object.fromEntries(\n data\n .listInputFields()\n .map((field) => [\n field,\n makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true })),\n ]),\n );\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, BlobResourceRef> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = makeLocalBlobRef(\n superPart.traverse({ field: key, errorIfFieldNotSet: true }),\n );\n }\n }\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinaryPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n\n // parsing the structure\n for (const field of data.listInputFields()) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.index = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.values = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n\n // structure validation\n for (const [key, part] of Object.entries(parts)) {\n if (part.index === undefined) throw new PFrameDriverError(`no index for part ${key}`);\n if (part.values === undefined) throw new PFrameDriverError(`no values for part ${key}`);\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinarySuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n for (const superKey of data.listInputFields()) {\n const superData = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superData.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const field of keys) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].index = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].values = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const key of data.listInputFields()) {\n const resource = data.traverse({\n field: key,\n assertFieldType: \"Input\",\n errorIfFieldNotSet: true,\n });\n\n parts[key] = traverseParquetChunkResource(resource);\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const resource = data.traverse({ field: key, errorIfFieldNotSet: true });\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = traverseParquetChunkResource(resource);\n }\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n }\n\n throw new PFrameDriverError(\n `unsupported resource type: ${resourceTypeToString(data.resourceType)}`,\n );\n}\n\nexport function traverseParquetChunkResource(\n resource: PlTreeNodeAccessor,\n): ParquetChunk<BlobResourceRef> {\n if (!resourceTypesEqual(resource.resourceType, ParquetChunkResourceType)) {\n throw new PFrameDriverError(\n `unknown resource type: ${resourceTypeToString(resource.resourceType)}, ` +\n `expected: ${resourceTypeToString(ParquetChunkResourceType)}`,\n );\n }\n\n const blob = makeRemoteBlobRef(\n resource.traverse({ field: \"blob\", assertFieldType: \"Service\", errorIfFieldNotSet: true }),\n );\n const partInfo = resource.getDataAsJson() as ParquetChunkMetadata;\n const mapping = resource\n .traverse({ field: \"mapping\", assertFieldType: \"Service\", errorIfFieldNotSet: true })\n .getDataAsJson() as ParquetChunkMapping;\n\n return {\n data: blob,\n ...partInfo,\n ...mapping,\n };\n}\n\nexport function deriveLegacyPObjectId(spec: PObjectSpec, data: PlTreeNodeAccessor): PObjectId {\n const hash = createHash(\"sha256\");\n hash.update(canonicalize(spec)!);\n hash.update(String(!isNullResourceId(data.originalId) ? data.originalId : data.id));\n return hash.digest().toString(\"hex\") as PObjectId;\n}\n\nexport function deriveGlobalPObjectId(blockId: string, exportName: string): PObjectId {\n return canonicalize({ __isRef: true, blockId, name: exportName } satisfies PlRef)! as PObjectId;\n}\n\nexport function deriveLocalPObjectId(resolvePath: string[], outputName: string): PObjectId {\n return canonicalize({ resolvePath, name: outputName })! as PObjectId;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"data.cjs","names":["OnDemandBlobResourceSnapshot","PFrameDriverError"],"sources":["../../src/pool/data.ts"],"sourcesContent":["import {\n PFrameDriverError,\n type BinaryChunk,\n type ParquetChunk,\n type ParquetChunkMapping,\n type ParquetChunkMetadata,\n type PColumnValue,\n type PlRef,\n type PObjectId,\n type PObjectSpec,\n} from \"@platforma-sdk/model\";\nimport { makeResourceSnapshot, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport canonicalize from \"canonicalize\";\nimport {\n anyResourceIdToBigint,\n isNullSignedResourceId,\n resourceIdToString,\n resourceType,\n resourceTypeToString,\n resourceTypesEqual,\n type SignedResourceId,\n type ResourceType,\n} from \"@milaboratories/pl-client\";\nimport type { Writable } from \"utility-types\";\nimport { createHash } from \"node:crypto\";\nimport type { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { OnDemandBlobResourceSnapshot } from \"@milaboratories/pl-drivers\";\n\n/**\n * Tree-independent reference to a blob resource used by the PFrame data flow.\n *\n * The earlier design carried a {@link PlTreeEntry} all the way into the blob pools;\n * resolution then went back through the originating tree, so when that tree dropped\n * the resource (e.g., a project recalculated with new settings), shared pool entries\n * — held alive by other projects — would start failing with \"resource not found in\n * the tree\" even though the underlying blob was still valid backend-side.\n *\n * BlobResourceRef captures the snapshot at parse time, where the tree is guaranteed\n * to resolve. The pools then key by rid (`resourceInfo.id`) and call into the\n * download driver with the snapshot directly — independent of any specific tree.\n */\nexport class BlobResourceRef {\n constructor(\n public readonly resourceInfo: { readonly id: SignedResourceId; readonly type: ResourceType },\n /** Present only for on-demand (remote) blobs; needed for size and signed handle. */\n public readonly onDemandSnapshot: OnDemandBlobResourceSnapshot | undefined,\n ) {}\n\n toJSON(): string {\n return resourceIdToString(this.resourceInfo.id);\n }\n}\n\nexport function makeLocalBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(accessor.resourceInfo, undefined);\n}\n\nfunction makeRemoteBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(\n accessor.resourceInfo,\n makeResourceSnapshot(accessor, OnDemandBlobResourceSnapshot),\n );\n}\n\nexport const PColumnDataJsonPartitioned = resourceType(\"PColumnData/JsonPartitioned\", \"1\");\nexport const PColumnDataJsonSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/JsonPartitioned\",\n \"1\",\n);\nexport const PColumnDataBinaryPartitioned = resourceType(\"PColumnData/BinaryPartitioned\", \"1\");\nexport const PColumnDataBinarySuperPartitioned = resourceType(\n \"PColumnData/Partitioned/BinaryPartitioned\",\n \"1\",\n);\nexport const PColumnDataParquetPartitioned = resourceType(\"PColumnData/ParquetPartitioned\", \"1\");\nexport const PColumnDataParquetSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/ParquetPartitioned\",\n \"1\",\n);\nexport const PColumnDataJson = resourceType(\"PColumnData/Json\", \"1\");\n\nexport const ParquetChunkResourceType = resourceType(\"ParquetChunk\", \"1\");\n\nexport type PColumnDataJsonResourceValue = {\n keyLength: number;\n data: Record<string, PColumnValue>;\n};\n\nexport type PColumnDataPartitionedResourceValue = {\n partitionKeyLength: number;\n};\n\nexport type PColumnDataSuperPartitionedResourceValue = {\n superPartitionKeyLength: number;\n partitionKeyLength: number;\n};\n\nconst BinaryPartitionedIndexFieldSuffix = \".index\";\nconst BinaryPartitionedValuesFieldSuffix = \".values\";\n\nexport function parseDataInfoResource(\n data: PlTreeNodeAccessor,\n): PFrameInternal.DataInfo<BlobResourceRef> {\n if (!data.getIsReadyOrError()) throw new PFrameDriverError(\"Data not ready.\");\n\n const resourceData = data.getDataAsJson();\n if (resourceData === undefined)\n throw new PFrameDriverError(\"unexpected data info structure, no resource data\");\n\n if (resourceTypesEqual(data.resourceType, PColumnDataJson)) {\n const dataContent = resourceData as PColumnDataJsonResourceValue;\n\n return {\n type: \"Json\",\n keyLength: dataContent.keyLength,\n data: dataContent.data,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts = Object.fromEntries(\n data\n .listInputFields()\n .map((field) => [\n field,\n makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true })),\n ]),\n );\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, BlobResourceRef> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = makeLocalBlobRef(\n superPart.traverse({ field: key, errorIfFieldNotSet: true }),\n );\n }\n }\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinaryPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n\n // parsing the structure\n for (const field of data.listInputFields()) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.index = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.values = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n\n // structure validation\n for (const [key, part] of Object.entries(parts)) {\n if (part.index === undefined) throw new PFrameDriverError(`no index for part ${key}`);\n if (part.values === undefined) throw new PFrameDriverError(`no values for part ${key}`);\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinarySuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n for (const superKey of data.listInputFields()) {\n const superData = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superData.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const field of keys) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].index = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].values = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const key of data.listInputFields()) {\n const resource = data.traverse({\n field: key,\n assertFieldType: \"Input\",\n errorIfFieldNotSet: true,\n });\n\n parts[key] = traverseParquetChunkResource(resource);\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const resource = data.traverse({ field: key, errorIfFieldNotSet: true });\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = traverseParquetChunkResource(resource);\n }\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n }\n\n throw new PFrameDriverError(\n `unsupported resource type: ${resourceTypeToString(data.resourceType)}`,\n );\n}\n\nexport function traverseParquetChunkResource(\n resource: PlTreeNodeAccessor,\n): ParquetChunk<BlobResourceRef> {\n if (!resourceTypesEqual(resource.resourceType, ParquetChunkResourceType)) {\n throw new PFrameDriverError(\n `unknown resource type: ${resourceTypeToString(resource.resourceType)}, ` +\n `expected: ${resourceTypeToString(ParquetChunkResourceType)}`,\n );\n }\n\n const blob = makeRemoteBlobRef(\n resource.traverse({ field: \"blob\", assertFieldType: \"Service\", errorIfFieldNotSet: true }),\n );\n const partInfo = resource.getDataAsJson() as ParquetChunkMetadata;\n const mapping = resource\n .traverse({ field: \"mapping\", assertFieldType: \"Service\", errorIfFieldNotSet: true })\n .getDataAsJson() as ParquetChunkMapping;\n\n return {\n data: blob,\n ...partInfo,\n ...mapping,\n };\n}\n\nexport function deriveLegacyPObjectId(spec: PObjectSpec, data: PlTreeNodeAccessor): PObjectId {\n const hash = createHash(\"sha256\");\n hash.update(canonicalize(spec)!);\n const rid = !isNullSignedResourceId(data.originalId) ? data.originalId : data.id;\n hash.update(String(anyResourceIdToBigint(rid)));\n return hash.digest().toString(\"hex\") as PObjectId;\n}\n\nexport function deriveGlobalPObjectId(blockId: string, exportName: string): PObjectId {\n return canonicalize({ __isRef: true, blockId, name: exportName } satisfies PlRef)! as PObjectId;\n}\n\nexport function deriveLocalPObjectId(resolvePath: string[], outputName: string): PObjectId {\n return canonicalize({ resolvePath, name: outputName })! as PObjectId;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { PObjectId } from "@platforma-sdk/model";
2
2
  import { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
3
- import { ResourceId, ResourceType } from "@milaboratories/pl-client";
3
+ import { ResourceType, SignedResourceId } from "@milaboratories/pl-client";
4
4
  import { PlTreeNodeAccessor } from "@milaboratories/pl-tree";
5
5
  import { OnDemandBlobResourceSnapshot } from "@milaboratories/pl-drivers";
6
6
 
@@ -1 +1 @@
1
- {"version":3,"file":"data.d.ts","names":[],"sources":["../../src/pool/data.ts"],"mappings":";;;;;;;iBA0UgB,qBAAA,CAAsB,OAAA,UAAiB,UAAA,WAAqB,SAAA;AAAA,iBAI5D,oBAAA,CAAqB,WAAA,YAAuB,UAAA,WAAqB,SAAA"}
1
+ {"version":3,"file":"data.d.ts","names":[],"sources":["../../src/pool/data.ts"],"mappings":";;;;;;;iBA4UgB,qBAAA,CAAsB,OAAA,UAAiB,UAAA,WAAqB,SAAA;AAAA,iBAI5D,oBAAA,CAAqB,WAAA,YAAuB,UAAA,WAAqB,SAAA"}
package/dist/pool/data.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PFrameDriverError } from "@platforma-sdk/model";
2
- import { isNullResourceId, resourceIdToString, resourceType, resourceTypeToString, resourceTypesEqual } from "@milaboratories/pl-client";
2
+ import { anyResourceIdToBigint, isNullSignedResourceId, resourceIdToString, resourceType, resourceTypeToString, resourceTypesEqual } from "@milaboratories/pl-client";
3
3
  import { makeResourceSnapshot } from "@milaboratories/pl-tree";
4
4
  import { createHash } from "node:crypto";
5
5
  import canonicalize from "canonicalize";
@@ -225,7 +225,8 @@ function traverseParquetChunkResource(resource) {
225
225
  function deriveLegacyPObjectId(spec, data) {
226
226
  const hash = createHash("sha256");
227
227
  hash.update(canonicalize(spec));
228
- hash.update(String(!isNullResourceId(data.originalId) ? data.originalId : data.id));
228
+ const rid = !isNullSignedResourceId(data.originalId) ? data.originalId : data.id;
229
+ hash.update(String(anyResourceIdToBigint(rid)));
229
230
  return hash.digest().toString("hex");
230
231
  }
231
232
  function deriveGlobalPObjectId(blockId, exportName) {
@@ -1 +1 @@
1
- {"version":3,"file":"data.js","names":[],"sources":["../../src/pool/data.ts"],"sourcesContent":["import {\n PFrameDriverError,\n type BinaryChunk,\n type ParquetChunk,\n type ParquetChunkMapping,\n type ParquetChunkMetadata,\n type PColumnValue,\n type PlRef,\n type PObjectId,\n type PObjectSpec,\n} from \"@platforma-sdk/model\";\nimport { makeResourceSnapshot, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport canonicalize from \"canonicalize\";\nimport {\n isNullResourceId,\n resourceIdToString,\n resourceType,\n resourceTypeToString,\n resourceTypesEqual,\n type ResourceId,\n type ResourceType,\n} from \"@milaboratories/pl-client\";\nimport type { Writable } from \"utility-types\";\nimport { createHash } from \"node:crypto\";\nimport type { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { OnDemandBlobResourceSnapshot } from \"@milaboratories/pl-drivers\";\n\n/**\n * Tree-independent reference to a blob resource used by the PFrame data flow.\n *\n * The earlier design carried a {@link PlTreeEntry} all the way into the blob pools;\n * resolution then went back through the originating tree, so when that tree dropped\n * the resource (e.g., a project recalculated with new settings), shared pool entries\n * — held alive by other projects — would start failing with \"resource not found in\n * the tree\" even though the underlying blob was still valid backend-side.\n *\n * BlobResourceRef captures the snapshot at parse time, where the tree is guaranteed\n * to resolve. The pools then key by rid (`resourceInfo.id`) and call into the\n * download driver with the snapshot directly — independent of any specific tree.\n */\nexport class BlobResourceRef {\n constructor(\n public readonly resourceInfo: { readonly id: ResourceId; readonly type: ResourceType },\n /** Present only for on-demand (remote) blobs; needed for size and signed handle. */\n public readonly onDemandSnapshot: OnDemandBlobResourceSnapshot | undefined,\n ) {}\n\n toJSON(): string {\n return resourceIdToString(this.resourceInfo.id);\n }\n}\n\nexport function makeLocalBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(accessor.resourceInfo, undefined);\n}\n\nfunction makeRemoteBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(\n accessor.resourceInfo,\n makeResourceSnapshot(accessor, OnDemandBlobResourceSnapshot),\n );\n}\n\nexport const PColumnDataJsonPartitioned = resourceType(\"PColumnData/JsonPartitioned\", \"1\");\nexport const PColumnDataJsonSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/JsonPartitioned\",\n \"1\",\n);\nexport const PColumnDataBinaryPartitioned = resourceType(\"PColumnData/BinaryPartitioned\", \"1\");\nexport const PColumnDataBinarySuperPartitioned = resourceType(\n \"PColumnData/Partitioned/BinaryPartitioned\",\n \"1\",\n);\nexport const PColumnDataParquetPartitioned = resourceType(\"PColumnData/ParquetPartitioned\", \"1\");\nexport const PColumnDataParquetSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/ParquetPartitioned\",\n \"1\",\n);\nexport const PColumnDataJson = resourceType(\"PColumnData/Json\", \"1\");\n\nexport const ParquetChunkResourceType = resourceType(\"ParquetChunk\", \"1\");\n\nexport type PColumnDataJsonResourceValue = {\n keyLength: number;\n data: Record<string, PColumnValue>;\n};\n\nexport type PColumnDataPartitionedResourceValue = {\n partitionKeyLength: number;\n};\n\nexport type PColumnDataSuperPartitionedResourceValue = {\n superPartitionKeyLength: number;\n partitionKeyLength: number;\n};\n\nconst BinaryPartitionedIndexFieldSuffix = \".index\";\nconst BinaryPartitionedValuesFieldSuffix = \".values\";\n\nexport function parseDataInfoResource(\n data: PlTreeNodeAccessor,\n): PFrameInternal.DataInfo<BlobResourceRef> {\n if (!data.getIsReadyOrError()) throw new PFrameDriverError(\"Data not ready.\");\n\n const resourceData = data.getDataAsJson();\n if (resourceData === undefined)\n throw new PFrameDriverError(\"unexpected data info structure, no resource data\");\n\n if (resourceTypesEqual(data.resourceType, PColumnDataJson)) {\n const dataContent = resourceData as PColumnDataJsonResourceValue;\n\n return {\n type: \"Json\",\n keyLength: dataContent.keyLength,\n data: dataContent.data,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts = Object.fromEntries(\n data\n .listInputFields()\n .map((field) => [\n field,\n makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true })),\n ]),\n );\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, BlobResourceRef> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = makeLocalBlobRef(\n superPart.traverse({ field: key, errorIfFieldNotSet: true }),\n );\n }\n }\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinaryPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n\n // parsing the structure\n for (const field of data.listInputFields()) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.index = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.values = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n\n // structure validation\n for (const [key, part] of Object.entries(parts)) {\n if (part.index === undefined) throw new PFrameDriverError(`no index for part ${key}`);\n if (part.values === undefined) throw new PFrameDriverError(`no values for part ${key}`);\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinarySuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n for (const superKey of data.listInputFields()) {\n const superData = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superData.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const field of keys) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].index = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].values = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const key of data.listInputFields()) {\n const resource = data.traverse({\n field: key,\n assertFieldType: \"Input\",\n errorIfFieldNotSet: true,\n });\n\n parts[key] = traverseParquetChunkResource(resource);\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const resource = data.traverse({ field: key, errorIfFieldNotSet: true });\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = traverseParquetChunkResource(resource);\n }\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n }\n\n throw new PFrameDriverError(\n `unsupported resource type: ${resourceTypeToString(data.resourceType)}`,\n );\n}\n\nexport function traverseParquetChunkResource(\n resource: PlTreeNodeAccessor,\n): ParquetChunk<BlobResourceRef> {\n if (!resourceTypesEqual(resource.resourceType, ParquetChunkResourceType)) {\n throw new PFrameDriverError(\n `unknown resource type: ${resourceTypeToString(resource.resourceType)}, ` +\n `expected: ${resourceTypeToString(ParquetChunkResourceType)}`,\n );\n }\n\n const blob = makeRemoteBlobRef(\n resource.traverse({ field: \"blob\", assertFieldType: \"Service\", errorIfFieldNotSet: true }),\n );\n const partInfo = resource.getDataAsJson() as ParquetChunkMetadata;\n const mapping = resource\n .traverse({ field: \"mapping\", assertFieldType: \"Service\", errorIfFieldNotSet: true })\n .getDataAsJson() as ParquetChunkMapping;\n\n return {\n data: blob,\n ...partInfo,\n ...mapping,\n };\n}\n\nexport function deriveLegacyPObjectId(spec: PObjectSpec, data: PlTreeNodeAccessor): PObjectId {\n const hash = createHash(\"sha256\");\n hash.update(canonicalize(spec)!);\n hash.update(String(!isNullResourceId(data.originalId) ? data.originalId : data.id));\n return hash.digest().toString(\"hex\") as PObjectId;\n}\n\nexport function deriveGlobalPObjectId(blockId: string, exportName: string): PObjectId {\n return canonicalize({ __isRef: true, blockId, name: exportName } satisfies PlRef)! as PObjectId;\n}\n\nexport function deriveLocalPObjectId(resolvePath: string[], outputName: string): PObjectId {\n return canonicalize({ resolvePath, name: outputName })! as PObjectId;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"data.js","names":[],"sources":["../../src/pool/data.ts"],"sourcesContent":["import {\n PFrameDriverError,\n type BinaryChunk,\n type ParquetChunk,\n type ParquetChunkMapping,\n type ParquetChunkMetadata,\n type PColumnValue,\n type PlRef,\n type PObjectId,\n type PObjectSpec,\n} from \"@platforma-sdk/model\";\nimport { makeResourceSnapshot, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport canonicalize from \"canonicalize\";\nimport {\n anyResourceIdToBigint,\n isNullSignedResourceId,\n resourceIdToString,\n resourceType,\n resourceTypeToString,\n resourceTypesEqual,\n type SignedResourceId,\n type ResourceType,\n} from \"@milaboratories/pl-client\";\nimport type { Writable } from \"utility-types\";\nimport { createHash } from \"node:crypto\";\nimport type { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { OnDemandBlobResourceSnapshot } from \"@milaboratories/pl-drivers\";\n\n/**\n * Tree-independent reference to a blob resource used by the PFrame data flow.\n *\n * The earlier design carried a {@link PlTreeEntry} all the way into the blob pools;\n * resolution then went back through the originating tree, so when that tree dropped\n * the resource (e.g., a project recalculated with new settings), shared pool entries\n * — held alive by other projects — would start failing with \"resource not found in\n * the tree\" even though the underlying blob was still valid backend-side.\n *\n * BlobResourceRef captures the snapshot at parse time, where the tree is guaranteed\n * to resolve. The pools then key by rid (`resourceInfo.id`) and call into the\n * download driver with the snapshot directly — independent of any specific tree.\n */\nexport class BlobResourceRef {\n constructor(\n public readonly resourceInfo: { readonly id: SignedResourceId; readonly type: ResourceType },\n /** Present only for on-demand (remote) blobs; needed for size and signed handle. */\n public readonly onDemandSnapshot: OnDemandBlobResourceSnapshot | undefined,\n ) {}\n\n toJSON(): string {\n return resourceIdToString(this.resourceInfo.id);\n }\n}\n\nexport function makeLocalBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(accessor.resourceInfo, undefined);\n}\n\nfunction makeRemoteBlobRef(accessor: PlTreeNodeAccessor): BlobResourceRef {\n return new BlobResourceRef(\n accessor.resourceInfo,\n makeResourceSnapshot(accessor, OnDemandBlobResourceSnapshot),\n );\n}\n\nexport const PColumnDataJsonPartitioned = resourceType(\"PColumnData/JsonPartitioned\", \"1\");\nexport const PColumnDataJsonSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/JsonPartitioned\",\n \"1\",\n);\nexport const PColumnDataBinaryPartitioned = resourceType(\"PColumnData/BinaryPartitioned\", \"1\");\nexport const PColumnDataBinarySuperPartitioned = resourceType(\n \"PColumnData/Partitioned/BinaryPartitioned\",\n \"1\",\n);\nexport const PColumnDataParquetPartitioned = resourceType(\"PColumnData/ParquetPartitioned\", \"1\");\nexport const PColumnDataParquetSuperPartitioned = resourceType(\n \"PColumnData/Partitioned/ParquetPartitioned\",\n \"1\",\n);\nexport const PColumnDataJson = resourceType(\"PColumnData/Json\", \"1\");\n\nexport const ParquetChunkResourceType = resourceType(\"ParquetChunk\", \"1\");\n\nexport type PColumnDataJsonResourceValue = {\n keyLength: number;\n data: Record<string, PColumnValue>;\n};\n\nexport type PColumnDataPartitionedResourceValue = {\n partitionKeyLength: number;\n};\n\nexport type PColumnDataSuperPartitionedResourceValue = {\n superPartitionKeyLength: number;\n partitionKeyLength: number;\n};\n\nconst BinaryPartitionedIndexFieldSuffix = \".index\";\nconst BinaryPartitionedValuesFieldSuffix = \".values\";\n\nexport function parseDataInfoResource(\n data: PlTreeNodeAccessor,\n): PFrameInternal.DataInfo<BlobResourceRef> {\n if (!data.getIsReadyOrError()) throw new PFrameDriverError(\"Data not ready.\");\n\n const resourceData = data.getDataAsJson();\n if (resourceData === undefined)\n throw new PFrameDriverError(\"unexpected data info structure, no resource data\");\n\n if (resourceTypesEqual(data.resourceType, PColumnDataJson)) {\n const dataContent = resourceData as PColumnDataJsonResourceValue;\n\n return {\n type: \"Json\",\n keyLength: dataContent.keyLength,\n data: dataContent.data,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts = Object.fromEntries(\n data\n .listInputFields()\n .map((field) => [\n field,\n makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true })),\n ]),\n );\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataJsonSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, BlobResourceRef> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = makeLocalBlobRef(\n superPart.traverse({ field: key, errorIfFieldNotSet: true }),\n );\n }\n }\n\n return {\n type: \"JsonPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinaryPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n\n // parsing the structure\n for (const field of data.listInputFields()) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.index = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const partKey = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n part.values = makeLocalBlobRef(data.traverse({ field, errorIfFieldNotSet: true }));\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n\n // structure validation\n for (const [key, part] of Object.entries(parts)) {\n if (part.index === undefined) throw new PFrameDriverError(`no index for part ${key}`);\n if (part.values === undefined) throw new PFrameDriverError(`no values for part ${key}`);\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataBinarySuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, Partial<Writable<BinaryChunk<BlobResourceRef>>>> = {};\n for (const superKey of data.listInputFields()) {\n const superData = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superData.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const field of keys) {\n if (field.endsWith(BinaryPartitionedIndexFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedIndexFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].index = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else if (field.endsWith(BinaryPartitionedValuesFieldSuffix)) {\n const key = field.slice(0, -BinaryPartitionedValuesFieldSuffix.length);\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n let part = parts[partKey];\n if (part === undefined) {\n part = {};\n parts[partKey] = part;\n }\n parts[partKey].values = makeLocalBlobRef(\n superData.traverse({ field, errorIfFieldNotSet: true }),\n );\n } else throw new PFrameDriverError(`unrecognized part field name: ${field}`);\n }\n }\n\n return {\n type: \"BinaryPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts: parts as Record<string, BinaryChunk<BlobResourceRef>>,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetPartitioned)) {\n const meta = resourceData as PColumnDataPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const key of data.listInputFields()) {\n const resource = data.traverse({\n field: key,\n assertFieldType: \"Input\",\n errorIfFieldNotSet: true,\n });\n\n parts[key] = traverseParquetChunkResource(resource);\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.partitionKeyLength,\n parts,\n };\n } else if (resourceTypesEqual(data.resourceType, PColumnDataParquetSuperPartitioned)) {\n const meta = resourceData as PColumnDataSuperPartitionedResourceValue;\n\n const parts: Record<string, ParquetChunk<BlobResourceRef>> = {};\n for (const superKey of data.listInputFields()) {\n const superPart = data.traverse({ field: superKey, errorIfFieldNotSet: true });\n const keys = superPart.listInputFields();\n if (keys === undefined)\n throw new PFrameDriverError(`no partition keys for super key ${superKey}`);\n\n for (const key of keys) {\n const resource = data.traverse({ field: key, errorIfFieldNotSet: true });\n\n const partKey = JSON.stringify([\n ...(JSON.parse(superKey) as PColumnValue[]),\n ...(JSON.parse(key) as PColumnValue[]),\n ]);\n parts[partKey] = traverseParquetChunkResource(resource);\n }\n }\n\n return {\n type: \"ParquetPartitioned\",\n partitionKeyLength: meta.superPartitionKeyLength + meta.partitionKeyLength,\n parts,\n };\n }\n\n throw new PFrameDriverError(\n `unsupported resource type: ${resourceTypeToString(data.resourceType)}`,\n );\n}\n\nexport function traverseParquetChunkResource(\n resource: PlTreeNodeAccessor,\n): ParquetChunk<BlobResourceRef> {\n if (!resourceTypesEqual(resource.resourceType, ParquetChunkResourceType)) {\n throw new PFrameDriverError(\n `unknown resource type: ${resourceTypeToString(resource.resourceType)}, ` +\n `expected: ${resourceTypeToString(ParquetChunkResourceType)}`,\n );\n }\n\n const blob = makeRemoteBlobRef(\n resource.traverse({ field: \"blob\", assertFieldType: \"Service\", errorIfFieldNotSet: true }),\n );\n const partInfo = resource.getDataAsJson() as ParquetChunkMetadata;\n const mapping = resource\n .traverse({ field: \"mapping\", assertFieldType: \"Service\", errorIfFieldNotSet: true })\n .getDataAsJson() as ParquetChunkMapping;\n\n return {\n data: blob,\n ...partInfo,\n ...mapping,\n };\n}\n\nexport function deriveLegacyPObjectId(spec: PObjectSpec, data: PlTreeNodeAccessor): PObjectId {\n const hash = createHash(\"sha256\");\n hash.update(canonicalize(spec)!);\n const rid = !isNullSignedResourceId(data.originalId) ? data.originalId : data.id;\n hash.update(String(anyResourceIdToBigint(rid)));\n return hash.digest().toString(\"hex\") as PObjectId;\n}\n\nexport function deriveGlobalPObjectId(blockId: string, exportName: string): PObjectId {\n return canonicalize({ __isRef: true, blockId, name: exportName } satisfies PlRef)! as PObjectId;\n}\n\nexport function deriveLocalPObjectId(resolvePath: string[], outputName: string): PObjectId {\n return canonicalize({ resolvePath, name: outputName })! as PObjectId;\n}\n"],"mappings":"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"}
@@ -5,6 +5,7 @@ let _milaboratories_pl_model_middle_layer = require("@milaboratories/pl-model-mi
5
5
  let node_path = require("node:path");
6
6
  node_path = require_runtime.__toESM(node_path);
7
7
  let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
8
+ let _milaboratories_pl_client = require("@milaboratories/pl-client");
8
9
  let _milaboratories_pl_tree = require("@milaboratories/pl-tree");
9
10
  let _milaboratories_pl_drivers = require("@milaboratories/pl-drivers");
10
11
  let _milaboratories_helpers = require("@milaboratories/helpers");
@@ -13,7 +14,8 @@ let _milaboratories_pframes_rs_node = require("@milaboratories/pframes-rs-node")
13
14
  let node_stream = require("node:stream");
14
15
  //#region src/pool/driver.ts
15
16
  function makeBlobId(res) {
16
- return String(res.resourceInfo.id);
17
+ const { globalId } = (0, _milaboratories_pl_client.parseSignedResourceId)(res.resourceInfo.id);
18
+ return String(globalId);
17
19
  }
18
20
  var LocalBlobProviderImpl = class extends _milaboratories_helpers.RefCountPoolBase {
19
21
  constructor(blobDriver, logger) {