@distilled.cloud/core 1.0.0-rc.1 → 1.0.0-rc.10
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/LICENSE +201 -0
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +16 -4
- package/lib/api.js.map +1 -1
- package/lib/category.d.ts +4 -4
- package/lib/category.js +4 -4
- package/lib/codegen/boolean-string-enums.d.ts +36 -0
- package/lib/codegen/boolean-string-enums.d.ts.map +1 -0
- package/lib/codegen/boolean-string-enums.js +94 -0
- package/lib/codegen/boolean-string-enums.js.map +1 -0
- package/lib/codegen/boolean-string-enums.test.d.ts +2 -0
- package/lib/codegen/boolean-string-enums.test.d.ts.map +1 -0
- package/lib/codegen/boolean-string-enums.test.js +147 -0
- package/lib/codegen/boolean-string-enums.test.js.map +1 -0
- package/lib/codegen/cli.d.ts +56 -6
- package/lib/codegen/cli.d.ts.map +1 -1
- package/lib/codegen/cli.js +59 -75
- package/lib/codegen/cli.js.map +1 -1
- package/lib/codegen/emit.d.ts +3 -7
- package/lib/codegen/emit.d.ts.map +1 -1
- package/lib/codegen/emit.js +6 -10
- package/lib/codegen/emit.js.map +1 -1
- package/lib/codegen/generator.d.ts.map +1 -1
- package/lib/codegen/generator.js +142 -20
- package/lib/codegen/generator.js.map +1 -1
- package/lib/codegen/graphql-client.d.ts +62 -0
- package/lib/codegen/graphql-client.d.ts.map +1 -0
- package/lib/codegen/graphql-client.js +294 -0
- package/lib/codegen/graphql-client.js.map +1 -0
- package/lib/codegen/graphql-client.test.d.ts +2 -0
- package/lib/codegen/graphql-client.test.d.ts.map +1 -0
- package/lib/codegen/graphql-client.test.js +311 -0
- package/lib/codegen/graphql-client.test.js.map +1 -0
- package/lib/codegen/graphql.d.ts +207 -0
- package/lib/codegen/graphql.d.ts.map +1 -0
- package/lib/codegen/graphql.js +799 -0
- package/lib/codegen/graphql.js.map +1 -0
- package/lib/codegen/openapi-cli.d.ts +17 -3
- package/lib/codegen/openapi-cli.d.ts.map +1 -1
- package/lib/codegen/openapi-cli.js +50 -48
- package/lib/codegen/openapi-cli.js.map +1 -1
- package/lib/codegen/openapi.d.ts +69 -6
- package/lib/codegen/openapi.d.ts.map +1 -1
- package/lib/codegen/openapi.js +173 -16
- package/lib/codegen/openapi.js.map +1 -1
- package/lib/codegen/patches.d.ts +65 -0
- package/lib/codegen/patches.d.ts.map +1 -0
- package/lib/codegen/patches.js +236 -0
- package/lib/codegen/patches.js.map +1 -0
- package/lib/codegen/patches.test.d.ts +2 -0
- package/lib/codegen/patches.test.d.ts.map +1 -0
- package/lib/codegen/patches.test.js +105 -0
- package/lib/codegen/patches.test.js.map +1 -0
- package/lib/codegen/proto.d.ts +121 -0
- package/lib/codegen/proto.d.ts.map +1 -0
- package/lib/codegen/proto.js +962 -0
- package/lib/codegen/proto.js.map +1 -0
- package/lib/codegen/rewrite-operation-ids.d.ts +131 -0
- package/lib/codegen/rewrite-operation-ids.d.ts.map +1 -0
- package/lib/codegen/rewrite-operation-ids.js +1079 -0
- package/lib/codegen/rewrite-operation-ids.js.map +1 -0
- package/lib/codegen/rewrite-operation-ids.test.d.ts +2 -0
- package/lib/codegen/rewrite-operation-ids.test.d.ts.map +1 -0
- package/lib/codegen/rewrite-operation-ids.test.js +533 -0
- package/lib/codegen/rewrite-operation-ids.test.js.map +1 -0
- package/lib/codegen/spec-path.d.ts +16 -0
- package/lib/codegen/spec-path.d.ts.map +1 -0
- package/lib/codegen/spec-path.js +101 -0
- package/lib/codegen/spec-path.js.map +1 -0
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +17 -13
- package/lib/errors.js.map +1 -1
- package/lib/graphql.d.ts +284 -0
- package/lib/graphql.d.ts.map +1 -0
- package/lib/graphql.fixture.d.ts +249 -0
- package/lib/graphql.fixture.d.ts.map +1 -0
- package/lib/graphql.fixture.js +240 -0
- package/lib/graphql.fixture.js.map +1 -0
- package/lib/graphql.js +718 -0
- package/lib/graphql.js.map +1 -0
- package/lib/graphql.test.d.ts +2 -0
- package/lib/graphql.test.d.ts.map +1 -0
- package/lib/graphql.test.js +780 -0
- package/lib/graphql.test.js.map +1 -0
- package/lib/graphql.types.d.ts +2 -0
- package/lib/graphql.types.d.ts.map +1 -0
- package/lib/graphql.types.js +45 -0
- package/lib/graphql.types.js.map +1 -0
- package/lib/json-patch.d.ts +18 -11
- package/lib/json-patch.d.ts.map +1 -1
- package/lib/json-patch.js +63 -25
- package/lib/json-patch.js.map +1 -1
- package/lib/pagination.d.ts +43 -3
- package/lib/pagination.d.ts.map +1 -1
- package/lib/pagination.js +91 -5
- package/lib/pagination.js.map +1 -1
- package/lib/protocol-http.d.ts.map +1 -1
- package/lib/protocol-http.js +62 -26
- package/lib/protocol-http.js.map +1 -1
- package/lib/protocol-http.test.d.ts +2 -0
- package/lib/protocol-http.test.d.ts.map +1 -0
- package/lib/protocol-http.test.js +88 -0
- package/lib/protocol-http.test.js.map +1 -0
- package/lib/protocol-rest.d.ts +12 -2
- package/lib/protocol-rest.d.ts.map +1 -1
- package/lib/protocol-rest.js +17 -3
- package/lib/protocol-rest.js.map +1 -1
- package/lib/retry.js +1 -1
- package/lib/schema.d.ts +1 -1
- package/lib/schema.d.ts.map +1 -1
- package/lib/schema.js +1 -1
- package/lib/schema.js.map +1 -1
- package/lib/trait.d.ts +27 -3
- package/lib/trait.d.ts.map +1 -1
- package/lib/trait.js +17 -1
- package/lib/trait.js.map +1 -1
- package/package.json +13 -10
- package/src/api.ts +18 -4
- package/src/category.ts +4 -4
- package/src/codegen/boolean-string-enums.test.ts +168 -0
- package/src/codegen/boolean-string-enums.ts +106 -0
- package/src/codegen/cli.ts +127 -110
- package/src/codegen/emit.ts +6 -10
- package/src/codegen/generator.ts +151 -21
- package/src/codegen/graphql-client.test.ts +386 -0
- package/src/codegen/graphql-client.ts +419 -0
- package/src/codegen/graphql.ts +1217 -0
- package/src/codegen/openapi-cli.ts +75 -59
- package/src/codegen/openapi.ts +255 -16
- package/src/codegen/patches.test.ts +130 -0
- package/src/codegen/patches.ts +291 -0
- package/src/codegen/proto.ts +1128 -0
- package/src/codegen/rewrite-operation-ids.test.ts +563 -0
- package/src/codegen/rewrite-operation-ids.ts +1206 -0
- package/src/codegen/spec-path.ts +115 -0
- package/src/errors.ts +20 -25
- package/src/graphql.fixture.ts +371 -0
- package/src/graphql.test.ts +974 -0
- package/src/graphql.ts +1321 -0
- package/src/graphql.types.ts +185 -0
- package/src/json-patch.ts +82 -25
- package/src/pagination.ts +134 -7
- package/src/protocol-http.test.ts +107 -0
- package/src/protocol-http.ts +67 -31
- package/src/protocol-rest.ts +28 -4
- package/src/retry.ts +1 -1
- package/src/schema.ts +2 -2
- package/src/trait.ts +39 -3
package/src/protocol-http.ts
CHANGED
|
@@ -22,10 +22,12 @@ import {
|
|
|
22
22
|
keyDictionarySymbol,
|
|
23
23
|
labelSymbol,
|
|
24
24
|
querySymbol,
|
|
25
|
+
stringEncodedSymbol,
|
|
25
26
|
unionCasesSymbol,
|
|
26
27
|
type ErrorMatcher,
|
|
27
28
|
type HttpTrait,
|
|
28
29
|
type KeyDictionaryEntries,
|
|
30
|
+
type UnionDiscriminator,
|
|
29
31
|
} from "./trait.ts";
|
|
30
32
|
|
|
31
33
|
//#region AST helpers (survive S.optional / Suspend / transforms)
|
|
@@ -167,13 +169,19 @@ export const mapKeys = (
|
|
|
167
169
|
|
|
168
170
|
// Discriminated union whose cases the API returns merged (every case's
|
|
169
171
|
// keys present, `null` for the inactive ones). Map wire names, then keep
|
|
170
|
-
// only the active case's keys.
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
172
|
+
// only the active case's keys. The case is read off the union's
|
|
173
|
+
// discriminator when it has one — cases that share a key set (every
|
|
174
|
+
// resource kind of `resource_tagging`, every page-rule action) are
|
|
175
|
+
// indistinguishable by keys alone. Without one, a case does NOT need every
|
|
176
|
+
// key present — arms carry optional members the wire may omit — so the
|
|
177
|
+
// active case is the one that best EXPLAINS the value: most present,
|
|
178
|
+
// non-null keys covered, minus present keys the case cannot explain (ties
|
|
179
|
+
// break by coverage, then declaration order).
|
|
175
180
|
const unionCases = getAnn(ast, unionCasesSymbol) as
|
|
176
|
-
|
|
|
181
|
+
| {
|
|
182
|
+
readonly cases: ReadonlyArray<ReadonlyArray<string>>;
|
|
183
|
+
readonly discriminator?: UnionDiscriminator;
|
|
184
|
+
}
|
|
177
185
|
| undefined;
|
|
178
186
|
if (unionCases && direction === "decode" && !Array.isArray(value)) {
|
|
179
187
|
const obj = (
|
|
@@ -182,25 +190,32 @@ export const mapKeys = (
|
|
|
182
190
|
const present = Object.keys(obj).filter(
|
|
183
191
|
(k) => obj[k] !== undefined && obj[k] !== null,
|
|
184
192
|
);
|
|
193
|
+
const disc = unionCases.discriminator;
|
|
194
|
+
const tag = disc ? obj[disc.key] : undefined;
|
|
195
|
+
const tagged = typeof tag === "string" ? disc!.values.indexOf(tag) : -1;
|
|
185
196
|
let best:
|
|
186
197
|
| { keys: ReadonlyArray<string>; score: number; matched: number }
|
|
187
198
|
| undefined;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
199
|
+
if (tagged >= 0) {
|
|
200
|
+
best = { keys: unionCases.cases[tagged]!, score: 0, matched: 0 };
|
|
201
|
+
} else {
|
|
202
|
+
for (const keys of unionCases.cases) {
|
|
203
|
+
const set = new Set(keys);
|
|
204
|
+
let matched = 0;
|
|
205
|
+
let excess = 0;
|
|
206
|
+
for (const k of present) {
|
|
207
|
+
if (set.has(k)) matched++;
|
|
208
|
+
else excess++;
|
|
209
|
+
}
|
|
210
|
+
const score = matched - excess;
|
|
211
|
+
if (
|
|
212
|
+
matched > 0 &&
|
|
213
|
+
(!best ||
|
|
214
|
+
score > best.score ||
|
|
215
|
+
(score === best.score && matched > best.matched))
|
|
216
|
+
) {
|
|
217
|
+
best = { keys, score, matched };
|
|
218
|
+
}
|
|
204
219
|
}
|
|
205
220
|
}
|
|
206
221
|
if (best) {
|
|
@@ -319,6 +334,18 @@ const BODYLESS = new Set(["GET", "HEAD"]);
|
|
|
319
334
|
* filter matching nothing — the call "succeeds" with zero results and the
|
|
320
335
|
* bug is invisible to the caller.
|
|
321
336
|
*/
|
|
337
|
+
/**
|
|
338
|
+
* Value form for a `StringEncoded()` member: the string spelling of the
|
|
339
|
+
* value, element-wise for lists. `null` stays `null` — an API that models a
|
|
340
|
+
* flag as `"true" | "false"` still means "unset" by null, not `"null"`.
|
|
341
|
+
*/
|
|
342
|
+
const stringEncode = (value: unknown): unknown =>
|
|
343
|
+
value === null
|
|
344
|
+
? null
|
|
345
|
+
: Array.isArray(value)
|
|
346
|
+
? value.map(stringEncode)
|
|
347
|
+
: String(value);
|
|
348
|
+
|
|
322
349
|
const appendQuery = (
|
|
323
350
|
query: URLSearchParams,
|
|
324
351
|
name: string,
|
|
@@ -482,12 +509,13 @@ export const buildRequest = ({
|
|
|
482
509
|
} else if (hasPropAnn(prop, httpBodySymbol)) {
|
|
483
510
|
rawBody = mapKeys(prop.type, value, "encode", rootDict);
|
|
484
511
|
} else {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
512
|
+
// T.StringEncoded(): the API takes this member's value only as its
|
|
513
|
+
// string spelling (`true` → `"true"`), while the TS surface keeps the
|
|
514
|
+
// natural type. Explicit here so a JSON body carries the string too,
|
|
515
|
+
// rather than relying on the multipart encoder's own `String()`.
|
|
516
|
+
body[nameOf(prop, bodySymbol)] = hasPropAnn(prop, stringEncodedSymbol)
|
|
517
|
+
? stringEncode(value)
|
|
518
|
+
: mapKeys(prop.type, value, "encode", rootDict);
|
|
491
519
|
}
|
|
492
520
|
}
|
|
493
521
|
|
|
@@ -616,9 +644,17 @@ export const buildRequest = ({
|
|
|
616
644
|
request = request.pipe(HttpClientRequest.bodyJsonUnsafe(rawBody));
|
|
617
645
|
}
|
|
618
646
|
} else if (
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
647
|
+
// A GET/HEAD sends a body only when body members actually carry values.
|
|
648
|
+
// Some APIs really do document GET-with-body: MongoDB Atlas's
|
|
649
|
+
// `…/lineItems:search` declares a `requestBody` on `get`, and AWS EFS's
|
|
650
|
+
// DescribeAccountPreferences takes `{ MaxResults, NextToken }` on a GET.
|
|
651
|
+
// Suppressing it outright dropped those members silently — the request
|
|
652
|
+
// succeeded, unfiltered, with nothing on the wire to show why.
|
|
653
|
+
//
|
|
654
|
+
// The `{}`-when-empty rule below stays limited to methods that expect a
|
|
655
|
+
// body, so a bodyless method with nothing set still sends nothing.
|
|
656
|
+
Object.keys(body).length > 0 ||
|
|
657
|
+
(hasBodyMembers && !BODYLESS.has(http.method) && http.method !== "DELETE")
|
|
622
658
|
) {
|
|
623
659
|
// Send `{}` rather than no body when the schema declares body members —
|
|
624
660
|
// some endpoints reject a missing JSON body outright.
|
package/src/protocol-rest.ts
CHANGED
|
@@ -30,9 +30,10 @@ import type * as AST from "effect/SchemaAST";
|
|
|
30
30
|
import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
31
31
|
import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse";
|
|
32
32
|
import * as API from "./api.ts";
|
|
33
|
-
import { makeAnnotation } from "./trait.ts";
|
|
33
|
+
import { httpSymbol, makeAnnotation, type HttpTrait } from "./trait.ts";
|
|
34
34
|
import {
|
|
35
35
|
buildRequest,
|
|
36
|
+
getAnn,
|
|
36
37
|
getPropAnn,
|
|
37
38
|
getProps,
|
|
38
39
|
isOpaqueValue,
|
|
@@ -185,8 +186,18 @@ export interface RestProtocolOptions<C> {
|
|
|
185
186
|
* `<Sdk>OpContext` annotations.
|
|
186
187
|
*/
|
|
187
188
|
readonly credentials: Effect.Effect<C, any, any>;
|
|
188
|
-
/**
|
|
189
|
-
|
|
189
|
+
/**
|
|
190
|
+
* API base URL from the resolved credentials. Receives the operation's
|
|
191
|
+
* route so APIs with more than one endpoint (e.g. S2's account vs
|
|
192
|
+
* per-basin hosts, chosen by path) can route per request; single-endpoint
|
|
193
|
+
* providers ignore the argument. A THROWN error (e.g. a required scope
|
|
194
|
+
* missing from the credentials) is caught and surfaced on the calling
|
|
195
|
+
* effect's error channel rather than dying as a defect.
|
|
196
|
+
*/
|
|
197
|
+
readonly baseUrl: (
|
|
198
|
+
credentials: C,
|
|
199
|
+
target: { readonly uri: string; readonly method: string },
|
|
200
|
+
) => string;
|
|
190
201
|
/** Auth (and any fixed) headers from the resolved credentials. */
|
|
191
202
|
readonly headers: (credentials: C) => Record<string, string>;
|
|
192
203
|
/**
|
|
@@ -257,10 +268,23 @@ export const makeRestProtocol = <C>(
|
|
|
257
268
|
}) =>
|
|
258
269
|
Effect.gen(function* () {
|
|
259
270
|
const creds = yield* options.credentials as Effect.Effect<C>;
|
|
271
|
+
const http = getAnn(inputAst, httpSymbol) as HttpTrait | undefined;
|
|
272
|
+
let baseUrl: string;
|
|
273
|
+
try {
|
|
274
|
+
baseUrl = options.baseUrl(creds, {
|
|
275
|
+
uri: http?.uri ?? "",
|
|
276
|
+
method: http?.method ?? "",
|
|
277
|
+
});
|
|
278
|
+
} catch (e) {
|
|
279
|
+
// A baseUrl that refuses the route (missing scope in the
|
|
280
|
+
// credentials) fails the call with that error, typed for callers by
|
|
281
|
+
// the operation's declared error channel.
|
|
282
|
+
return yield* Effect.fail(e);
|
|
283
|
+
}
|
|
260
284
|
return buildRequest({
|
|
261
285
|
input: unwrapRedactedDeep(input),
|
|
262
286
|
inputAst,
|
|
263
|
-
baseUrl
|
|
287
|
+
baseUrl,
|
|
264
288
|
headers: options.headers(creds),
|
|
265
289
|
mapMemberHeader: options.mapMemberHeader,
|
|
266
290
|
unknownKeyToWire: options.unknownKeyToWire,
|
package/src/retry.ts
CHANGED
|
@@ -121,7 +121,7 @@ export const ServerRetryHintCapMs = Context.Service<number>(
|
|
|
121
121
|
export const serverRetryHintCapLayer = (capMs: number) =>
|
|
122
122
|
Layer.succeed(ServerRetryHintCapMs, capMs);
|
|
123
123
|
|
|
124
|
-
const serverRetryHintCapMsConfig: Config.Config<number> = Config.
|
|
124
|
+
const serverRetryHintCapMsConfig: Config.Config<number> = Config.String(
|
|
125
125
|
ENV_SERVER_RETRY_HINT_CAP_MS,
|
|
126
126
|
).pipe(
|
|
127
127
|
Config.map((raw) => {
|
package/src/schema.ts
CHANGED
|
@@ -23,7 +23,7 @@ export * from "effect/Schema";
|
|
|
23
23
|
|
|
24
24
|
import * as S from "effect/Schema";
|
|
25
25
|
|
|
26
|
-
type AnyFn = (...args: any[]) => any;
|
|
26
|
+
export type AnyFn = (...args: any[]) => any;
|
|
27
27
|
|
|
28
28
|
// Construction surface — collapse to `any` so generics are never instantiated.
|
|
29
29
|
export const optional: AnyFn = S.optional as AnyFn;
|
|
@@ -48,6 +48,6 @@ export const Null: any = S.Null;
|
|
|
48
48
|
export const Void: any = S.Void;
|
|
49
49
|
export const Any: any = S.Any;
|
|
50
50
|
|
|
51
|
-
// NOTE: `Schema` / `Codec` (types) and `
|
|
51
|
+
// NOTE: `Schema` / `Codec` (types) and `TaggedError` are intentionally NOT
|
|
52
52
|
// overridden — they flow through `export *` with real types so cast targets and
|
|
53
53
|
// typed error classes stay precise.
|
package/src/trait.ts
CHANGED
|
@@ -2,7 +2,7 @@ export const annotationMetaSymbol = Symbol.for(
|
|
|
2
2
|
"@distilled.cloud/core/annotation-meta",
|
|
3
3
|
);
|
|
4
4
|
|
|
5
|
-
type Annotatable = {
|
|
5
|
+
export type Annotatable = {
|
|
6
6
|
annotate(annotations: any): Annotatable;
|
|
7
7
|
};
|
|
8
8
|
|
|
@@ -147,6 +147,20 @@ export const httpBodySymbol = Symbol.for("@distilled.cloud/core/http-body");
|
|
|
147
147
|
*/
|
|
148
148
|
export const HttpBody = () => makeAnnotation(httpBodySymbol, true);
|
|
149
149
|
|
|
150
|
+
export const stringEncodedSymbol = Symbol.for(
|
|
151
|
+
"@distilled.cloud/core/http/string-encoded",
|
|
152
|
+
);
|
|
153
|
+
/**
|
|
154
|
+
* Marks a member the API only accepts as the STRING spelling of its value —
|
|
155
|
+
* `true` travels as `"true"`, `3` as `"3"` — while the TS surface keeps the
|
|
156
|
+
* natural type (e.g. Cloudflare's multipart `validation_enabled`, which the
|
|
157
|
+
* docs model as the enum `"true" | "false"`). The protocol stringifies body
|
|
158
|
+
* members on encode, element-wise for lists; `null` is left alone. Query and
|
|
159
|
+
* header bindings already serialize their values as strings, so the trait is
|
|
160
|
+
* documentation there.
|
|
161
|
+
*/
|
|
162
|
+
export const StringEncoded = () => makeAnnotation(stringEncodedSymbol, true);
|
|
163
|
+
|
|
150
164
|
export const formDataFileSymbol = Symbol.for(
|
|
151
165
|
"@distilled.cloud/core/form-data-file",
|
|
152
166
|
);
|
|
@@ -184,15 +198,37 @@ export const KeyDictionary = (dict: KeyDictionaryEntries) =>
|
|
|
184
198
|
makeAnnotation(keyDictionarySymbol, dict);
|
|
185
199
|
|
|
186
200
|
export const unionCasesSymbol = Symbol.for("@distilled.cloud/core/union-cases");
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The literal member every case of a union fixes to its own value — what the
|
|
204
|
+
* docs call the resource `type` or the action `id`. `values[i]` is the value
|
|
205
|
+
* case `i` carries.
|
|
206
|
+
*/
|
|
207
|
+
export interface UnionDiscriminator {
|
|
208
|
+
readonly key: string;
|
|
209
|
+
readonly values: ReadonlyArray<string>;
|
|
210
|
+
}
|
|
211
|
+
|
|
187
212
|
/**
|
|
188
213
|
* Marks an opaque schema standing in for a discriminated union of object
|
|
189
214
|
* cases, carrying each case's TS-facing key set. For APIs that return every
|
|
190
215
|
* case's keys with `null` for the inactive ones, the protocol uses these key
|
|
191
216
|
* sets to pick the active case and drop the others, so consumers' `"key" in
|
|
192
217
|
* value` discrimination works.
|
|
218
|
+
*
|
|
219
|
+
* Key sets alone cannot tell apart cases that share them — the 41 resource
|
|
220
|
+
* kinds of `resource_tagging`, the 34 page-rule actions — so when the union
|
|
221
|
+
* has a discriminator the protocol reads the case off it and only falls back
|
|
222
|
+
* to key-set scoring for a value whose tag is missing or unknown.
|
|
193
223
|
*/
|
|
194
|
-
export const UnionCases = (
|
|
195
|
-
|
|
224
|
+
export const UnionCases = (
|
|
225
|
+
cases: ReadonlyArray<ReadonlyArray<string>>,
|
|
226
|
+
discriminator?: UnionDiscriminator,
|
|
227
|
+
) =>
|
|
228
|
+
makeAnnotation(
|
|
229
|
+
unionCasesSymbol,
|
|
230
|
+
discriminator ? { cases, discriminator } : { cases },
|
|
231
|
+
);
|
|
196
232
|
//#endregion
|
|
197
233
|
|
|
198
234
|
//#region Error matcher traits
|