@beignet/core 0.0.34 → 0.0.36
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/CHANGELOG.md +23 -0
- package/README.md +145 -15
- package/dist/agent-capabilities/index.d.ts +197 -0
- package/dist/agent-capabilities/index.d.ts.map +1 -0
- package/dist/agent-capabilities/index.js +282 -0
- package/dist/agent-capabilities/index.js.map +1 -0
- package/dist/application/index.d.ts +2 -2
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +13 -3
- package/dist/application/index.js.map +1 -1
- package/dist/client/client.d.ts +2 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +33 -16
- package/dist/client/client.js.map +1 -1
- package/dist/contracts/contract-builder.d.ts +5 -3
- package/dist/contracts/contract-builder.d.ts.map +1 -1
- package/dist/contracts/contract-builder.js +3 -12
- package/dist/contracts/contract-builder.js.map +1 -1
- package/dist/contracts/contract-group.d.ts +2 -2
- package/dist/contracts/contract-group.d.ts.map +1 -1
- package/dist/contracts/contract-group.js +2 -1
- package/dist/contracts/contract-group.js.map +1 -1
- package/dist/contracts/metadata.d.ts +3 -0
- package/dist/contracts/metadata.d.ts.map +1 -0
- package/dist/contracts/metadata.js +14 -0
- package/dist/contracts/metadata.js.map +1 -0
- package/dist/contracts/types.d.ts +20 -0
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.js.map +1 -1
- package/dist/events/index.d.ts +4 -4
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +5 -5
- package/dist/events/index.js.map +1 -1
- package/dist/idempotency/index.d.ts.map +1 -1
- package/dist/idempotency/index.js +14 -9
- package/dist/idempotency/index.js.map +1 -1
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +10 -4
- package/dist/jobs/index.js.map +1 -1
- package/dist/openapi/index.js +15 -8
- package/dist/openapi/index.js.map +1 -1
- package/dist/ports/auth.d.ts +2 -0
- package/dist/ports/auth.d.ts.map +1 -1
- package/dist/ports/auth.js +2 -0
- package/dist/ports/auth.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/provider.d.ts +9 -1
- package/dist/providers/provider.d.ts.map +1 -1
- package/dist/providers/provider.js.map +1 -1
- package/dist/schedules/index.d.ts +6 -13
- package/dist/schedules/index.d.ts.map +1 -1
- package/dist/schedules/index.js +24 -44
- package/dist/schedules/index.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/request-executor.d.ts +72 -0
- package/dist/server/request-executor.d.ts.map +1 -0
- package/dist/server/request-executor.js +781 -0
- package/dist/server/request-executor.js.map +1 -0
- package/dist/server/request-preparation.d.ts +39 -0
- package/dist/server/request-preparation.d.ts.map +1 -0
- package/dist/server/request-preparation.js +248 -0
- package/dist/server/request-preparation.js.map +1 -0
- package/dist/server/response-finalization.d.ts +34 -0
- package/dist/server/response-finalization.d.ts.map +1 -0
- package/dist/server/response-finalization.js +279 -0
- package/dist/server/response-finalization.js.map +1 -0
- package/dist/server/route-definitions.d.ts +139 -0
- package/dist/server/route-definitions.d.ts.map +1 -0
- package/dist/server/route-definitions.js +94 -0
- package/dist/server/route-definitions.js.map +1 -0
- package/dist/server/route-matching.d.ts +15 -0
- package/dist/server/route-matching.d.ts.map +1 -0
- package/dist/server/route-matching.js +46 -0
- package/dist/server/route-matching.js.map +1 -0
- package/dist/server/server.d.ts +6 -147
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +29 -1350
- package/dist/server/server.js.map +1 -1
- package/dist/server/use-case-route.d.ts +1 -1
- package/dist/tracing/index.d.ts +1 -1
- package/dist/tracing/index.d.ts.map +1 -1
- package/dist/tracing/index.js.map +1 -1
- package/dist/uploads/client.js +4 -7
- package/dist/uploads/client.js.map +1 -1
- package/dist/uploads/index.d.ts.map +1 -1
- package/dist/uploads/index.js +59 -12
- package/dist/uploads/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/app-architecture/SKILL.md +27 -1
- package/src/agent-capabilities/index.ts +643 -0
- package/src/application/index.ts +19 -5
- package/src/client/client.ts +41 -17
- package/src/contracts/contract-builder.ts +8 -16
- package/src/contracts/contract-group.ts +4 -6
- package/src/contracts/metadata.ts +20 -0
- package/src/contracts/types.ts +39 -0
- package/src/events/index.ts +16 -20
- package/src/idempotency/index.ts +17 -9
- package/src/jobs/index.ts +8 -3
- package/src/openapi/index.ts +24 -12
- package/src/ports/auth.ts +2 -0
- package/src/providers/index.ts +1 -0
- package/src/providers/provider.ts +17 -2
- package/src/schedules/index.ts +57 -63
- package/src/server/index.ts +4 -2
- package/src/server/request-executor.ts +1170 -0
- package/src/server/request-preparation.ts +409 -0
- package/src/server/response-finalization.ts +385 -0
- package/src/server/route-definitions.ts +338 -0
- package/src/server/route-matching.ts +64 -0
- package/src/server/server.ts +63 -2139
- package/src/server/use-case-route.ts +1 -1
- package/src/tracing/index.ts +1 -0
- package/src/uploads/client.ts +4 -7
- package/src/uploads/index.ts +70 -12
package/src/client/client.ts
CHANGED
|
@@ -485,9 +485,11 @@ async function validateHeaderSchemas(
|
|
|
485
485
|
type PrimitiveParam = string | number | boolean;
|
|
486
486
|
type QueryParamValue =
|
|
487
487
|
| PrimitiveParam
|
|
488
|
+
| Date
|
|
489
|
+
| Record<string, unknown>
|
|
488
490
|
| null
|
|
489
491
|
| undefined
|
|
490
|
-
| Array<PrimitiveParam
|
|
492
|
+
| Array<PrimitiveParam | Date | Record<string, unknown>>;
|
|
491
493
|
|
|
492
494
|
type PathParams = Record<string, PrimitiveParam>;
|
|
493
495
|
type QueryParams = Record<string, QueryParamValue>;
|
|
@@ -946,26 +948,12 @@ export class Endpoint<
|
|
|
946
948
|
const normalizedQuery = queryToSerialize ?? query;
|
|
947
949
|
for (const [key, value] of Object.entries(normalizedQuery)) {
|
|
948
950
|
if (value !== undefined && value !== null) {
|
|
949
|
-
if (typeof value === "object" && !Array.isArray(value)) {
|
|
950
|
-
throw new ContractError({
|
|
951
|
-
source: "client",
|
|
952
|
-
code: "INVALID_QUERY_PARAM",
|
|
953
|
-
message: `Query parameter "${key}" contains a non-serializable object. Use primitive values or arrays of primitives.`,
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
951
|
if (Array.isArray(value)) {
|
|
957
952
|
for (const v of value) {
|
|
958
|
-
|
|
959
|
-
throw new ContractError({
|
|
960
|
-
source: "client",
|
|
961
|
-
code: "INVALID_QUERY_PARAM",
|
|
962
|
-
message: `Query parameter "${key}" contains a non-serializable array element. Use primitive values only.`,
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
params.append(key, String(v));
|
|
953
|
+
params.append(key, this.serializeQueryParam(key, v));
|
|
966
954
|
}
|
|
967
955
|
} else {
|
|
968
|
-
params.append(key,
|
|
956
|
+
params.append(key, this.serializeQueryParam(key, value));
|
|
969
957
|
}
|
|
970
958
|
}
|
|
971
959
|
}
|
|
@@ -983,6 +971,42 @@ export class Endpoint<
|
|
|
983
971
|
return baseUrl + url;
|
|
984
972
|
}
|
|
985
973
|
|
|
974
|
+
private serializeQueryParam(key: string, value: unknown): string {
|
|
975
|
+
if (
|
|
976
|
+
typeof value === "string" ||
|
|
977
|
+
typeof value === "number" ||
|
|
978
|
+
typeof value === "boolean"
|
|
979
|
+
) {
|
|
980
|
+
return String(value);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if (value instanceof Date) {
|
|
984
|
+
if (!Number.isFinite(value.getTime())) {
|
|
985
|
+
throw this.invalidQueryParam(key, "contains an invalid Date");
|
|
986
|
+
}
|
|
987
|
+
return value.toISOString();
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (typeof value === "object" && value !== null) {
|
|
991
|
+
try {
|
|
992
|
+
const serialized = JSON.stringify(value);
|
|
993
|
+
if (serialized !== undefined) return serialized;
|
|
994
|
+
} catch {
|
|
995
|
+
// Report the contract/query key below instead of leaking JSON internals.
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
throw this.invalidQueryParam(key, "contains a non-serializable value");
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
private invalidQueryParam(key: string, reason: string): ContractError {
|
|
1003
|
+
return new ContractError({
|
|
1004
|
+
source: "client",
|
|
1005
|
+
code: "INVALID_QUERY_PARAM",
|
|
1006
|
+
message: `Query parameter "${key}" ${reason}. Use primitive values, Dates, JSON-serializable objects, or arrays of those values.`,
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
|
|
986
1010
|
/**
|
|
987
1011
|
* Build request headers
|
|
988
1012
|
*/
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
mergeCatalogErrors,
|
|
6
6
|
responsesFromErrors,
|
|
7
7
|
} from "./catalog-errors.js";
|
|
8
|
+
import { mergeContractMeta } from "./metadata.js";
|
|
8
9
|
import type { OpenAPIOperationMeta } from "./openapi-meta.js";
|
|
9
10
|
import { parsePathTemplate } from "./path-template.js";
|
|
10
11
|
import type {
|
|
@@ -15,6 +16,7 @@ import type {
|
|
|
15
16
|
ContractResponses,
|
|
16
17
|
HttpContractConfig,
|
|
17
18
|
HttpMethod,
|
|
19
|
+
MergeContractMeta,
|
|
18
20
|
MergedContractErrorResponses,
|
|
19
21
|
OmitMetaKeys,
|
|
20
22
|
ResponsesFromErrorDefinitions,
|
|
@@ -436,7 +438,7 @@ export class ContractBuilder<
|
|
|
436
438
|
TBody,
|
|
437
439
|
THeaders,
|
|
438
440
|
TResponses,
|
|
439
|
-
|
|
441
|
+
MergeContractMeta<TMeta, TNewMeta>,
|
|
440
442
|
TPath
|
|
441
443
|
> {
|
|
442
444
|
return new ContractBuilder({
|
|
@@ -451,18 +453,15 @@ export class ContractBuilder<
|
|
|
451
453
|
headers: this._headers,
|
|
452
454
|
body: this._body,
|
|
453
455
|
responses: this._responses,
|
|
454
|
-
metadata:
|
|
455
|
-
...this._meta,
|
|
456
|
-
...newMeta,
|
|
457
|
-
} as unknown as OmitMetaKeys<TMeta, keyof TNewMeta> & TNewMeta,
|
|
456
|
+
metadata: mergeContractMeta(this._meta, newMeta),
|
|
458
457
|
});
|
|
459
458
|
}
|
|
460
459
|
|
|
461
460
|
/**
|
|
462
461
|
* Merge OpenAPI operation metadata into this contract.
|
|
463
462
|
*/
|
|
464
|
-
openapi(
|
|
465
|
-
patch:
|
|
463
|
+
openapi<TPatch extends Partial<OpenAPIOperationMeta>>(
|
|
464
|
+
patch: TPatch,
|
|
466
465
|
): ContractBuilder<
|
|
467
466
|
TMethod,
|
|
468
467
|
TPathParams,
|
|
@@ -470,10 +469,9 @@ export class ContractBuilder<
|
|
|
470
469
|
TBody,
|
|
471
470
|
THeaders,
|
|
472
471
|
TResponses,
|
|
473
|
-
TMeta,
|
|
472
|
+
MergeContractMeta<TMeta, { openapi: TPatch }>,
|
|
474
473
|
TPath
|
|
475
474
|
> {
|
|
476
|
-
const existingOpenApi = this._meta.openapi ?? {};
|
|
477
475
|
return new ContractBuilder({
|
|
478
476
|
kind: this.kind,
|
|
479
477
|
name: this.name,
|
|
@@ -486,13 +484,7 @@ export class ContractBuilder<
|
|
|
486
484
|
headers: this._headers,
|
|
487
485
|
body: this._body,
|
|
488
486
|
responses: this._responses,
|
|
489
|
-
metadata: {
|
|
490
|
-
...this._meta,
|
|
491
|
-
openapi: {
|
|
492
|
-
...existingOpenApi,
|
|
493
|
-
...patch,
|
|
494
|
-
},
|
|
495
|
-
} as TMeta,
|
|
487
|
+
metadata: mergeContractMeta(this._meta, { openapi: patch }),
|
|
496
488
|
});
|
|
497
489
|
}
|
|
498
490
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
responsesFromErrors,
|
|
6
6
|
} from "./catalog-errors.js";
|
|
7
7
|
import { ContractBuilder } from "./contract-builder.js";
|
|
8
|
+
import { mergeContractMeta } from "./metadata.js";
|
|
8
9
|
import { parsePathTemplate } from "./path-template.js";
|
|
9
10
|
import type {
|
|
10
11
|
ContractErrorResponses,
|
|
@@ -12,6 +13,7 @@ import type {
|
|
|
12
13
|
ContractMeta,
|
|
13
14
|
ContractResponses,
|
|
14
15
|
HttpMethod,
|
|
16
|
+
MergeContractMeta,
|
|
15
17
|
MergedContractErrorResponses,
|
|
16
18
|
OmitMetaKeys,
|
|
17
19
|
ResponsesFromErrorDefinitions,
|
|
@@ -149,17 +151,13 @@ export class ContractGroup<
|
|
|
149
151
|
meta: TNewMeta,
|
|
150
152
|
): ContractGroup<
|
|
151
153
|
TSharedResponses,
|
|
152
|
-
|
|
154
|
+
MergeContractMeta<TSharedMeta, TNewMeta>,
|
|
153
155
|
TSharedHeaders,
|
|
154
156
|
TPathPrefix
|
|
155
157
|
> {
|
|
156
158
|
return new ContractGroup({
|
|
157
159
|
namespace: this._namespace,
|
|
158
|
-
meta:
|
|
159
|
-
TSharedMeta,
|
|
160
|
-
keyof TNewMeta
|
|
161
|
-
> &
|
|
162
|
-
TNewMeta,
|
|
160
|
+
meta: mergeContractMeta(this._meta, meta),
|
|
163
161
|
responses: this._responses,
|
|
164
162
|
headers: this._headers,
|
|
165
163
|
pathPrefix: this._pathPrefix,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ContractMeta, MergeContractMeta } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export function mergeContractMeta<
|
|
4
|
+
TCurrent extends ContractMeta,
|
|
5
|
+
TNext extends ContractMeta,
|
|
6
|
+
>(current: TCurrent, next: TNext): MergeContractMeta<TCurrent, TNext> {
|
|
7
|
+
const metadata: ContractMeta = {
|
|
8
|
+
...current,
|
|
9
|
+
...next,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
if (next.openapi !== undefined) {
|
|
13
|
+
metadata.openapi = {
|
|
14
|
+
...current.openapi,
|
|
15
|
+
...next.openapi,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return metadata as MergeContractMeta<TCurrent, TNext>;
|
|
20
|
+
}
|
package/src/contracts/types.ts
CHANGED
|
@@ -256,6 +256,45 @@ export type OmitMetaKeys<TMeta, K extends PropertyKey> = {
|
|
|
256
256
|
[P in keyof TMeta as P extends K ? never : P]: TMeta[P];
|
|
257
257
|
};
|
|
258
258
|
|
|
259
|
+
type DefinedOpenAPIMeta<T> = [Extract<T, OpenAPIOperationMeta>] extends [never]
|
|
260
|
+
? Record<never, never>
|
|
261
|
+
: Extract<T, OpenAPIOperationMeta>;
|
|
262
|
+
|
|
263
|
+
type OpenAPIMetaFrom<TMeta extends ContractMeta> = TMeta extends {
|
|
264
|
+
openapi: infer TOpenAPI;
|
|
265
|
+
}
|
|
266
|
+
? DefinedOpenAPIMeta<TOpenAPI>
|
|
267
|
+
: "openapi" extends keyof TMeta
|
|
268
|
+
? NonNullable<TMeta["openapi"]>
|
|
269
|
+
: Record<never, never>;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Merge contract metadata while preserving accumulated OpenAPI fields.
|
|
273
|
+
*
|
|
274
|
+
* Top-level metadata uses last-write-wins semantics. OpenAPI operation
|
|
275
|
+
* metadata is merged one field deep so `meta({ openapi: ... })` and
|
|
276
|
+
* `openapi(...)` compose consistently.
|
|
277
|
+
*/
|
|
278
|
+
export type MergeContractMeta<
|
|
279
|
+
TCurrent extends ContractMeta,
|
|
280
|
+
TNext extends ContractMeta,
|
|
281
|
+
> = ContractMeta &
|
|
282
|
+
OmitMetaKeys<TCurrent, keyof TNext> &
|
|
283
|
+
OmitMetaKeys<TNext, "openapi"> &
|
|
284
|
+
("openapi" extends keyof TNext
|
|
285
|
+
? TNext extends { openapi: infer TNextOpenAPI }
|
|
286
|
+
? TNextOpenAPI extends OpenAPIOperationMeta
|
|
287
|
+
? {
|
|
288
|
+
openapi: Omit<
|
|
289
|
+
OpenAPIMetaFrom<TCurrent>,
|
|
290
|
+
keyof OpenAPIMetaFrom<TNext>
|
|
291
|
+
> &
|
|
292
|
+
OpenAPIMetaFrom<TNext>;
|
|
293
|
+
}
|
|
294
|
+
: { openapi: TNextOpenAPI }
|
|
295
|
+
: { openapi?: OpenAPIOperationMeta }
|
|
296
|
+
: Record<never, never>);
|
|
297
|
+
|
|
259
298
|
/**
|
|
260
299
|
* Plain HTTP contract configuration consumed by server, client, and tooling.
|
|
261
300
|
*/
|
package/src/events/index.ts
CHANGED
|
@@ -119,15 +119,11 @@ export interface ListenerDef<
|
|
|
119
119
|
/**
|
|
120
120
|
* Options for `defineListener(...)`.
|
|
121
121
|
*/
|
|
122
|
-
export interface DefineListenerOptions<
|
|
123
|
-
E extends EventDef,
|
|
124
|
-
Ctx,
|
|
125
|
-
Name extends string,
|
|
126
|
-
> {
|
|
122
|
+
export interface DefineListenerOptions<E extends EventDef, Ctx> {
|
|
127
123
|
/**
|
|
128
|
-
*
|
|
124
|
+
* Event this listener handles.
|
|
129
125
|
*/
|
|
130
|
-
|
|
126
|
+
event: E;
|
|
131
127
|
/**
|
|
132
128
|
* Handle a parsed event payload.
|
|
133
129
|
*/
|
|
@@ -181,9 +177,9 @@ export interface Listeners<Ctx> {
|
|
|
181
177
|
/**
|
|
182
178
|
* Define a listener with the bound context type.
|
|
183
179
|
*/
|
|
184
|
-
defineListener<
|
|
185
|
-
|
|
186
|
-
options: DefineListenerOptions<E, Ctx
|
|
180
|
+
defineListener<Name extends string, E extends EventDef>(
|
|
181
|
+
name: Name,
|
|
182
|
+
options: DefineListenerOptions<E, Ctx>,
|
|
187
183
|
): ListenerDef<E, Ctx, Name>;
|
|
188
184
|
}
|
|
189
185
|
|
|
@@ -269,17 +265,17 @@ export function defineEvent<
|
|
|
269
265
|
}
|
|
270
266
|
|
|
271
267
|
function defineListenerImpl<
|
|
272
|
-
E extends EventDef,
|
|
273
|
-
Ctx = unknown,
|
|
274
268
|
Name extends string = string,
|
|
269
|
+
E extends EventDef = EventDef,
|
|
270
|
+
Ctx = unknown,
|
|
275
271
|
>(
|
|
276
|
-
|
|
277
|
-
options: DefineListenerOptions<E, Ctx
|
|
272
|
+
name: Name,
|
|
273
|
+
options: DefineListenerOptions<E, Ctx>,
|
|
278
274
|
): ListenerDef<E, Ctx, Name> {
|
|
279
275
|
return {
|
|
280
276
|
kind: "listener",
|
|
281
|
-
name
|
|
282
|
-
event,
|
|
277
|
+
name,
|
|
278
|
+
event: options.event,
|
|
283
279
|
handle: options.handle,
|
|
284
280
|
};
|
|
285
281
|
}
|
|
@@ -369,11 +365,11 @@ export function registerListeners<Ctx>(
|
|
|
369
365
|
*/
|
|
370
366
|
export function createListeners<Ctx>(): Listeners<Ctx> {
|
|
371
367
|
return {
|
|
372
|
-
defineListener<
|
|
373
|
-
|
|
374
|
-
options: DefineListenerOptions<E, Ctx
|
|
368
|
+
defineListener<Name extends string, E extends EventDef>(
|
|
369
|
+
name: Name,
|
|
370
|
+
options: DefineListenerOptions<E, Ctx>,
|
|
375
371
|
): ListenerDef<E, Ctx, Name> {
|
|
376
|
-
return defineListenerImpl(
|
|
372
|
+
return defineListenerImpl(name, options);
|
|
377
373
|
},
|
|
378
374
|
};
|
|
379
375
|
}
|
package/src/idempotency/index.ts
CHANGED
|
@@ -644,16 +644,9 @@ export async function runIdempotently<Result>(
|
|
|
644
644
|
throw new IdempotencyConflictError(reservation);
|
|
645
645
|
}
|
|
646
646
|
case "reserved": {
|
|
647
|
+
let result: Result;
|
|
647
648
|
try {
|
|
648
|
-
|
|
649
|
-
await idempotency.complete({
|
|
650
|
-
namespace: operation.namespace,
|
|
651
|
-
key: operation.key,
|
|
652
|
-
scope: operation.scope,
|
|
653
|
-
fingerprint: operation.fingerprint,
|
|
654
|
-
result,
|
|
655
|
-
});
|
|
656
|
-
return result;
|
|
649
|
+
result = await options.run();
|
|
657
650
|
} catch (error) {
|
|
658
651
|
await idempotency.fail({
|
|
659
652
|
namespace: operation.namespace,
|
|
@@ -664,6 +657,21 @@ export async function runIdempotently<Result>(
|
|
|
664
657
|
});
|
|
665
658
|
throw error;
|
|
666
659
|
}
|
|
660
|
+
|
|
661
|
+
await idempotency.complete({
|
|
662
|
+
namespace: operation.namespace,
|
|
663
|
+
key: operation.key,
|
|
664
|
+
scope: operation.scope,
|
|
665
|
+
fingerprint: operation.fingerprint,
|
|
666
|
+
result,
|
|
667
|
+
});
|
|
668
|
+
return result;
|
|
669
|
+
}
|
|
670
|
+
default: {
|
|
671
|
+
const unsupported = reservation as { status?: unknown };
|
|
672
|
+
throw new Error(
|
|
673
|
+
`Idempotency port returned unsupported reservation status "${String(unsupported.status)}".`,
|
|
674
|
+
);
|
|
667
675
|
}
|
|
668
676
|
}
|
|
669
677
|
}
|
package/src/jobs/index.ts
CHANGED
|
@@ -1452,10 +1452,15 @@ export function createInlineJobDispatcher<Ctx>(
|
|
|
1452
1452
|
payload: InferJobPayload<J>,
|
|
1453
1453
|
retryEnabled: boolean,
|
|
1454
1454
|
dispatchOptions: SingleAttemptJobDispatchOptions = {},
|
|
1455
|
+
propagateFailure = false,
|
|
1455
1456
|
): Promise<void> {
|
|
1456
1457
|
const fail = (error: unknown): void => {
|
|
1457
|
-
|
|
1458
|
-
|
|
1458
|
+
try {
|
|
1459
|
+
options.onError?.(error, job);
|
|
1460
|
+
} catch (observerError) {
|
|
1461
|
+
if (!propagateFailure) throw observerError;
|
|
1462
|
+
}
|
|
1463
|
+
if (propagateFailure || !options.onError) throw error;
|
|
1459
1464
|
};
|
|
1460
1465
|
|
|
1461
1466
|
let parsed: InferJobPayload<J>;
|
|
@@ -1522,7 +1527,7 @@ export function createInlineJobDispatcher<Ctx>(
|
|
|
1522
1527
|
job: J,
|
|
1523
1528
|
payload: InferJobPayload<J>,
|
|
1524
1529
|
dispatchOptions?: SingleAttemptJobDispatchOptions,
|
|
1525
|
-
) => run(job, payload, false, dispatchOptions),
|
|
1530
|
+
) => run(job, payload, false, dispatchOptions, true),
|
|
1526
1531
|
enumerable: false,
|
|
1527
1532
|
});
|
|
1528
1533
|
|
package/src/openapi/index.ts
CHANGED
|
@@ -553,10 +553,13 @@ function addPathParams(
|
|
|
553
553
|
|
|
554
554
|
const shape = state.introspector.getShape(contract.pathParams);
|
|
555
555
|
if (!shape) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
556
|
+
for (const key of pathKeys) {
|
|
557
|
+
addParameter(operation, {
|
|
558
|
+
name: key,
|
|
559
|
+
in: "path",
|
|
560
|
+
required: true,
|
|
561
|
+
schema: { type: "string" },
|
|
562
|
+
});
|
|
560
563
|
}
|
|
561
564
|
return;
|
|
562
565
|
}
|
|
@@ -612,14 +615,8 @@ function addQueryParams(
|
|
|
612
615
|
const originalField = shape[key];
|
|
613
616
|
const optional = state.introspector.isOptional(originalField);
|
|
614
617
|
|
|
615
|
-
let description = state.introspector.getDescription(originalField);
|
|
616
|
-
|
|
617
618
|
const field = originalField;
|
|
618
|
-
|
|
619
|
-
// If outer optional didn't have description, check inner type
|
|
620
|
-
if (!description && optional) {
|
|
621
|
-
description = state.introspector.getDescription(field);
|
|
622
|
-
}
|
|
619
|
+
const description = parameterDescription(field, optional, state);
|
|
623
620
|
|
|
624
621
|
const paramSchemaRef = schemaToConvertedSchemaRef(
|
|
625
622
|
field,
|
|
@@ -656,7 +653,7 @@ function addHeaderParams(
|
|
|
656
653
|
for (const key of Object.keys(shape)) {
|
|
657
654
|
const originalField = shape[key];
|
|
658
655
|
const optional = state.introspector.isOptional(originalField);
|
|
659
|
-
const description =
|
|
656
|
+
const description = parameterDescription(originalField, optional, state);
|
|
660
657
|
|
|
661
658
|
const paramSchemaRef = schemaToConvertedSchemaRef(
|
|
662
659
|
originalField,
|
|
@@ -676,6 +673,21 @@ function addHeaderParams(
|
|
|
676
673
|
}
|
|
677
674
|
}
|
|
678
675
|
|
|
676
|
+
function parameterDescription(
|
|
677
|
+
field: unknown,
|
|
678
|
+
optional: boolean,
|
|
679
|
+
state: GeneratorState,
|
|
680
|
+
): string | undefined {
|
|
681
|
+
return (
|
|
682
|
+
state.introspector.getDescription(field) ??
|
|
683
|
+
(optional
|
|
684
|
+
? state.introspector.getDescription(
|
|
685
|
+
state.introspector.unwrapOptional(field),
|
|
686
|
+
)
|
|
687
|
+
: undefined)
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
|
|
679
691
|
/**
|
|
680
692
|
* Add request body from contract to operation
|
|
681
693
|
*/
|
package/src/ports/auth.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface AuthSession<User = unknown, Session = unknown> {
|
|
|
36
36
|
*/
|
|
37
37
|
export class AuthUnauthorizedError extends Error {
|
|
38
38
|
readonly code = "UNAUTHORIZED";
|
|
39
|
+
/** HTTP status used when the server maps this framework error. */
|
|
40
|
+
readonly status = 401;
|
|
39
41
|
|
|
40
42
|
constructor(message = "Unauthorized") {
|
|
41
43
|
super(message);
|
package/src/providers/index.ts
CHANGED
|
@@ -240,6 +240,22 @@ export interface ServiceProvider<
|
|
|
240
240
|
readonly __providedPorts?: ProvidedPorts;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
/**
|
|
244
|
+
* A provider configuration schema whose concrete validation library and input
|
|
245
|
+
* shape are intentionally erased while its validated output may stay typed.
|
|
246
|
+
*
|
|
247
|
+
* Reusable provider packages use this in their named provider return types so
|
|
248
|
+
* internal Zod schemas do not become part of the package's public API.
|
|
249
|
+
*/
|
|
250
|
+
export type AnyProviderConfigSchema<
|
|
251
|
+
// biome-ignore lint/suspicious/noExplicitAny: arbitrary provider consumers may erase config output
|
|
252
|
+
Output = any,
|
|
253
|
+
> = StandardSchemaV1<
|
|
254
|
+
// biome-ignore lint/suspicious/noExplicitAny: reusable provider return types intentionally erase config input
|
|
255
|
+
any,
|
|
256
|
+
Output
|
|
257
|
+
>;
|
|
258
|
+
|
|
243
259
|
/**
|
|
244
260
|
* Loosely typed service provider.
|
|
245
261
|
*
|
|
@@ -250,8 +266,7 @@ export interface ServiceProvider<
|
|
|
250
266
|
*/
|
|
251
267
|
export type AnyServiceProvider = ServiceProvider<
|
|
252
268
|
unknown,
|
|
253
|
-
|
|
254
|
-
StandardSchemaV1<any, any>,
|
|
269
|
+
AnyProviderConfigSchema,
|
|
255
270
|
ProviderPorts,
|
|
256
271
|
// biome-ignore lint/suspicious/noExplicitAny: provider context types are erased at this level
|
|
257
272
|
any,
|