@milaboratories/pl-client 3.4.2 → 3.6.0
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.map +1 -1
- package/dist/core/final.js.map +1 -1
- package/dist/core/ll_client.cjs +7 -1
- package/dist/core/ll_client.cjs.map +1 -1
- package/dist/core/ll_client.d.ts.map +1 -1
- package/dist/core/ll_client.js +7 -1
- package/dist/core/ll_client.js.map +1 -1
- package/dist/core/ll_transaction.cjs +151 -26
- package/dist/core/ll_transaction.cjs.map +1 -1
- package/dist/core/ll_transaction.d.ts +1 -0
- package/dist/core/ll_transaction.d.ts.map +1 -1
- package/dist/core/ll_transaction.js +151 -26
- package/dist/core/ll_transaction.js.map +1 -1
- package/dist/core/transaction.cjs +89 -0
- package/dist/core/transaction.cjs.map +1 -1
- package/dist/core/transaction.d.ts +47 -1
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +90 -1
- package/dist/core/transaction.js.map +1 -1
- package/dist/core/tree_filter.cjs +106 -0
- package/dist/core/tree_filter.cjs.map +1 -0
- package/dist/core/tree_filter.d.ts +85 -0
- package/dist/core/tree_filter.d.ts.map +1 -0
- package/dist/core/tree_filter.js +106 -0
- package/dist/core/tree_filter.js.map +1 -0
- package/dist/core/type_conversion.cjs +1 -0
- package/dist/core/type_conversion.cjs.map +1 -1
- package/dist/core/type_conversion.js +1 -1
- package/dist/core/type_conversion.js.map +1 -1
- package/dist/core/types.cjs +10 -0
- package/dist/core/types.cjs.map +1 -1
- package/dist/core/types.d.ts +4 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +10 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.cjs +6 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs.map +1 -1
- package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +450 -4
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +328 -2
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +449 -5
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
- package/dist/proto-grpc/google/protobuf/timestamp.cjs.map +1 -1
- package/dist/proto-grpc/google/protobuf/timestamp.d.ts +9 -8
- package/dist/proto-grpc/google/protobuf/timestamp.d.ts.map +1 -1
- package/dist/proto-grpc/google/protobuf/timestamp.js.map +1 -1
- package/dist/proto-grpc/google/rpc/code.cjs.map +1 -1
- package/dist/proto-grpc/google/rpc/code.js.map +1 -1
- package/package.json +6 -6
- package/src/core/final.ts +1 -1
- package/src/core/ll_client.ts +11 -1
- package/src/core/ll_transaction.test.ts +13 -18
- package/src/core/ll_transaction.ts +237 -60
- package/src/core/transaction.test.ts +38 -0
- package/src/core/transaction.ts +136 -1
- package/src/core/tree_filter.test.ts +217 -0
- package/src/core/tree_filter.ts +182 -0
- package/src/core/type_conversion.ts +1 -1
- package/src/core/types.ts +13 -3
- package/src/index.ts +1 -0
- package/src/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.ts +1 -1
- package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +604 -6
- package/src/proto-grpc/google/api/http.ts +1 -1
- package/src/proto-grpc/google/protobuf/descriptor.ts +242 -12
- package/src/proto-grpc/google/protobuf/timestamp.ts +9 -8
- package/src/proto-grpc/google/protobuf/wrappers.ts +38 -4
- package/src/proto-grpc/google/rpc/code.ts +1 -1
- package/src/proto-grpc/google/rpc/error_details.ts +5 -5
- package/src/proto-grpc/google/rpc/http.ts +1 -1
- package/src/proto-grpc/google/rpc/status.ts +1 -1
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { treeFilter, FilterOperatorType, FilterProperty } from "./tree_filter";
|
|
3
|
+
import type { Filter } from "./tree_filter";
|
|
4
|
+
|
|
5
|
+
describe("treeFilter builders — wire shape", () => {
|
|
6
|
+
// ── Leaf helpers ───────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
test("resourceTypeEq produces EQUAL / RESOURCE_TYPE / stringValue", () => {
|
|
9
|
+
const f = treeFilter.resourceTypeEq("Blob");
|
|
10
|
+
expect(f).toEqual<Filter>({
|
|
11
|
+
key: FilterProperty.RESOURCE_TYPE,
|
|
12
|
+
operator: FilterOperatorType.EQUAL,
|
|
13
|
+
value: { oneofKind: "stringValue", stringValue: "Blob" },
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("resourceTypeMatch produces MATCH / RESOURCE_TYPE / stringValue", () => {
|
|
18
|
+
const f = treeFilter.resourceTypeMatch("^Blob/");
|
|
19
|
+
expect(f).toEqual<Filter>({
|
|
20
|
+
key: FilterProperty.RESOURCE_TYPE,
|
|
21
|
+
operator: FilterOperatorType.MATCH,
|
|
22
|
+
value: { oneofKind: "stringValue", stringValue: "^Blob/" },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("fieldNameEq produces EQUAL / FIELD_NAME / stringValue", () => {
|
|
27
|
+
const f = treeFilter.fieldNameEq("output");
|
|
28
|
+
expect(f).toEqual<Filter>({
|
|
29
|
+
key: FilterProperty.FIELD_NAME,
|
|
30
|
+
operator: FilterOperatorType.EQUAL,
|
|
31
|
+
value: { oneofKind: "stringValue", stringValue: "output" },
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("fieldNameMatch produces MATCH / FIELD_NAME / stringValue", () => {
|
|
36
|
+
const f = treeFilter.fieldNameMatch("^__service");
|
|
37
|
+
expect(f).toEqual<Filter>({
|
|
38
|
+
key: FilterProperty.FIELD_NAME,
|
|
39
|
+
operator: FilterOperatorType.MATCH,
|
|
40
|
+
value: { oneofKind: "stringValue", stringValue: "^__service" },
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("isFinal(true) produces EQUAL / IS_FINAL / boolValue:true", () => {
|
|
45
|
+
const f = treeFilter.isFinal(true);
|
|
46
|
+
expect(f).toEqual<Filter>({
|
|
47
|
+
key: FilterProperty.IS_FINAL,
|
|
48
|
+
operator: FilterOperatorType.EQUAL,
|
|
49
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("isFinal(false) produces EQUAL / IS_FINAL / boolValue:false", () => {
|
|
54
|
+
const f = treeFilter.isFinal(false);
|
|
55
|
+
expect(f).toEqual<Filter>({
|
|
56
|
+
key: FilterProperty.IS_FINAL,
|
|
57
|
+
operator: FilterOperatorType.EQUAL,
|
|
58
|
+
value: { oneofKind: "boolValue", boolValue: false },
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("allOutputsFinal(true) produces EQUAL / ALL_OUTPUTS_FINAL / boolValue:true", () => {
|
|
63
|
+
const f = treeFilter.allOutputsFinal(true);
|
|
64
|
+
expect(f).toEqual<Filter>({
|
|
65
|
+
key: FilterProperty.ALL_OUTPUTS_FINAL,
|
|
66
|
+
operator: FilterOperatorType.EQUAL,
|
|
67
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("resourceReadyForCalculation(true) produces EQUAL / RESOURCE_READY_FOR_CALCULATION / boolValue:true", () => {
|
|
72
|
+
const f = treeFilter.resourceReadyForCalculation(true);
|
|
73
|
+
expect(f).toEqual<Filter>({
|
|
74
|
+
key: FilterProperty.RESOURCE_READY_FOR_CALCULATION,
|
|
75
|
+
operator: FilterOperatorType.EQUAL,
|
|
76
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("isDuplicate(false) produces EQUAL / IS_DUPLICATE / boolValue:false", () => {
|
|
81
|
+
const f = treeFilter.isDuplicate(false);
|
|
82
|
+
expect(f).toEqual<Filter>({
|
|
83
|
+
key: FilterProperty.IS_DUPLICATE,
|
|
84
|
+
operator: FilterOperatorType.EQUAL,
|
|
85
|
+
value: { oneofKind: "boolValue", boolValue: false },
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("hasErrors(true) produces EQUAL / HAS_ERRORS / boolValue:true", () => {
|
|
90
|
+
const f = treeFilter.hasErrors(true);
|
|
91
|
+
expect(f).toEqual<Filter>({
|
|
92
|
+
key: FilterProperty.HAS_ERRORS,
|
|
93
|
+
operator: FilterOperatorType.EQUAL,
|
|
94
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("outputsLocked(true) produces EQUAL / OUTPUTS_LOCKED / boolValue:true", () => {
|
|
99
|
+
const f = treeFilter.outputsLocked(true);
|
|
100
|
+
expect(f).toEqual<Filter>({
|
|
101
|
+
key: FilterProperty.OUTPUTS_LOCKED,
|
|
102
|
+
operator: FilterOperatorType.EQUAL,
|
|
103
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("readyOrDuplicateOrError() produces OR of the three conditions", () => {
|
|
108
|
+
const f = treeFilter.readyOrDuplicateOrError();
|
|
109
|
+
expect(f.operator).toBe(FilterOperatorType.OR);
|
|
110
|
+
if (f.value.oneofKind !== "filtersValue") throw new Error("expected filtersValue");
|
|
111
|
+
const { filters } = f.value.filtersValue;
|
|
112
|
+
expect(filters).toHaveLength(3);
|
|
113
|
+
expect(filters[0]).toEqual<Filter>({
|
|
114
|
+
key: FilterProperty.RESOURCE_READY_FOR_CALCULATION,
|
|
115
|
+
operator: FilterOperatorType.EQUAL,
|
|
116
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
117
|
+
});
|
|
118
|
+
expect(filters[1]).toEqual<Filter>({
|
|
119
|
+
key: FilterProperty.IS_DUPLICATE,
|
|
120
|
+
operator: FilterOperatorType.EQUAL,
|
|
121
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
122
|
+
});
|
|
123
|
+
expect(filters[2]).toEqual<Filter>({
|
|
124
|
+
key: FilterProperty.HAS_ERRORS,
|
|
125
|
+
operator: FilterOperatorType.EQUAL,
|
|
126
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("generic eq() and match() use supplied property", () => {
|
|
131
|
+
expect(treeFilter.eq(FilterProperty.RESOURCE_TYPE, "Foo")).toEqual<Filter>({
|
|
132
|
+
key: FilterProperty.RESOURCE_TYPE,
|
|
133
|
+
operator: FilterOperatorType.EQUAL,
|
|
134
|
+
value: { oneofKind: "stringValue", stringValue: "Foo" },
|
|
135
|
+
});
|
|
136
|
+
expect(treeFilter.match(FilterProperty.FIELD_NAME, "^out")).toEqual<Filter>({
|
|
137
|
+
key: FilterProperty.FIELD_NAME,
|
|
138
|
+
operator: FilterOperatorType.MATCH,
|
|
139
|
+
value: { oneofKind: "stringValue", stringValue: "^out" },
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// ── Group operators ────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
test("and() wraps children in AND / filtersValue", () => {
|
|
146
|
+
const a = treeFilter.resourceTypeEq("A");
|
|
147
|
+
const b = treeFilter.resourceTypeEq("B");
|
|
148
|
+
const f = treeFilter.and(a, b);
|
|
149
|
+
expect(f).toEqual<Filter>({
|
|
150
|
+
operator: FilterOperatorType.AND,
|
|
151
|
+
value: { oneofKind: "filtersValue", filtersValue: { filters: [a, b] } },
|
|
152
|
+
});
|
|
153
|
+
// key must be absent for group operators
|
|
154
|
+
expect(f.key).toBeUndefined();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("or() wraps children in OR / filtersValue", () => {
|
|
158
|
+
const a = treeFilter.isFinal(true);
|
|
159
|
+
const b = treeFilter.allOutputsFinal(true);
|
|
160
|
+
const f = treeFilter.or(a, b);
|
|
161
|
+
expect(f).toEqual<Filter>({
|
|
162
|
+
operator: FilterOperatorType.OR,
|
|
163
|
+
value: { oneofKind: "filtersValue", filtersValue: { filters: [a, b] } },
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("not() wraps single child in NOT / filtersValue", () => {
|
|
168
|
+
const inner = treeFilter.resourceTypeEq("Blob");
|
|
169
|
+
const f = treeFilter.not(inner);
|
|
170
|
+
expect(f).toEqual<Filter>({
|
|
171
|
+
operator: FilterOperatorType.NOT,
|
|
172
|
+
value: { oneofKind: "filtersValue", filtersValue: { filters: [inner] } },
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// ── Nested composition ────────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
test("nested AND(RESOURCE_TYPE, IS_FINAL) round-trips correctly", () => {
|
|
179
|
+
// Mirrors the Go-side mustCompileTraverseStopRules usage:
|
|
180
|
+
// AND(RESOURCE_TYPE == "StdMap", IS_FINAL == true)
|
|
181
|
+
const f = treeFilter.and(treeFilter.resourceTypeEq("StdMap"), treeFilter.isFinal(true));
|
|
182
|
+
|
|
183
|
+
expect(f.operator).toBe(FilterOperatorType.AND);
|
|
184
|
+
expect(f.key).toBeUndefined();
|
|
185
|
+
if (f.value.oneofKind !== "filtersValue") throw new Error("expected filtersValue");
|
|
186
|
+
|
|
187
|
+
const { filters } = f.value.filtersValue;
|
|
188
|
+
expect(filters).toHaveLength(2);
|
|
189
|
+
|
|
190
|
+
expect(filters[0]).toEqual<Filter>({
|
|
191
|
+
key: FilterProperty.RESOURCE_TYPE,
|
|
192
|
+
operator: FilterOperatorType.EQUAL,
|
|
193
|
+
value: { oneofKind: "stringValue", stringValue: "StdMap" },
|
|
194
|
+
});
|
|
195
|
+
expect(filters[1]).toEqual<Filter>({
|
|
196
|
+
key: FilterProperty.IS_FINAL,
|
|
197
|
+
operator: FilterOperatorType.EQUAL,
|
|
198
|
+
value: { oneofKind: "boolValue", boolValue: true },
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("NOT(AND(RESOURCE_TYPE, FIELD_NAME)) used in field_filters", () => {
|
|
203
|
+
// NOT( AND(RESOURCE_TYPE == "BlockPackCustom", FIELD_NAME == "template") )
|
|
204
|
+
const f = treeFilter.not(
|
|
205
|
+
treeFilter.and(
|
|
206
|
+
treeFilter.resourceTypeEq("BlockPackCustom"),
|
|
207
|
+
treeFilter.fieldNameEq("template"),
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
expect(f.operator).toBe(FilterOperatorType.NOT);
|
|
211
|
+
if (f.value.oneofKind !== "filtersValue") throw new Error("expected filtersValue");
|
|
212
|
+
const [andNode] = f.value.filtersValue.filters;
|
|
213
|
+
expect(andNode.operator).toBe(FilterOperatorType.AND);
|
|
214
|
+
if (andNode.value.oneofKind !== "filtersValue") throw new Error("expected filtersValue");
|
|
215
|
+
expect(andNode.value.filtersValue.filters).toHaveLength(2);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { ResourceAPI_Tree_Filter } from "../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api";
|
|
2
|
+
import {
|
|
3
|
+
ResourceAPI_Tree_Filter_OperatorType,
|
|
4
|
+
ResourceAPI_Tree_Filter_Property,
|
|
5
|
+
} from "../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api";
|
|
6
|
+
|
|
7
|
+
export type { ResourceAPI_Tree_Filter };
|
|
8
|
+
|
|
9
|
+
/** Shorthand alias for the filter predicate type. */
|
|
10
|
+
export type Filter = ResourceAPI_Tree_Filter;
|
|
11
|
+
|
|
12
|
+
/** Shorthand alias for the Property enum. */
|
|
13
|
+
export type Property = ResourceAPI_Tree_Filter_Property;
|
|
14
|
+
|
|
15
|
+
// Re-export enum namespaces under shorter aliases so callers need not import api.ts directly.
|
|
16
|
+
export { ResourceAPI_Tree_Filter_OperatorType as FilterOperatorType };
|
|
17
|
+
export { ResourceAPI_Tree_Filter_Property as FilterProperty };
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Builder helpers for {@link ResourceAPI_Tree_Filter} predicates used in
|
|
21
|
+
* `ResourceAPI_Tree_Request.fieldFilter` and `traverseStopRules`.
|
|
22
|
+
*
|
|
23
|
+
* Property restrictions (not enforced here; the backend rejects invalid combinations):
|
|
24
|
+
* - `FIELD_NAME` is only valid inside `fieldFilter`.
|
|
25
|
+
* - `IS_FINAL`, `ALL_OUTPUTS_FINAL`, and resource-level boolean predicates are only valid inside `traverseStopRules`.
|
|
26
|
+
*/
|
|
27
|
+
export const treeFilter = {
|
|
28
|
+
// ── Group operators ──────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
and(...children: Filter[]): Filter {
|
|
31
|
+
return {
|
|
32
|
+
operator: ResourceAPI_Tree_Filter_OperatorType.AND,
|
|
33
|
+
value: { oneofKind: "filtersValue", filtersValue: { filters: children } },
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
or(...children: Filter[]): Filter {
|
|
38
|
+
return {
|
|
39
|
+
operator: ResourceAPI_Tree_Filter_OperatorType.OR,
|
|
40
|
+
value: { oneofKind: "filtersValue", filtersValue: { filters: children } },
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
not(child: Filter): Filter {
|
|
45
|
+
return {
|
|
46
|
+
operator: ResourceAPI_Tree_Filter_OperatorType.NOT,
|
|
47
|
+
value: { oneofKind: "filtersValue", filtersValue: { filters: [child] } },
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
// ── Generic leaf operators ───────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
/** Exact-match predicate on a string property. */
|
|
54
|
+
eq(prop: Property, value: string): Filter {
|
|
55
|
+
return {
|
|
56
|
+
key: prop,
|
|
57
|
+
operator: ResourceAPI_Tree_Filter_OperatorType.EQUAL,
|
|
58
|
+
value: { oneofKind: "stringValue", stringValue: value },
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
/** Regex-match predicate on a string property. */
|
|
63
|
+
match(prop: Property, pattern: string): Filter {
|
|
64
|
+
return {
|
|
65
|
+
key: prop,
|
|
66
|
+
operator: ResourceAPI_Tree_Filter_OperatorType.MATCH,
|
|
67
|
+
value: { oneofKind: "stringValue", stringValue: pattern },
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
/** Boolean-equality predicate. */
|
|
72
|
+
boolEq(prop: Property, value: boolean): Filter {
|
|
73
|
+
return {
|
|
74
|
+
key: prop,
|
|
75
|
+
operator: ResourceAPI_Tree_Filter_OperatorType.EQUAL,
|
|
76
|
+
value: { oneofKind: "boolValue", boolValue: value },
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// ── Typed convenience wrappers ───────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
/** Match resources whose type string equals `name` exactly. */
|
|
83
|
+
resourceTypeEq(name: string): Filter {
|
|
84
|
+
return treeFilter.eq(ResourceAPI_Tree_Filter_Property.RESOURCE_TYPE, name);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/** Match resources whose type string satisfies the regex `pattern`. */
|
|
88
|
+
resourceTypeMatch(pattern: string): Filter {
|
|
89
|
+
return treeFilter.match(ResourceAPI_Tree_Filter_Property.RESOURCE_TYPE, pattern);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Match field edges whose name equals `name` exactly.
|
|
94
|
+
* Valid only inside `fieldFilter`.
|
|
95
|
+
*/
|
|
96
|
+
fieldNameEq(name: string): Filter {
|
|
97
|
+
return treeFilter.eq(ResourceAPI_Tree_Filter_Property.FIELD_NAME, name);
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Match field edges whose name satisfies the regex `pattern`.
|
|
102
|
+
* Valid only inside `fieldFilter`.
|
|
103
|
+
*/
|
|
104
|
+
fieldNameMatch(pattern: string): Filter {
|
|
105
|
+
return treeFilter.match(ResourceAPI_Tree_Filter_Property.FIELD_NAME, pattern);
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Match resources where `is_final == value`.
|
|
110
|
+
* Valid only inside `traverseStopRules`.
|
|
111
|
+
*/
|
|
112
|
+
isFinal(value: boolean): Filter {
|
|
113
|
+
return treeFilter.boolEq(ResourceAPI_Tree_Filter_Property.IS_FINAL, value);
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Match resources where `all_outputs_final == value`.
|
|
118
|
+
* Valid only inside `traverseStopRules`.
|
|
119
|
+
*/
|
|
120
|
+
allOutputsFinal(value: boolean): Filter {
|
|
121
|
+
return treeFilter.boolEq(ResourceAPI_Tree_Filter_Property.ALL_OUTPUTS_FINAL, value);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Match resources where `resource_ready_for_calculation == value`.
|
|
126
|
+
* True when the resource is Original, inputs are locked, and all inputs are final.
|
|
127
|
+
* Valid only inside `traverseStopRules`.
|
|
128
|
+
*/
|
|
129
|
+
resourceReadyForCalculation(value: boolean): Filter {
|
|
130
|
+
return treeFilter.boolEq(
|
|
131
|
+
ResourceAPI_Tree_Filter_Property.RESOURCE_READY_FOR_CALCULATION,
|
|
132
|
+
value,
|
|
133
|
+
);
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Match resources where `is_duplicate == value`.
|
|
138
|
+
* True when the resource has a non-zero original_resource_id.
|
|
139
|
+
* Valid only inside `traverseStopRules`.
|
|
140
|
+
*/
|
|
141
|
+
isDuplicate(value: boolean): Filter {
|
|
142
|
+
return treeFilter.boolEq(ResourceAPI_Tree_Filter_Property.IS_DUPLICATE, value);
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Match resources where `has_errors == value`.
|
|
147
|
+
* True when the resource has at least one field carrying an error (aggregated
|
|
148
|
+
* has-error flag). Can be true even when the resource's own status is not
|
|
149
|
+
* Error (e.g., an Original resource with a failed input field).
|
|
150
|
+
* Valid only inside `traverseStopRules`.
|
|
151
|
+
*/
|
|
152
|
+
hasErrors(value: boolean): Filter {
|
|
153
|
+
return treeFilter.boolEq(ResourceAPI_Tree_Filter_Property.HAS_ERRORS, value);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Match resources where `outputs_locked == value`.
|
|
158
|
+
* Valid only inside `traverseStopRules`.
|
|
159
|
+
*/
|
|
160
|
+
outputsLocked(value: boolean): Filter {
|
|
161
|
+
return treeFilter.boolEq(ResourceAPI_Tree_Filter_Property.OUTPUTS_LOCKED, value);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Match resources that are ready-or-duplicate-or-error: mirrors the BFS
|
|
166
|
+
* `readyOrDuplicateOrError` predicate used by pl-tree to decide whether a
|
|
167
|
+
* resource subtree needs further polling.
|
|
168
|
+
*
|
|
169
|
+
* Use this as `traverseStopRules` to stop the server-side tree walk at
|
|
170
|
+
* exactly the same nodes where the client BFS would stop, eliminating
|
|
171
|
+
* unnecessary follow-up calls.
|
|
172
|
+
*
|
|
173
|
+
* Valid only inside `traverseStopRules`.
|
|
174
|
+
*/
|
|
175
|
+
readyOrDuplicateOrError(): Filter {
|
|
176
|
+
return treeFilter.or(
|
|
177
|
+
treeFilter.resourceReadyForCalculation(true),
|
|
178
|
+
treeFilter.isDuplicate(true),
|
|
179
|
+
treeFilter.hasErrors(true),
|
|
180
|
+
);
|
|
181
|
+
},
|
|
182
|
+
} as const;
|
|
@@ -26,7 +26,7 @@ import { throwPlNotFoundError } from "./errors";
|
|
|
26
26
|
|
|
27
27
|
const ResourceErrorField = "resourceError";
|
|
28
28
|
|
|
29
|
-
function resourceIsDeleted(proto: Resource): boolean {
|
|
29
|
+
export function resourceIsDeleted(proto: Resource): boolean {
|
|
30
30
|
return proto.deletedTime !== undefined && proto.deletedTime.seconds !== 0n;
|
|
31
31
|
}
|
|
32
32
|
|
package/src/core/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable eslint-js/no-restricted-syntax -- this file is the canonical place to construct SignedResourceId values; outside callers must use asSignedResourceId(). */
|
|
2
|
+
|
|
1
3
|
import type { Branded } from "@milaboratories/pl-model-common";
|
|
2
4
|
import { cachedDeserialize, notEmpty } from "@milaboratories/ts-helpers";
|
|
3
5
|
|
|
@@ -282,7 +284,6 @@ export function isNullSignedResourceId(
|
|
|
282
284
|
export function isNotNullSignedResourceId(
|
|
283
285
|
resourceId: OptionalSignedResourceId,
|
|
284
286
|
): resourceId is SignedResourceId {
|
|
285
|
-
// lint-allow-cast
|
|
286
287
|
return resourceId !== NullSignedResourceId;
|
|
287
288
|
}
|
|
288
289
|
|
|
@@ -294,10 +295,19 @@ export function ensureSignedResourceIdNotNull(
|
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
export function isSignedResourceId(resourceId: bigint | string): resourceId is SignedResourceId {
|
|
297
|
-
// lint-allow-cast
|
|
298
298
|
return typeof resourceId === "string" && resourceId.includes("|");
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
+
/** Validate a string as a SignedResourceId and return it with the branded type.
|
|
302
|
+
* Requires the format "<globalId>|<signatureHex>" with a non-empty signature. */
|
|
303
|
+
export function asSignedResourceId(str: string): SignedResourceId {
|
|
304
|
+
const pipeIdx = str.indexOf("|");
|
|
305
|
+
if (pipeIdx < 0) throw new Error(`Not a signed resource id (no '|' separator): ${str}`);
|
|
306
|
+
if (pipeIdx === 0) throw new Error(`Signed resource id has empty globalId: ${str}`);
|
|
307
|
+
if (pipeIdx === str.length - 1) throw new Error(`Signed resource id has empty signature: ${str}`);
|
|
308
|
+
return str as SignedResourceId;
|
|
309
|
+
}
|
|
310
|
+
|
|
301
311
|
/** Encode resource signature to base64url for embedding in URL-based handles. */
|
|
302
312
|
export function signatureToBase64Url(sig?: ResourceSignature): string {
|
|
303
313
|
return sig && sig.length > 0 ? Buffer.from(sig).toString("base64url") : "";
|
|
@@ -326,7 +336,7 @@ export function createSignedResourceId(
|
|
|
326
336
|
if (isNullResourceId(globalId)) throw new Error(`Null resource id.`);
|
|
327
337
|
|
|
328
338
|
const sigHex = signature ? Buffer.from(signature).toString("hex") : "";
|
|
329
|
-
return `${String(globalId)}|${sigHex}` as SignedResourceId;
|
|
339
|
+
return `${String(globalId)}|${sigHex}` as SignedResourceId;
|
|
330
340
|
}
|
|
331
341
|
|
|
332
342
|
export function parseSignedResourceId(resourceId: SignedResourceId): {
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./core/default_client";
|
|
|
9
9
|
export * from "./core/unauth_client";
|
|
10
10
|
export * from "./core/auth";
|
|
11
11
|
export * from "./core/final";
|
|
12
|
+
export * from "./core/tree_filter";
|
|
12
13
|
export * from "./core/user_resources";
|
|
13
14
|
export * from "./core/wire";
|
|
14
15
|
export * from "./helpers/tx_helpers";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @generated from protobuf file "github.com/googleapis/googleapis/google/rpc/status.proto" (package "google.rpc", syntax proto3)
|
|
3
3
|
// tslint:disable
|
|
4
4
|
//
|
|
5
|
-
// Copyright
|
|
5
|
+
// Copyright 2026 Google LLC
|
|
6
6
|
//
|
|
7
7
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
8
|
// you may not use this file except in compliance with the License.
|