@voyant-travel/apps 0.7.0 → 0.8.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/api-runtime.d.ts +23 -11
- package/dist/api-runtime.js +38 -1
- package/dist/api-runtime.test.d.ts +1 -0
- package/dist/api-runtime.test.js +54 -0
- package/dist/app-api-contracts.d.ts +58 -8
- package/dist/app-api-contracts.js +102 -9
- package/dist/app-api-finance-routes.test.js +482 -2
- package/dist/app-api-routes.d.ts +6 -5
- package/dist/app-api-routes.js +131 -22
- package/dist/app-api-service.d.ts +31 -1
- package/dist/app-api-service.js +162 -0
- package/dist/consent.d.ts +1 -0
- package/dist/consent.js +2 -2
- package/dist/contracts.d.ts +1 -25
- package/dist/contracts.js +3 -10
- package/dist/oauth-service.d.ts +6 -1
- package/dist/oauth-service.js +41 -4
- package/dist/oauth-service.test.js +39 -2
- package/dist/routes-openapi.d.ts +1 -25
- package/dist/routes-viewer-scopes.test.d.ts +1 -0
- package/dist/routes-viewer-scopes.test.js +46 -0
- package/dist/routes.d.ts +5 -5
- package/dist/routes.js +41 -24
- package/dist/runtime-contributor.d.ts +9 -1
- package/dist/runtime-contributor.js +25 -2
- package/dist/runtime-contributor.test.d.ts +1 -0
- package/dist/runtime-contributor.test.js +52 -0
- package/dist/runtime-port.d.ts +8 -0
- package/dist/runtime-port.js +22 -0
- package/dist/session-token-service.d.ts +2 -0
- package/dist/session-token-service.js +44 -27
- package/dist/session-token-service.test.d.ts +1 -0
- package/dist/session-token-service.test.js +157 -0
- package/dist/session-token.d.ts +5 -1
- package/dist/session-token.js +0 -0
- package/dist/session-token.test.js +5 -0
- package/dist/voyant.js +95 -1
- package/package.json +8 -3
package/dist/api-runtime.d.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
export declare const createAppsApiModule: import("@voyant-travel/core/project").VoyantGraphRuntimeFactory<{
|
|
2
|
-
module: {
|
|
3
|
-
name: string;
|
|
4
|
-
};
|
|
5
|
-
adminRoutes: import("@hono/zod-openapi").OpenAPIHono<{
|
|
6
|
-
Bindings: {
|
|
7
|
-
VOYANT_CLOUD_DEPLOYMENT_ID?: string;
|
|
8
|
-
};
|
|
9
|
-
Variables: {
|
|
10
|
-
db: import("drizzle-orm/postgres-js").PostgresJsDatabase;
|
|
11
|
-
};
|
|
12
|
-
}, {}, "/">;
|
|
13
2
|
lazyRoutes: {
|
|
14
3
|
paths: string[];
|
|
15
4
|
load: () => Promise<import("hono").Hono<{
|
|
5
|
+
Bindings: {
|
|
6
|
+
API_BASE_URL?: string;
|
|
7
|
+
};
|
|
16
8
|
Variables: {
|
|
17
9
|
db: import("drizzle-orm/postgres-js").PostgresJsDatabase;
|
|
18
10
|
appId?: string;
|
|
@@ -20,9 +12,29 @@ export declare const createAppsApiModule: import("@voyant-travel/core/project").
|
|
|
20
12
|
appReleaseId?: string;
|
|
21
13
|
appTokenMode?: "offline" | "online";
|
|
22
14
|
appViewerId?: string;
|
|
15
|
+
appContextConstraint?: import("@voyant-travel/core").VoyantAppContextConstraint;
|
|
23
16
|
callerType?: string;
|
|
24
17
|
scopes?: string[];
|
|
25
18
|
};
|
|
26
19
|
}, import("hono/types").BlankSchema, "/">>;
|
|
27
20
|
};
|
|
21
|
+
clientAuthenticated?: readonly [{
|
|
22
|
+
readonly method: "POST";
|
|
23
|
+
readonly path: "/oauth/token";
|
|
24
|
+
}, {
|
|
25
|
+
readonly method: "POST";
|
|
26
|
+
readonly path: "/oauth/session-token/exchange";
|
|
27
|
+
}] | undefined;
|
|
28
|
+
authAugmentation?: {
|
|
29
|
+
resolveAppToken: ({ db, token }: import("@voyant-travel/hono").VoyantAuthAppTokenResolveArgs<import("@voyant-travel/hono").VoyantBindings>) => Promise<import("@voyant-travel/core").VoyantAuthContext | null>;
|
|
30
|
+
} | undefined;
|
|
31
|
+
module: {
|
|
32
|
+
name: string;
|
|
33
|
+
};
|
|
34
|
+
adminRoutes: import("@hono/zod-openapi").OpenAPIHono<{
|
|
35
|
+
Variables: {
|
|
36
|
+
db: import("drizzle-orm/postgres-js").PostgresJsDatabase;
|
|
37
|
+
scopes?: string[];
|
|
38
|
+
};
|
|
39
|
+
}, {}, "/">;
|
|
28
40
|
}>;
|
package/dist/api-runtime.js
CHANGED
|
@@ -3,7 +3,9 @@ import { customFieldValueLifecycleRuntimePort, customFieldValueOperationsRuntime
|
|
|
3
3
|
import { createCustomFieldTargetRegistry } from "@voyant-travel/custom-fields";
|
|
4
4
|
import { financeAppApiRuntimePort } from "@voyant-travel/finance-contracts/app-api";
|
|
5
5
|
import { createAppsAppApiRoutes } from "./app-api-routes.js";
|
|
6
|
+
import { createAppOAuthService } from "./oauth-service.js";
|
|
6
7
|
import { createAppsAdminRoutes } from "./routes.js";
|
|
8
|
+
import { appsManagedAuthRuntimePort } from "./runtime-port.js";
|
|
7
9
|
export const createAppsApiModule = defineGraphRuntimeFactory(async ({ getPort, getPorts, graph, hasPort }) => {
|
|
8
10
|
const customFieldTargets = createCustomFieldTargetRegistry(graph.customFieldTargets ?? []);
|
|
9
11
|
const customFieldValueLifecycles = await getPorts(customFieldValueLifecycleRuntimePort);
|
|
@@ -11,9 +13,44 @@ export const createAppsApiModule = defineGraphRuntimeFactory(async ({ getPort, g
|
|
|
11
13
|
const finance = hasPort(financeAppApiRuntimePort)
|
|
12
14
|
? await getPort(financeAppApiRuntimePort)
|
|
13
15
|
: undefined;
|
|
16
|
+
const managedAuth = hasPort(appsManagedAuthRuntimePort)
|
|
17
|
+
? await getPort(appsManagedAuthRuntimePort)
|
|
18
|
+
: undefined;
|
|
19
|
+
const oauthOptions = managedAuth
|
|
20
|
+
? {
|
|
21
|
+
accessCatalog: graph.accessCatalog,
|
|
22
|
+
deploymentId: managedAuth.runtimeAudience,
|
|
23
|
+
clientAuthentication: "required",
|
|
24
|
+
}
|
|
25
|
+
: undefined;
|
|
26
|
+
const oauth = oauthOptions ? createAppOAuthService(oauthOptions) : undefined;
|
|
14
27
|
return {
|
|
15
28
|
module: { name: "apps" },
|
|
16
|
-
adminRoutes: createAppsAdminRoutes({
|
|
29
|
+
adminRoutes: createAppsAdminRoutes({
|
|
30
|
+
eventCatalog: graph.eventCatalog,
|
|
31
|
+
...(oauthOptions ? { oauth: oauthOptions } : {}),
|
|
32
|
+
...(managedAuth
|
|
33
|
+
? {
|
|
34
|
+
sessionToken: {
|
|
35
|
+
secret: managedAuth.sessionTokenSigningSecret,
|
|
36
|
+
...(managedAuth.sessionTokenTtlSeconds === undefined
|
|
37
|
+
? {}
|
|
38
|
+
: { ttlSeconds: managedAuth.sessionTokenTtlSeconds }),
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
: {}),
|
|
42
|
+
}),
|
|
43
|
+
...(oauth
|
|
44
|
+
? {
|
|
45
|
+
clientAuthenticated: [
|
|
46
|
+
{ method: "POST", path: "/oauth/token" },
|
|
47
|
+
{ method: "POST", path: "/oauth/session-token/exchange" },
|
|
48
|
+
],
|
|
49
|
+
authAugmentation: {
|
|
50
|
+
resolveAppToken: ({ db, token }) => oauth.resolveAccessToken(db, token),
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
: {}),
|
|
17
54
|
lazyRoutes: {
|
|
18
55
|
paths: ["/v1/app", "/v1/app/*"],
|
|
19
56
|
load: async () => createAppsAppApiRoutes({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createAppsApiModule } from "./api-runtime.js";
|
|
3
|
+
import { appsManagedAuthRuntimePort } from "./runtime-port.js";
|
|
4
|
+
const graph = {
|
|
5
|
+
providerSelections: {},
|
|
6
|
+
customFieldTargets: [],
|
|
7
|
+
accessCatalog: { resources: [], presets: [] },
|
|
8
|
+
eventCatalog: { schemaVersion: "voyant.event-catalog.v1", events: [] },
|
|
9
|
+
tools: [],
|
|
10
|
+
references: [],
|
|
11
|
+
setupSteps: [],
|
|
12
|
+
};
|
|
13
|
+
function context(managedAuth) {
|
|
14
|
+
return {
|
|
15
|
+
unitId: "@voyant-travel/apps",
|
|
16
|
+
projectConfig: {},
|
|
17
|
+
getUnitProjectConfig: () => undefined,
|
|
18
|
+
api: [],
|
|
19
|
+
graph,
|
|
20
|
+
runtimePorts: managedAuth ? { [appsManagedAuthRuntimePort.id]: managedAuth } : {},
|
|
21
|
+
hasPort: (port) => port.id === appsManagedAuthRuntimePort.id && managedAuth !== undefined,
|
|
22
|
+
getPort: async (port) => {
|
|
23
|
+
if (port.id === appsManagedAuthRuntimePort.id && managedAuth)
|
|
24
|
+
return managedAuth;
|
|
25
|
+
throw new Error(`missing ${port.id}`);
|
|
26
|
+
},
|
|
27
|
+
getPorts: async () => [],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
describe("createAppsApiModule", () => {
|
|
31
|
+
it("keeps managed auth and client-authenticated routes off without host inputs", async () => {
|
|
32
|
+
const module = await createAppsApiModule(context());
|
|
33
|
+
expect(module.clientAuthenticated).toBeUndefined();
|
|
34
|
+
expect(module.authAugmentation).toBeUndefined();
|
|
35
|
+
});
|
|
36
|
+
it("composes OAuth, session exchange, and token resolution from the host port", async () => {
|
|
37
|
+
const module = await createAppsApiModule(context({
|
|
38
|
+
runtimeAudience: "deployment-1",
|
|
39
|
+
sessionTokenSigningSecret: "s".repeat(32),
|
|
40
|
+
sessionTokenTtlSeconds: 180,
|
|
41
|
+
}));
|
|
42
|
+
expect(module.clientAuthenticated).toEqual([
|
|
43
|
+
{ method: "POST", path: "/oauth/token" },
|
|
44
|
+
{ method: "POST", path: "/oauth/session-token/exchange" },
|
|
45
|
+
]);
|
|
46
|
+
expect(module.authAugmentation?.resolveAppToken).toBeTypeOf("function");
|
|
47
|
+
await expect(module.authAugmentation?.resolveAppToken({
|
|
48
|
+
request: new Request("http://test/v1/app"),
|
|
49
|
+
env: { DATABASE_URL: "postgres://test" },
|
|
50
|
+
db: {},
|
|
51
|
+
token: "test-token",
|
|
52
|
+
})).resolves.toBeNull();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { customFieldDefinitionInputSchema, customFieldDefinitionListQuerySchema, customFieldValueListQuerySchema, updateCustomFieldDefinitionSchema, upsertCustomFieldValueSchema } from "@voyant-travel/custom-fields";
|
|
2
|
-
import type { FinanceAppApiExternalReference, FinanceAppApiIssuanceDocument } from "@voyant-travel/finance-contracts/app-api";
|
|
2
|
+
import type { FinanceAppApiExternalLifecycleObservation, FinanceAppApiExternalReference, FinanceAppApiExternalSyncState, FinanceAppApiIssuanceDocument, FinanceAppApiPdfArtifact, FinanceAppApiSettlementObservation } from "@voyant-travel/finance-contracts/app-api";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
export declare const APP_API_VERSION = "2026-07-01";
|
|
5
5
|
export declare const appApiVersionHeader = "voyant-app-api-version";
|
|
@@ -48,6 +48,53 @@ export declare const appApiFinanceExternalReferenceUpsertSchema: z.ZodObject<{
|
|
|
48
48
|
invoiceNumber: z.ZodString;
|
|
49
49
|
}, z.core.$strict>>;
|
|
50
50
|
}, z.core.$strict>;
|
|
51
|
+
export declare const appApiFinancePdfArtifactHeadersSchema: z.ZodObject<{
|
|
52
|
+
idempotencyKey: z.ZodString;
|
|
53
|
+
fileName: z.ZodString;
|
|
54
|
+
}, z.core.$strict>;
|
|
55
|
+
export declare const appApiFinanceExternalSyncStateSchema: z.ZodObject<{
|
|
56
|
+
operationId: z.ZodString;
|
|
57
|
+
status: z.ZodEnum<{
|
|
58
|
+
succeeded: "succeeded";
|
|
59
|
+
retryable_failure: "retryable_failure";
|
|
60
|
+
terminal_failure: "terminal_failure";
|
|
61
|
+
}>;
|
|
62
|
+
occurredAt: z.ZodString;
|
|
63
|
+
error: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
64
|
+
code: z.ZodString;
|
|
65
|
+
message: z.ZodString;
|
|
66
|
+
}, z.core.$strict>>>;
|
|
67
|
+
metadata: z.ZodDefault<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
68
|
+
}, z.core.$strict>;
|
|
69
|
+
export declare const appApiFinanceExternalLifecycleStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
70
|
+
operationId: z.ZodString;
|
|
71
|
+
state: z.ZodLiteral<"converted">;
|
|
72
|
+
occurredAt: z.ZodString;
|
|
73
|
+
lineage: z.ZodObject<{
|
|
74
|
+
sourceDocumentId: z.ZodString;
|
|
75
|
+
successorDocumentId: z.ZodString;
|
|
76
|
+
}, z.core.$strict>;
|
|
77
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
78
|
+
operationId: z.ZodString;
|
|
79
|
+
state: z.ZodLiteral<"voided">;
|
|
80
|
+
occurredAt: z.ZodString;
|
|
81
|
+
lineage: z.ZodDefault<z.ZodNull>;
|
|
82
|
+
}, z.core.$strict>], "state">;
|
|
83
|
+
export declare const appApiFinanceSettlementObservationSchema: z.ZodObject<{
|
|
84
|
+
operationId: z.ZodString;
|
|
85
|
+
occurredAt: z.ZodString;
|
|
86
|
+
status: z.ZodEnum<{
|
|
87
|
+
partial: "partial";
|
|
88
|
+
paid: "paid";
|
|
89
|
+
}>;
|
|
90
|
+
currency: z.ZodString;
|
|
91
|
+
totals: z.ZodObject<{
|
|
92
|
+
totalCents: z.ZodNumber;
|
|
93
|
+
paidCents: z.ZodNumber;
|
|
94
|
+
balanceDueCents: z.ZodNumber;
|
|
95
|
+
}, z.core.$strict>;
|
|
96
|
+
paymentIdentifiers: z.ZodArray<z.ZodString>;
|
|
97
|
+
}, z.core.$strict>;
|
|
51
98
|
export declare const appApiWebhookReplaySchema: z.ZodObject<{
|
|
52
99
|
deliveryId: z.ZodString;
|
|
53
100
|
signingKeyId: z.ZodString;
|
|
@@ -57,13 +104,6 @@ export declare const appApiAuditQuerySchema: z.ZodObject<{
|
|
|
57
104
|
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
58
105
|
offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
59
106
|
}, z.core.$strip>;
|
|
60
|
-
export declare const appApiTokenExchangeSchema: z.ZodObject<{
|
|
61
|
-
client_id: z.ZodString;
|
|
62
|
-
client_secret: z.ZodOptional<z.ZodString>;
|
|
63
|
-
viewer_id: z.ZodString;
|
|
64
|
-
viewer_scopes: z.ZodArray<z.ZodString>;
|
|
65
|
-
contextual_scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
66
|
-
}, z.core.$strict>;
|
|
67
107
|
export declare const appApiDefinitionNamespaceSchema: z.ZodObject<{
|
|
68
108
|
namespace: z.ZodOptional<z.ZodString>;
|
|
69
109
|
}, z.core.$strict>;
|
|
@@ -72,7 +112,17 @@ export type AppApiEntityReadQuery = z.infer<typeof appApiEntityReadQuerySchema>;
|
|
|
72
112
|
export type AppApiFinanceDocumentQuery = z.infer<typeof appApiFinanceDocumentQuerySchema>;
|
|
73
113
|
export type AppApiFinanceActionInput = z.infer<typeof appApiFinanceActionSchema>;
|
|
74
114
|
export type AppApiFinanceExternalReferenceUpsertInput = z.infer<typeof appApiFinanceExternalReferenceUpsertSchema>;
|
|
115
|
+
export type AppApiFinancePdfArtifactHeaders = z.infer<typeof appApiFinancePdfArtifactHeadersSchema>;
|
|
116
|
+
export type AppApiFinanceExternalSyncStateInput = z.infer<typeof appApiFinanceExternalSyncStateSchema>;
|
|
117
|
+
export type AppApiFinanceExternalLifecycleStateInput = z.infer<typeof appApiFinanceExternalLifecycleStateSchema>;
|
|
118
|
+
export type AppApiFinanceSettlementObservationInput = z.infer<typeof appApiFinanceSettlementObservationSchema>;
|
|
75
119
|
export type AppApiWebhookReplayInput = z.infer<typeof appApiWebhookReplaySchema>;
|
|
76
120
|
export type AppApiAuditQuery = z.infer<typeof appApiAuditQuerySchema>;
|
|
77
121
|
export type AppApiFinanceIssuanceDocument = FinanceAppApiIssuanceDocument;
|
|
78
122
|
export type AppApiFinanceExternalReference = FinanceAppApiExternalReference;
|
|
123
|
+
export type AppApiFinancePdfArtifact = FinanceAppApiPdfArtifact & {
|
|
124
|
+
documentUrl: string;
|
|
125
|
+
};
|
|
126
|
+
export type AppApiFinanceExternalSyncState = FinanceAppApiExternalSyncState;
|
|
127
|
+
export type AppApiFinanceExternalLifecycleObservation = FinanceAppApiExternalLifecycleObservation;
|
|
128
|
+
export type AppApiFinanceSettlementObservation = FinanceAppApiSettlementObservation;
|
|
@@ -50,6 +50,108 @@ export const appApiFinanceExternalReferenceUpsertSchema = z
|
|
|
50
50
|
.optional(),
|
|
51
51
|
})
|
|
52
52
|
.strict();
|
|
53
|
+
export const appApiFinancePdfArtifactHeadersSchema = z
|
|
54
|
+
.object({
|
|
55
|
+
idempotencyKey: z.string().trim().min(1).max(200),
|
|
56
|
+
fileName: z
|
|
57
|
+
.string()
|
|
58
|
+
.trim()
|
|
59
|
+
.min(1)
|
|
60
|
+
.max(200)
|
|
61
|
+
.refine((value) => [...value].every((character) => {
|
|
62
|
+
const code = character.charCodeAt(0);
|
|
63
|
+
return code >= 32 && code !== 127 && character !== "/" && character !== "\\";
|
|
64
|
+
}), "Invalid artifact file name."),
|
|
65
|
+
})
|
|
66
|
+
.strict();
|
|
67
|
+
const appApiFinanceExternalSyncErrorSchema = z
|
|
68
|
+
.object({
|
|
69
|
+
code: z.string().trim().min(1).max(100),
|
|
70
|
+
message: z.string().trim().min(1).max(2_000),
|
|
71
|
+
})
|
|
72
|
+
.strict();
|
|
73
|
+
export const appApiFinanceExternalSyncStateSchema = z
|
|
74
|
+
.object({
|
|
75
|
+
operationId: z.string().trim().min(1).max(200),
|
|
76
|
+
status: z.enum(["succeeded", "retryable_failure", "terminal_failure"]),
|
|
77
|
+
occurredAt: z.string().datetime(),
|
|
78
|
+
error: appApiFinanceExternalSyncErrorSchema.nullable().default(null),
|
|
79
|
+
metadata: z
|
|
80
|
+
.record(z.string().trim().min(1).max(100), z.unknown())
|
|
81
|
+
.refine((value) => JSON.stringify(value).length <= 16_384, "Metadata is too large.")
|
|
82
|
+
.nullable()
|
|
83
|
+
.default(null),
|
|
84
|
+
})
|
|
85
|
+
.strict()
|
|
86
|
+
.superRefine((value, context) => {
|
|
87
|
+
if (value.status === "succeeded" && value.error) {
|
|
88
|
+
context.addIssue({
|
|
89
|
+
code: "custom",
|
|
90
|
+
message: "Successful sync state cannot include an error.",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (value.status !== "succeeded" && !value.error) {
|
|
94
|
+
context.addIssue({ code: "custom", message: "Failed sync state requires an error." });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const appApiFinanceDocumentLineageSchema = z
|
|
98
|
+
.object({
|
|
99
|
+
sourceDocumentId: z.string().trim().min(1).max(200),
|
|
100
|
+
successorDocumentId: z.string().trim().min(1).max(200),
|
|
101
|
+
})
|
|
102
|
+
.strict();
|
|
103
|
+
export const appApiFinanceExternalLifecycleStateSchema = z.discriminatedUnion("state", [
|
|
104
|
+
z
|
|
105
|
+
.object({
|
|
106
|
+
operationId: z.string().trim().min(1).max(200),
|
|
107
|
+
state: z.literal("converted"),
|
|
108
|
+
occurredAt: z.string().datetime(),
|
|
109
|
+
lineage: appApiFinanceDocumentLineageSchema,
|
|
110
|
+
})
|
|
111
|
+
.strict(),
|
|
112
|
+
z
|
|
113
|
+
.object({
|
|
114
|
+
operationId: z.string().trim().min(1).max(200),
|
|
115
|
+
state: z.literal("voided"),
|
|
116
|
+
occurredAt: z.string().datetime(),
|
|
117
|
+
lineage: z.null().default(null),
|
|
118
|
+
})
|
|
119
|
+
.strict(),
|
|
120
|
+
]);
|
|
121
|
+
export const appApiFinanceSettlementObservationSchema = z
|
|
122
|
+
.object({
|
|
123
|
+
operationId: z.string().trim().min(1).max(200),
|
|
124
|
+
occurredAt: z.string().datetime(),
|
|
125
|
+
status: z.enum(["partial", "paid"]),
|
|
126
|
+
currency: z
|
|
127
|
+
.string()
|
|
128
|
+
.trim()
|
|
129
|
+
.regex(/^[A-Z]{3}$/),
|
|
130
|
+
totals: z
|
|
131
|
+
.object({
|
|
132
|
+
totalCents: z.number().int().nonnegative(),
|
|
133
|
+
paidCents: z.number().int().positive(),
|
|
134
|
+
balanceDueCents: z.number().int().nonnegative(),
|
|
135
|
+
})
|
|
136
|
+
.strict(),
|
|
137
|
+
paymentIdentifiers: z
|
|
138
|
+
.array(z.string().trim().min(1).max(200))
|
|
139
|
+
.min(1)
|
|
140
|
+
.max(100)
|
|
141
|
+
.refine((value) => new Set(value).size === value.length, "Payment identifiers must be unique."),
|
|
142
|
+
})
|
|
143
|
+
.strict()
|
|
144
|
+
.superRefine((value, context) => {
|
|
145
|
+
if (value.totals.paidCents + value.totals.balanceDueCents !== value.totals.totalCents) {
|
|
146
|
+
context.addIssue({ code: "custom", message: "Settlement totals must balance." });
|
|
147
|
+
}
|
|
148
|
+
if (value.status === "partial" && value.totals.balanceDueCents === 0) {
|
|
149
|
+
context.addIssue({ code: "custom", message: "Partial settlement requires a balance." });
|
|
150
|
+
}
|
|
151
|
+
if (value.status === "paid" && value.totals.balanceDueCents !== 0) {
|
|
152
|
+
context.addIssue({ code: "custom", message: "Paid settlement cannot have a balance." });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
53
155
|
export const appApiWebhookReplaySchema = z
|
|
54
156
|
.object({
|
|
55
157
|
deliveryId: z.string().min(1),
|
|
@@ -61,15 +163,6 @@ export const appApiAuditQuerySchema = z.object({
|
|
|
61
163
|
limit: z.coerce.number().int().min(1).max(100).default(25),
|
|
62
164
|
offset: z.coerce.number().int().min(0).default(0),
|
|
63
165
|
});
|
|
64
|
-
export const appApiTokenExchangeSchema = z
|
|
65
|
-
.object({
|
|
66
|
-
client_id: z.string().min(1),
|
|
67
|
-
client_secret: z.string().optional(),
|
|
68
|
-
viewer_id: z.string().min(1),
|
|
69
|
-
viewer_scopes: z.array(z.string().min(1)),
|
|
70
|
-
contextual_scopes: z.array(z.string().min(1)).optional(),
|
|
71
|
-
})
|
|
72
|
-
.strict();
|
|
73
166
|
export const appApiDefinitionNamespaceSchema = z
|
|
74
167
|
.object({
|
|
75
168
|
namespace: z.string().optional(),
|