@voyant-travel/apps 0.1.0 → 0.2.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/LICENSE +201 -0
- package/dist/contracts.d.ts +8 -87
- package/dist/contracts.js +0 -50
- package/dist/index.d.ts +1 -5
- package/dist/index.js +1 -5
- package/dist/installation-reconciliation.d.ts +1 -1
- package/dist/installation-reconciliation.js +1 -5
- package/dist/installation-service.d.ts +14 -25
- package/dist/installation-service.js +7 -22
- package/dist/routes.d.ts +1 -8
- package/dist/routes.js +1 -67
- package/dist/schema.d.ts +4 -536
- package/dist/schema.js +1 -52
- package/dist/service.d.ts +18 -18
- package/migrations/meta/_journal.json +0 -7
- package/package.json +78 -116
- package/dist/access-boundary.d.ts +0 -29
- package/dist/access-boundary.js +0 -33
- package/dist/app-api-contracts.d.ts +0 -51
- package/dist/app-api-contracts.js +0 -50
- package/dist/app-api-routes.d.ts +0 -26
- package/dist/app-api-routes.js +0 -133
- package/dist/app-api-service.d.ts +0 -1263
- package/dist/app-api-service.js +0 -231
- package/dist/app-api-service.test.d.ts +0 -1
- package/dist/app-api-service.test.js +0 -105
- package/dist/consent.d.ts +0 -15
- package/dist/consent.js +0 -50
- package/dist/consent.test.d.ts +0 -1
- package/dist/consent.test.js +0 -80
- package/dist/oauth-crypto.d.ts +0 -8
- package/dist/oauth-crypto.js +0 -23
- package/dist/oauth-crypto.test.d.ts +0 -1
- package/dist/oauth-crypto.test.js +0 -11
- package/dist/oauth-service.d.ts +0 -65
- package/dist/oauth-service.js +0 -381
- package/dist/oauth-service.test.d.ts +0 -1
- package/dist/oauth-service.test.js +0 -8
- package/migrations/20260717143000_app_oauth_authorization.sql +0 -47
package/dist/app-api-routes.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { ApiHttpError, parseJsonBody, parseQuery, RequestValidationError, } from "@voyant-travel/hono";
|
|
2
|
-
import { Hono } from "hono";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { appApiAuditQuerySchema, appApiCustomFieldDefinitionCreateSchema, appApiCustomFieldDefinitionListQuerySchema, appApiCustomFieldDefinitionUpdateSchema, appApiCustomFieldValueListQuerySchema, appApiCustomFieldValueUpsertSchema, appApiEntityReadQuerySchema, appApiFinanceActionSchema, appApiFinanceDocumentQuerySchema, appApiTokenExchangeSchema, appApiVersionHeader, appApiWebhookReplaySchema, } from "./app-api-contracts.js";
|
|
5
|
-
import { createAppApiService, withAppApiDeadline, } from "./app-api-service.js";
|
|
6
|
-
import { createAppOAuthService } from "./oauth-service.js";
|
|
7
|
-
import { replayAppWebhookDelivery } from "./webhook-delivery.js";
|
|
8
|
-
const idParamSchema = z.object({ id: z.string().min(1) });
|
|
9
|
-
const entityParamSchema = z.object({ entity: z.string().min(1) });
|
|
10
|
-
const definitionBodySchema = z
|
|
11
|
-
.object({
|
|
12
|
-
namespace: z.string().optional(),
|
|
13
|
-
definition: appApiCustomFieldDefinitionCreateSchema,
|
|
14
|
-
})
|
|
15
|
-
.strict();
|
|
16
|
-
const definitionUpdateBodySchema = z
|
|
17
|
-
.object({
|
|
18
|
-
namespace: z.string().optional(),
|
|
19
|
-
definition: appApiCustomFieldDefinitionUpdateSchema,
|
|
20
|
-
})
|
|
21
|
-
.strict();
|
|
22
|
-
export function createAppsAppApiRoutes(options = {}) {
|
|
23
|
-
const routes = new Hono();
|
|
24
|
-
const service = createAppApiService(options);
|
|
25
|
-
const oauth = options.oauth ? createAppOAuthService(options.oauth) : null;
|
|
26
|
-
const maxPayloadBytes = options.maxPayloadBytes ?? 256 * 1024;
|
|
27
|
-
routes.use("*", async (c, next) => {
|
|
28
|
-
if (c.get("callerType") !== "app") {
|
|
29
|
-
throw new ApiHttpError("App API requires an app access token.", {
|
|
30
|
-
status: 401,
|
|
31
|
-
code: "app_api_token_required",
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
const length = Number(c.req.header("content-length") ?? "0");
|
|
35
|
-
if (Number.isFinite(length) && length > maxPayloadBytes) {
|
|
36
|
-
throw new ApiHttpError("App API payload is too large.", {
|
|
37
|
-
status: 413,
|
|
38
|
-
code: "app_api_payload_too_large",
|
|
39
|
-
details: { maxPayloadBytes },
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
return next();
|
|
43
|
-
});
|
|
44
|
-
routes.get("/self", (c) => run(c, service.introspect(c.get("db"), appContext(c)), options.deadlineMs));
|
|
45
|
-
routes.get("/entities/:entity", (c) => {
|
|
46
|
-
const { entity } = parseEntityParams(c.req.param());
|
|
47
|
-
const query = parseQuery(c, appApiEntityReadQuerySchema);
|
|
48
|
-
return run(c, service.listEntities(c.get("db"), appContext(c), entity, query), options.deadlineMs);
|
|
49
|
-
});
|
|
50
|
-
routes.get("/finance/documents", (c) => run(c, service.listFinanceDocuments(c.get("db"), appContext(c), parseQuery(c, appApiFinanceDocumentQuerySchema)), options.deadlineMs));
|
|
51
|
-
routes.post("/finance/actions", async (c) => {
|
|
52
|
-
const body = await parseJsonBody(c, appApiFinanceActionSchema);
|
|
53
|
-
return run(c, service.executeFinanceAction(c.get("db"), appContext(c), body), options.deadlineMs);
|
|
54
|
-
});
|
|
55
|
-
routes.get("/custom-fields/definitions", (c) => run(c, service.listCustomFieldDefinitions(c.get("db"), appContext(c), parseQuery(c, appApiCustomFieldDefinitionListQuerySchema)), options.deadlineMs));
|
|
56
|
-
routes.post("/custom-fields/definitions", async (c) => {
|
|
57
|
-
const body = await parseJsonBody(c, definitionBodySchema);
|
|
58
|
-
return run(c, service.createCustomFieldDefinition(c.get("db"), appContext(c), body.namespace, body.definition), options.deadlineMs);
|
|
59
|
-
});
|
|
60
|
-
routes.patch("/custom-fields/definitions/:id", async (c) => {
|
|
61
|
-
const { id } = parseIdParams(c.req.param());
|
|
62
|
-
const body = await parseJsonBody(c, definitionUpdateBodySchema);
|
|
63
|
-
return run(c, service.updateCustomFieldDefinition(c.get("db"), appContext(c), id, body.namespace, body.definition), options.deadlineMs);
|
|
64
|
-
});
|
|
65
|
-
routes.get("/custom-fields/values", (c) => run(c, service.listCustomFieldValues(c.get("db"), appContext(c), parseQuery(c, appApiCustomFieldValueListQuerySchema)), options.deadlineMs));
|
|
66
|
-
routes.put("/custom-fields/definitions/:id/value", async (c) => {
|
|
67
|
-
const { id } = parseIdParams(c.req.param());
|
|
68
|
-
const body = await parseJsonBody(c, appApiCustomFieldValueUpsertSchema);
|
|
69
|
-
return run(c, service.upsertCustomFieldValue(c.get("db"), appContext(c), id, body), options.deadlineMs);
|
|
70
|
-
});
|
|
71
|
-
routes.get("/webhooks", (c) => run(c, service.listWebhookHealth(c.get("db"), appContext(c)), options.deadlineMs));
|
|
72
|
-
routes.post("/webhooks/replay", async (c) => {
|
|
73
|
-
await service.requireAccess(c.get("db"), appContext(c), ["webhooks:replay"]);
|
|
74
|
-
const body = await parseJsonBody(c, appApiWebhookReplaySchema);
|
|
75
|
-
return run(c, replayAppWebhookDelivery(c.get("db"), {
|
|
76
|
-
deliveryId: body.deliveryId,
|
|
77
|
-
actorId: appContext(c).appId,
|
|
78
|
-
signingKey: { id: body.signingKeyId, secret: body.signingSecret },
|
|
79
|
-
}), options.deadlineMs, 202);
|
|
80
|
-
});
|
|
81
|
-
routes.get("/audit", (c) => run(c, service.listAuditHistory(c.get("db"), appContext(c), parseQuery(c, appApiAuditQuerySchema)), options.deadlineMs));
|
|
82
|
-
routes.post("/oauth/token-exchange", async (c) => {
|
|
83
|
-
if (!oauth)
|
|
84
|
-
return c.json({ error: "App OAuth is not configured" }, 501);
|
|
85
|
-
const context = appContext(c);
|
|
86
|
-
await service.requireAccess(c.get("db"), context, ["online-token:exchange"]);
|
|
87
|
-
const body = await parseJsonBody(c, appApiTokenExchangeSchema);
|
|
88
|
-
return run(c, oauth.token(c.get("db"), {
|
|
89
|
-
grantType: "urn:voyant:params:oauth:grant-type:actor-token-exchange",
|
|
90
|
-
installationId: context.installationId,
|
|
91
|
-
viewerId: body.viewer_id,
|
|
92
|
-
viewerScopes: body.viewer_scopes,
|
|
93
|
-
contextualScopes: body.contextual_scopes,
|
|
94
|
-
clientId: body.client_id,
|
|
95
|
-
clientSecret: body.client_secret,
|
|
96
|
-
}), options.deadlineMs);
|
|
97
|
-
});
|
|
98
|
-
return routes;
|
|
99
|
-
}
|
|
100
|
-
function appContext(c) {
|
|
101
|
-
const appId = c.get("appId");
|
|
102
|
-
const installationId = c.get("appInstallationId");
|
|
103
|
-
const releaseId = c.get("appReleaseId");
|
|
104
|
-
const tokenMode = c.get("appTokenMode");
|
|
105
|
-
if (!appId || !installationId || !releaseId || !tokenMode) {
|
|
106
|
-
throw new RequestValidationError("App token context is missing.");
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
appId,
|
|
110
|
-
installationId,
|
|
111
|
-
releaseId,
|
|
112
|
-
tokenMode,
|
|
113
|
-
viewerId: c.get("appViewerId"),
|
|
114
|
-
scopes: c.get("scopes") ?? [],
|
|
115
|
-
apiVersion: c.req.header(appApiVersionHeader) ?? undefined,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function parseIdParams(input) {
|
|
119
|
-
const parsed = idParamSchema.safeParse(input);
|
|
120
|
-
if (!parsed.success)
|
|
121
|
-
throw new RequestValidationError("Invalid route parameters");
|
|
122
|
-
return parsed.data;
|
|
123
|
-
}
|
|
124
|
-
function parseEntityParams(input) {
|
|
125
|
-
const parsed = entityParamSchema.safeParse(input);
|
|
126
|
-
if (!parsed.success)
|
|
127
|
-
throw new RequestValidationError("Invalid route parameters");
|
|
128
|
-
return parsed.data;
|
|
129
|
-
}
|
|
130
|
-
async function run(c, promise, deadlineMs, status = 200) {
|
|
131
|
-
const data = await withAppApiDeadline(promise, deadlineMs);
|
|
132
|
-
return c.json(data, status);
|
|
133
|
-
}
|