@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.
Files changed (74) hide show
  1. package/dist/core/final.cjs.map +1 -1
  2. package/dist/core/final.js.map +1 -1
  3. package/dist/core/ll_client.cjs +7 -1
  4. package/dist/core/ll_client.cjs.map +1 -1
  5. package/dist/core/ll_client.d.ts.map +1 -1
  6. package/dist/core/ll_client.js +7 -1
  7. package/dist/core/ll_client.js.map +1 -1
  8. package/dist/core/ll_transaction.cjs +151 -26
  9. package/dist/core/ll_transaction.cjs.map +1 -1
  10. package/dist/core/ll_transaction.d.ts +1 -0
  11. package/dist/core/ll_transaction.d.ts.map +1 -1
  12. package/dist/core/ll_transaction.js +151 -26
  13. package/dist/core/ll_transaction.js.map +1 -1
  14. package/dist/core/transaction.cjs +89 -0
  15. package/dist/core/transaction.cjs.map +1 -1
  16. package/dist/core/transaction.d.ts +47 -1
  17. package/dist/core/transaction.d.ts.map +1 -1
  18. package/dist/core/transaction.js +90 -1
  19. package/dist/core/transaction.js.map +1 -1
  20. package/dist/core/tree_filter.cjs +106 -0
  21. package/dist/core/tree_filter.cjs.map +1 -0
  22. package/dist/core/tree_filter.d.ts +85 -0
  23. package/dist/core/tree_filter.d.ts.map +1 -0
  24. package/dist/core/tree_filter.js +106 -0
  25. package/dist/core/tree_filter.js.map +1 -0
  26. package/dist/core/type_conversion.cjs +1 -0
  27. package/dist/core/type_conversion.cjs.map +1 -1
  28. package/dist/core/type_conversion.js +1 -1
  29. package/dist/core/type_conversion.js.map +1 -1
  30. package/dist/core/types.cjs +10 -0
  31. package/dist/core/types.cjs.map +1 -1
  32. package/dist/core/types.d.ts +4 -1
  33. package/dist/core/types.d.ts.map +1 -1
  34. package/dist/core/types.js +10 -1
  35. package/dist/core/types.js.map +1 -1
  36. package/dist/index.cjs +6 -0
  37. package/dist/index.d.ts +5 -3
  38. package/dist/index.js +4 -2
  39. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.cjs.map +1 -1
  40. package/dist/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.js.map +1 -1
  41. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +450 -4
  42. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
  43. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +328 -2
  44. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
  45. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +449 -5
  46. package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
  47. package/dist/proto-grpc/google/protobuf/timestamp.cjs.map +1 -1
  48. package/dist/proto-grpc/google/protobuf/timestamp.d.ts +9 -8
  49. package/dist/proto-grpc/google/protobuf/timestamp.d.ts.map +1 -1
  50. package/dist/proto-grpc/google/protobuf/timestamp.js.map +1 -1
  51. package/dist/proto-grpc/google/rpc/code.cjs.map +1 -1
  52. package/dist/proto-grpc/google/rpc/code.js.map +1 -1
  53. package/package.json +6 -6
  54. package/src/core/final.ts +1 -1
  55. package/src/core/ll_client.ts +11 -1
  56. package/src/core/ll_transaction.test.ts +13 -18
  57. package/src/core/ll_transaction.ts +237 -60
  58. package/src/core/transaction.test.ts +38 -0
  59. package/src/core/transaction.ts +136 -1
  60. package/src/core/tree_filter.test.ts +217 -0
  61. package/src/core/tree_filter.ts +182 -0
  62. package/src/core/type_conversion.ts +1 -1
  63. package/src/core/types.ts +13 -3
  64. package/src/index.ts +1 -0
  65. package/src/proto-grpc/github.com/googleapis/googleapis/google/rpc/status.ts +1 -1
  66. package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +604 -6
  67. package/src/proto-grpc/google/api/http.ts +1 -1
  68. package/src/proto-grpc/google/protobuf/descriptor.ts +242 -12
  69. package/src/proto-grpc/google/protobuf/timestamp.ts +9 -8
  70. package/src/proto-grpc/google/protobuf/wrappers.ts +38 -4
  71. package/src/proto-grpc/google/rpc/code.ts +1 -1
  72. package/src/proto-grpc/google/rpc/error_details.ts +5 -5
  73. package/src/proto-grpc/google/rpc/http.ts +1 -1
  74. 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; // lint-allow-cast
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 2025 Google LLC
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.