@milaboratories/pl-client 3.17.6 → 3.17.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/final.cjs +4 -2
- package/dist/core/final.cjs.map +1 -1
- package/dist/core/final.js +5 -3
- package/dist/core/final.js.map +1 -1
- package/package.json +2 -2
- package/src/core/final.test.ts +73 -0
- package/src/core/final.ts +13 -3
package/dist/core/final.cjs
CHANGED
|
@@ -19,8 +19,10 @@ const DefaultFinalResourceDataPredicate = (r) => {
|
|
|
19
19
|
if (!readyOrDuplicateOrError(r)) return false;
|
|
20
20
|
if (r.fields === void 0) return true;
|
|
21
21
|
if (require_types.isNotNullSignedResourceId(r.error)) return true;
|
|
22
|
-
const downloadable =
|
|
23
|
-
|
|
22
|
+
const downloadable = r.fields.find((f) => f.name === "downloadable");
|
|
23
|
+
const stream = r.fields.find((f) => f.name === "stream");
|
|
24
|
+
if (downloadable === void 0 || stream === void 0 || require_types.isNullSignedResourceId(stream.value)) return false;
|
|
25
|
+
return stream.value === downloadable.value;
|
|
24
26
|
}
|
|
25
27
|
case _milaboratories_pl_model_common.ResourceTypeName.StdMap:
|
|
26
28
|
case _milaboratories_pl_model_common.ResourceTypeName.StdMapSlash:
|
package/dist/core/final.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"final.cjs","names":["isNotNullSignedResourceId","isNullSignedResourceId","ResourceTypeName","
|
|
1
|
+
{"version":3,"file":"final.cjs","names":["isNotNullSignedResourceId","isNullSignedResourceId","ResourceTypeName","ResourceTypePrefix"],"sources":["../../src/core/final.ts"],"sourcesContent":["import type { Optional } from \"utility-types\";\nimport type { BasicResourceData, ResourceData } from \"./types\";\nimport { isNotNullSignedResourceId, isNullSignedResourceId } from \"./types\";\nimport { ResourceTypeName, ResourceTypePrefix } from \"@milaboratories/pl-model-common\";\nexport { ResourceTypeName, ResourceTypePrefix };\n\n/**\n * Function is used to guide multiple layers of caching in pl-client and derived pl-tree.\n *\n * This function defines expected resource-specific state mutation behaviour,\n * if it returns true, system will expect that this data will never change as long as resource exist.\n *\n * If resource data contain information about fields, if should be taken into account, fields are undefined,\n * \"final\" state should be calculated for \"basic\" part of resource data only.\n */\nexport type FinalResourceDataPredicate = (\n resourceData: Optional<ResourceData, \"fields\">,\n) => boolean;\n\nexport function readyOrDuplicateOrError(r: ResourceData | BasicResourceData): boolean {\n return (\n r.resourceReady ||\n isNotNullSignedResourceId(r.originalResourceId) ||\n isNotNullSignedResourceId(r.error)\n );\n}\n\nfunction readyAndHasAllOutputsFilled(r: Optional<ResourceData, \"fields\">): boolean {\n if (!readyOrDuplicateOrError(r)) return false;\n if (!r.outputsLocked) return false;\n if (r.fields === undefined) return true; // if fields are not provided basic resource state is not expected to change in the future\n for (const f of r.fields)\n if (isNullSignedResourceId(f.error) && (isNullSignedResourceId(f.value) || !f.valueIsFinal))\n return false;\n return true;\n}\n\n// solely for logging\nconst unknownResourceTypeNames = new Set<string>();\n\n/** Default implementation, defining behaviour for built-in resource types. */\nexport const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r): boolean => {\n switch (r.type.name) {\n case ResourceTypeName.StreamManager: {\n if (!readyOrDuplicateOrError(r)) return false;\n if (r.fields === undefined) return true; // if fields are not provided basic resource state is not expected to change in the future\n if (isNotNullSignedResourceId(r.error)) return true;\n // Fields can be pruned away by the reader: without them nothing proves the switch, so\n // the resource is not final. The predicate must not throw, since a throw inside the\n // tree's update invalidates and rebuilds the whole tree.\n const downloadable = r.fields.find((f) => f.name === \"downloadable\");\n const stream = r.fields.find((f) => f.name === \"stream\");\n // Both still empty is not a switch either.\n if (\n downloadable === undefined ||\n stream === undefined ||\n isNullSignedResourceId(stream.value)\n )\n return false;\n return stream.value === downloadable.value; // it's equal to the resource is marked as final on backend side\n }\n case ResourceTypeName.StdMap:\n case ResourceTypeName.StdMapSlash:\n case ResourceTypeName.EphStdMap:\n case ResourceTypeName.PFrame:\n case ResourceTypeName.ParquetChunk:\n case ResourceTypeName.BContext:\n case ResourceTypeName.BlockPackCustom:\n case ResourceTypeName.BinaryMap:\n case ResourceTypeName.BinaryValue:\n case ResourceTypeName.BlobMap:\n case ResourceTypeName.BResolveSingle:\n case ResourceTypeName.BResolveSingleNoResult:\n case ResourceTypeName.BQueryResult:\n case ResourceTypeName.TengoTemplate:\n case ResourceTypeName.TengoLib:\n case ResourceTypeName.SoftwareInfo:\n case ResourceTypeName.Dummy:\n return readyOrDuplicateOrError(r);\n case ResourceTypeName.JsonResourceError:\n return r.type.version === \"1\";\n case ResourceTypeName.JsonObject:\n case ResourceTypeName.JsonGzObject:\n case ResourceTypeName.JsonString:\n case ResourceTypeName.JsonArray:\n case ResourceTypeName.JsonNumber:\n case ResourceTypeName.BContextEnd:\n case ResourceTypeName.FrontendFromUrl:\n case ResourceTypeName.FrontendFromFolder:\n case ResourceTypeName.BObjectSpec:\n case ResourceTypeName.Blob:\n case ResourceTypeName.Null:\n case ResourceTypeName.Binary:\n case ResourceTypeName.LSProvider:\n case ResourceTypeName.WorkingDirectory:\n return true;\n case ResourceTypeName.UserProject:\n case ResourceTypeName.Projects:\n case ResourceTypeName.ClientRoot:\n // Never final — these sharing resources gain and lose dynamic child fields over their lifetime.\n case ResourceTypeName.SharingOutbox:\n case ResourceTypeName.SharingState:\n case ResourceTypeName.SharedEnvelope:\n return false;\n default:\n if (\n r.type.name.startsWith(ResourceTypePrefix.Blob) ||\n r.type.name.startsWith(ResourceTypePrefix.LS) ||\n r.type.name.startsWith(ResourceTypePrefix.WorkingDirectory) ||\n r.type.name.startsWith(ResourceTypePrefix.StorageSpaceAllocation)\n ) {\n return true;\n } else if (\n r.type.name.startsWith(ResourceTypePrefix.BlobUpload) ||\n r.type.name.startsWith(ResourceTypePrefix.BlobIndex)\n ) {\n return readyAndHasAllOutputsFilled(r);\n } else if (r.type.name.startsWith(ResourceTypePrefix.PColumnData)) {\n return readyOrDuplicateOrError(r);\n } else if (r.type.name.startsWith(ResourceTypePrefix.StreamWorkdir)) {\n return readyOrDuplicateOrError(r);\n } else {\n // Unknown resource type detected\n // Set used to log this message only once\n if (!unknownResourceTypeNames.has(r.type.name)) {\n console.log(\"UNKNOWN RESOURCE TYPE: \" + r.type.name);\n unknownResourceTypeNames.add(r.type.name);\n }\n }\n }\n return false;\n};\n"],"mappings":"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"}
|
package/dist/core/final.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isNotNullSignedResourceId, isNullSignedResourceId } from "./types.js";
|
|
2
2
|
import { ResourceTypeName, ResourceTypePrefix } from "@milaboratories/pl-model-common";
|
|
3
3
|
//#region src/core/final.ts
|
|
4
4
|
function readyOrDuplicateOrError(r) {
|
|
@@ -19,8 +19,10 @@ const DefaultFinalResourceDataPredicate = (r) => {
|
|
|
19
19
|
if (!readyOrDuplicateOrError(r)) return false;
|
|
20
20
|
if (r.fields === void 0) return true;
|
|
21
21
|
if (isNotNullSignedResourceId(r.error)) return true;
|
|
22
|
-
const downloadable =
|
|
23
|
-
|
|
22
|
+
const downloadable = r.fields.find((f) => f.name === "downloadable");
|
|
23
|
+
const stream = r.fields.find((f) => f.name === "stream");
|
|
24
|
+
if (downloadable === void 0 || stream === void 0 || isNullSignedResourceId(stream.value)) return false;
|
|
25
|
+
return stream.value === downloadable.value;
|
|
24
26
|
}
|
|
25
27
|
case ResourceTypeName.StdMap:
|
|
26
28
|
case ResourceTypeName.StdMapSlash:
|
package/dist/core/final.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"final.js","names":[],"sources":["../../src/core/final.ts"],"sourcesContent":["import type { Optional } from \"utility-types\";\nimport type { BasicResourceData, ResourceData } from \"./types\";\nimport {
|
|
1
|
+
{"version":3,"file":"final.js","names":[],"sources":["../../src/core/final.ts"],"sourcesContent":["import type { Optional } from \"utility-types\";\nimport type { BasicResourceData, ResourceData } from \"./types\";\nimport { isNotNullSignedResourceId, isNullSignedResourceId } from \"./types\";\nimport { ResourceTypeName, ResourceTypePrefix } from \"@milaboratories/pl-model-common\";\nexport { ResourceTypeName, ResourceTypePrefix };\n\n/**\n * Function is used to guide multiple layers of caching in pl-client and derived pl-tree.\n *\n * This function defines expected resource-specific state mutation behaviour,\n * if it returns true, system will expect that this data will never change as long as resource exist.\n *\n * If resource data contain information about fields, if should be taken into account, fields are undefined,\n * \"final\" state should be calculated for \"basic\" part of resource data only.\n */\nexport type FinalResourceDataPredicate = (\n resourceData: Optional<ResourceData, \"fields\">,\n) => boolean;\n\nexport function readyOrDuplicateOrError(r: ResourceData | BasicResourceData): boolean {\n return (\n r.resourceReady ||\n isNotNullSignedResourceId(r.originalResourceId) ||\n isNotNullSignedResourceId(r.error)\n );\n}\n\nfunction readyAndHasAllOutputsFilled(r: Optional<ResourceData, \"fields\">): boolean {\n if (!readyOrDuplicateOrError(r)) return false;\n if (!r.outputsLocked) return false;\n if (r.fields === undefined) return true; // if fields are not provided basic resource state is not expected to change in the future\n for (const f of r.fields)\n if (isNullSignedResourceId(f.error) && (isNullSignedResourceId(f.value) || !f.valueIsFinal))\n return false;\n return true;\n}\n\n// solely for logging\nconst unknownResourceTypeNames = new Set<string>();\n\n/** Default implementation, defining behaviour for built-in resource types. */\nexport const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r): boolean => {\n switch (r.type.name) {\n case ResourceTypeName.StreamManager: {\n if (!readyOrDuplicateOrError(r)) return false;\n if (r.fields === undefined) return true; // if fields are not provided basic resource state is not expected to change in the future\n if (isNotNullSignedResourceId(r.error)) return true;\n // Fields can be pruned away by the reader: without them nothing proves the switch, so\n // the resource is not final. The predicate must not throw, since a throw inside the\n // tree's update invalidates and rebuilds the whole tree.\n const downloadable = r.fields.find((f) => f.name === \"downloadable\");\n const stream = r.fields.find((f) => f.name === \"stream\");\n // Both still empty is not a switch either.\n if (\n downloadable === undefined ||\n stream === undefined ||\n isNullSignedResourceId(stream.value)\n )\n return false;\n return stream.value === downloadable.value; // it's equal to the resource is marked as final on backend side\n }\n case ResourceTypeName.StdMap:\n case ResourceTypeName.StdMapSlash:\n case ResourceTypeName.EphStdMap:\n case ResourceTypeName.PFrame:\n case ResourceTypeName.ParquetChunk:\n case ResourceTypeName.BContext:\n case ResourceTypeName.BlockPackCustom:\n case ResourceTypeName.BinaryMap:\n case ResourceTypeName.BinaryValue:\n case ResourceTypeName.BlobMap:\n case ResourceTypeName.BResolveSingle:\n case ResourceTypeName.BResolveSingleNoResult:\n case ResourceTypeName.BQueryResult:\n case ResourceTypeName.TengoTemplate:\n case ResourceTypeName.TengoLib:\n case ResourceTypeName.SoftwareInfo:\n case ResourceTypeName.Dummy:\n return readyOrDuplicateOrError(r);\n case ResourceTypeName.JsonResourceError:\n return r.type.version === \"1\";\n case ResourceTypeName.JsonObject:\n case ResourceTypeName.JsonGzObject:\n case ResourceTypeName.JsonString:\n case ResourceTypeName.JsonArray:\n case ResourceTypeName.JsonNumber:\n case ResourceTypeName.BContextEnd:\n case ResourceTypeName.FrontendFromUrl:\n case ResourceTypeName.FrontendFromFolder:\n case ResourceTypeName.BObjectSpec:\n case ResourceTypeName.Blob:\n case ResourceTypeName.Null:\n case ResourceTypeName.Binary:\n case ResourceTypeName.LSProvider:\n case ResourceTypeName.WorkingDirectory:\n return true;\n case ResourceTypeName.UserProject:\n case ResourceTypeName.Projects:\n case ResourceTypeName.ClientRoot:\n // Never final — these sharing resources gain and lose dynamic child fields over their lifetime.\n case ResourceTypeName.SharingOutbox:\n case ResourceTypeName.SharingState:\n case ResourceTypeName.SharedEnvelope:\n return false;\n default:\n if (\n r.type.name.startsWith(ResourceTypePrefix.Blob) ||\n r.type.name.startsWith(ResourceTypePrefix.LS) ||\n r.type.name.startsWith(ResourceTypePrefix.WorkingDirectory) ||\n r.type.name.startsWith(ResourceTypePrefix.StorageSpaceAllocation)\n ) {\n return true;\n } else if (\n r.type.name.startsWith(ResourceTypePrefix.BlobUpload) ||\n r.type.name.startsWith(ResourceTypePrefix.BlobIndex)\n ) {\n return readyAndHasAllOutputsFilled(r);\n } else if (r.type.name.startsWith(ResourceTypePrefix.PColumnData)) {\n return readyOrDuplicateOrError(r);\n } else if (r.type.name.startsWith(ResourceTypePrefix.StreamWorkdir)) {\n return readyOrDuplicateOrError(r);\n } else {\n // Unknown resource type detected\n // Set used to log this message only once\n if (!unknownResourceTypeNames.has(r.type.name)) {\n console.log(\"UNKNOWN RESOURCE TYPE: \" + r.type.name);\n unknownResourceTypeNames.add(r.type.name);\n }\n }\n }\n return false;\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-client",
|
|
3
|
-
"version": "3.17.
|
|
3
|
+
"version": "3.17.7",
|
|
4
4
|
"description": "New TS/JS client for Platform API",
|
|
5
5
|
"files": [
|
|
6
6
|
"./dist/**/*",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"openapi-typescript": "^7.13.0",
|
|
42
42
|
"typescript": "7.0.2",
|
|
43
43
|
"vitest": "^4.1.3",
|
|
44
|
-
"@milaboratories/ts-builder": "1.7.2",
|
|
45
44
|
"@milaboratories/build-configs": "2.0.2",
|
|
45
|
+
"@milaboratories/ts-builder": "1.7.2",
|
|
46
46
|
"@milaboratories/ts-configs": "1.4.0"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { DefaultFinalResourceDataPredicate } from "./final";
|
|
3
|
+
import type { FieldData, ResourceData } from "./types";
|
|
4
|
+
import { createSignedResourceId, NullSignedResourceId } from "./types";
|
|
5
|
+
|
|
6
|
+
const A = createSignedResourceId(1n);
|
|
7
|
+
const B = createSignedResourceId(2n);
|
|
8
|
+
|
|
9
|
+
function streamManager(patch: Partial<ResourceData>): ResourceData {
|
|
10
|
+
return {
|
|
11
|
+
id: createSignedResourceId(10n),
|
|
12
|
+
originalResourceId: NullSignedResourceId,
|
|
13
|
+
kind: "Structural",
|
|
14
|
+
type: { name: "StreamManager", version: "1" },
|
|
15
|
+
data: undefined,
|
|
16
|
+
error: NullSignedResourceId,
|
|
17
|
+
inputsLocked: true,
|
|
18
|
+
outputsLocked: true,
|
|
19
|
+
resourceReady: true,
|
|
20
|
+
final: false,
|
|
21
|
+
fields: [],
|
|
22
|
+
...patch,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function field(name: string, value: typeof A): FieldData {
|
|
27
|
+
return {
|
|
28
|
+
name,
|
|
29
|
+
type: "Output",
|
|
30
|
+
status: "Resolved",
|
|
31
|
+
value,
|
|
32
|
+
error: NullSignedResourceId,
|
|
33
|
+
valueIsFinal: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test("a ready, non-errored StreamManager with its fields pruned away is not final, and does not throw", () => {
|
|
38
|
+
expect(DefaultFinalResourceDataPredicate(streamManager({ fields: [] }))).toBe(false);
|
|
39
|
+
expect(DefaultFinalResourceDataPredicate(streamManager({ fields: [field("stream", A)] }))).toBe(
|
|
40
|
+
false,
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("a ready StreamManager is final once stream and downloadable share a non-null value", () => {
|
|
45
|
+
expect(
|
|
46
|
+
DefaultFinalResourceDataPredicate(
|
|
47
|
+
streamManager({ fields: [field("stream", A), field("downloadable", B)] }),
|
|
48
|
+
),
|
|
49
|
+
).toBe(false);
|
|
50
|
+
expect(
|
|
51
|
+
DefaultFinalResourceDataPredicate(
|
|
52
|
+
streamManager({ fields: [field("stream", B), field("downloadable", B)] }),
|
|
53
|
+
),
|
|
54
|
+
).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("an errored StreamManager is final whatever its fields", () => {
|
|
58
|
+
expect(DefaultFinalResourceDataPredicate(streamManager({ error: A, fields: [] }))).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("a StreamManager with both fields still empty is not final", () => {
|
|
62
|
+
const empty = (name: string): FieldData => ({
|
|
63
|
+
...field(name, A),
|
|
64
|
+
value: NullSignedResourceId,
|
|
65
|
+
status: "Empty",
|
|
66
|
+
valueIsFinal: false,
|
|
67
|
+
});
|
|
68
|
+
expect(
|
|
69
|
+
DefaultFinalResourceDataPredicate(
|
|
70
|
+
streamManager({ fields: [empty("stream"), empty("downloadable")] }),
|
|
71
|
+
),
|
|
72
|
+
).toBe(false);
|
|
73
|
+
});
|
package/src/core/final.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Optional } from "utility-types";
|
|
2
2
|
import type { BasicResourceData, ResourceData } from "./types";
|
|
3
|
-
import {
|
|
3
|
+
import { isNotNullSignedResourceId, isNullSignedResourceId } from "./types";
|
|
4
4
|
import { ResourceTypeName, ResourceTypePrefix } from "@milaboratories/pl-model-common";
|
|
5
5
|
export { ResourceTypeName, ResourceTypePrefix };
|
|
6
6
|
|
|
@@ -45,8 +45,18 @@ export const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r)
|
|
|
45
45
|
if (!readyOrDuplicateOrError(r)) return false;
|
|
46
46
|
if (r.fields === undefined) return true; // if fields are not provided basic resource state is not expected to change in the future
|
|
47
47
|
if (isNotNullSignedResourceId(r.error)) return true;
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
// Fields can be pruned away by the reader: without them nothing proves the switch, so
|
|
49
|
+
// the resource is not final. The predicate must not throw, since a throw inside the
|
|
50
|
+
// tree's update invalidates and rebuilds the whole tree.
|
|
51
|
+
const downloadable = r.fields.find((f) => f.name === "downloadable");
|
|
52
|
+
const stream = r.fields.find((f) => f.name === "stream");
|
|
53
|
+
// Both still empty is not a switch either.
|
|
54
|
+
if (
|
|
55
|
+
downloadable === undefined ||
|
|
56
|
+
stream === undefined ||
|
|
57
|
+
isNullSignedResourceId(stream.value)
|
|
58
|
+
)
|
|
59
|
+
return false;
|
|
50
60
|
return stream.value === downloadable.value; // it's equal to the resource is marked as final on backend side
|
|
51
61
|
}
|
|
52
62
|
case ResourceTypeName.StdMap:
|