@peektravel/app-utilities 0.7.1 → 0.7.2
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/README.md +14 -5
- package/dist/index.cjs +54 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +171 -38
- package/dist/index.d.ts +171 -38
- package/dist/index.js +54 -21
- package/dist/index.js.map +1 -1
- package/dist/ui/index.cjs +26 -0
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +77 -3
- package/dist/ui/index.d.ts +77 -3
- package/dist/ui/index.js +25 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/odyssey.css +24 -2
- package/dist/ui/tokens.css +10 -1
- package/docs/ui.md +63 -14
- package/docs/webhooks.md +109 -26
- package/llms.txt +33 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -309,11 +309,20 @@ expiry, issuer, and `"Joken"` audience (the same checks as `verifyPeekAuthToken`
|
|
|
309
309
|
throwing the underlying `jsonwebtoken` error — or `InvalidPeekTokenError` when a
|
|
310
310
|
present `user` block has no `id`), then returns one flat, JSON-safe
|
|
311
311
|
`InstallWebhook`: `installId`, `accountId` (**also called the partner ID**),
|
|
312
|
-
`accountName`, `platform`, `isTest`, `
|
|
313
|
-
a **nullable** `user`. The **
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
312
|
+
`accountName`, `platform`, `isTest`, `timezone`, `apiUrl`, `status`, `rawStatus`,
|
|
313
|
+
`displayVersion`, and a **nullable** `user`. The **JSON body is the source of the
|
|
314
|
+
event data**; the fields it shares with the token (`installId` / `accountId` /
|
|
315
|
+
`status` / `displayVersion` / `user`) fall back to the **verified token** when
|
|
316
|
+
the body omits them. The token authenticates the whole delivery, so the body is
|
|
317
|
+
trusted within a verified request. This webhook is the only source of
|
|
318
|
+
`accountId`, `timezone`, and `apiUrl` in the package, since no GraphQL read
|
|
319
|
+
returns them — **persist them per install**. `apiUrl` is the install's app
|
|
320
|
+
endpoint: use it **as given** (don't reconstruct it from `appId`) — pass it as the
|
|
321
|
+
access service's `apiUrl`, or hand the whole install to
|
|
322
|
+
`createAccessServiceForInstall(install, { jwtSecret, issuer })`, which picks the
|
|
323
|
+
service by `platform` and wires the endpoint for you. Every event is a **full
|
|
324
|
+
snapshot** — an `update_installed` carries the same fields as the original
|
|
325
|
+
`installed`, so upsert by `installId` and take the latest `apiUrl`. `status` and
|
|
317
326
|
`platform` are **nullable** — a value this version doesn't recognise stays `null`
|
|
318
327
|
(wire value on `rawStatus`) instead of being coerced, so a newer registry can't be
|
|
319
328
|
mistaken for an older one. (The older `verifyInstallWebhook(token, secret)` is
|
package/dist/index.cjs
CHANGED
|
@@ -2044,7 +2044,8 @@ var GraphQLClient = class {
|
|
|
2044
2044
|
);
|
|
2045
2045
|
}
|
|
2046
2046
|
endpoint(endpointName) {
|
|
2047
|
-
const { baseUrl, appId, endpointPathPrefix } = this.options;
|
|
2047
|
+
const { apiUrl, baseUrl, appId, endpointPathPrefix } = this.options;
|
|
2048
|
+
if (apiUrl) return apiUrl;
|
|
2048
2049
|
const prefix = endpointPathPrefix ? `${endpointPathPrefix}/` : "";
|
|
2049
2050
|
return `${baseUrl}/${appId}/${prefix}${endpointName}`;
|
|
2050
2051
|
}
|
|
@@ -3561,25 +3562,27 @@ var PeekAccessService = class {
|
|
|
3561
3562
|
bookingService;
|
|
3562
3563
|
reviewService;
|
|
3563
3564
|
constructor(config) {
|
|
3565
|
+
const hasApiUrl = !!config.apiUrl;
|
|
3564
3566
|
const isV2 = config.mode === "v2";
|
|
3565
3567
|
requireNonEmpty(config.installId, "installId", "PeekAccessService");
|
|
3566
3568
|
requireNonEmpty(config.jwtSecret, "jwtSecret", "PeekAccessService");
|
|
3567
3569
|
requireNonEmpty(config.issuer, "issuer", "PeekAccessService");
|
|
3568
|
-
requireNonEmpty(config.appId, "appId", "PeekAccessService");
|
|
3569
|
-
if (!isV2) requireNonEmpty(config.gatewayKey ?? "", "gatewayKey", "PeekAccessService");
|
|
3570
|
+
if (!hasApiUrl) requireNonEmpty(config.appId ?? "", "appId", "PeekAccessService");
|
|
3571
|
+
if (!hasApiUrl && !isV2) requireNonEmpty(config.gatewayKey ?? "", "gatewayKey", "PeekAccessService");
|
|
3570
3572
|
this.jwtSecret = config.jwtSecret;
|
|
3571
3573
|
const logger = config.logger ?? noopLogger;
|
|
3572
3574
|
const tokens = createTokenManager(config);
|
|
3573
3575
|
const defaultBaseUrl = isV2 ? DEFAULT_V2_BASE_URL : DEFAULT_BASE_URL;
|
|
3574
3576
|
this.client = new GraphQLClient({
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
+
apiUrl: config.apiUrl,
|
|
3578
|
+
baseUrl: hasApiUrl ? void 0 : config.baseUrl ?? defaultBaseUrl,
|
|
3579
|
+
appId: hasApiUrl ? void 0 : config.appId,
|
|
3577
3580
|
gatewayKey: config.gatewayKey,
|
|
3578
3581
|
getToken: () => tokens.getToken(),
|
|
3579
3582
|
retryDelaysMs: config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS,
|
|
3580
3583
|
logger,
|
|
3581
3584
|
fetchFn: config.fetch ?? globalThis.fetch,
|
|
3582
|
-
endpointPathPrefix: isV2 ? V2_EXTENDABLE_SLUG : void 0
|
|
3585
|
+
endpointPathPrefix: !hasApiUrl && isV2 ? V2_EXTENDABLE_SLUG : void 0
|
|
3583
3586
|
});
|
|
3584
3587
|
this.productServiceOptions = {
|
|
3585
3588
|
itemOptionsPageSize: config.itemOptionsPageSize
|
|
@@ -4007,7 +4010,8 @@ var RestClient = class {
|
|
|
4007
4010
|
);
|
|
4008
4011
|
}
|
|
4009
4012
|
endpoint(path) {
|
|
4010
|
-
const { baseUrl, appId, extendableSlug } = this.options;
|
|
4013
|
+
const { apiUrl, baseUrl, appId, extendableSlug } = this.options;
|
|
4014
|
+
if (apiUrl) return `${apiUrl}/${path}`;
|
|
4011
4015
|
return `${baseUrl}/${appId}/${extendableSlug}/${path}`;
|
|
4012
4016
|
}
|
|
4013
4017
|
buildHeaders() {
|
|
@@ -4024,15 +4028,17 @@ var CngAccessService = class {
|
|
|
4024
4028
|
client;
|
|
4025
4029
|
productService;
|
|
4026
4030
|
constructor(config) {
|
|
4031
|
+
const hasApiUrl = !!config.apiUrl;
|
|
4027
4032
|
requireNonEmpty(config.installId, "installId", "CngAccessService");
|
|
4028
4033
|
requireNonEmpty(config.jwtSecret, "jwtSecret", "CngAccessService");
|
|
4029
4034
|
requireNonEmpty(config.issuer, "issuer", "CngAccessService");
|
|
4030
|
-
requireNonEmpty(config.appId, "appId", "CngAccessService");
|
|
4035
|
+
if (!hasApiUrl) requireNonEmpty(config.appId ?? "", "appId", "CngAccessService");
|
|
4031
4036
|
const logger = config.logger ?? noopLogger;
|
|
4032
4037
|
const tokens = createTokenManager(config);
|
|
4033
4038
|
this.client = new RestClient({
|
|
4034
|
-
|
|
4035
|
-
|
|
4039
|
+
apiUrl: config.apiUrl,
|
|
4040
|
+
baseUrl: hasApiUrl ? void 0 : config.baseUrl ?? DEFAULT_BASE_URL2,
|
|
4041
|
+
appId: hasApiUrl ? void 0 : config.appId,
|
|
4036
4042
|
extendableSlug: CNG_EXTENDABLE_SLUG,
|
|
4037
4043
|
getToken: () => tokens.getToken(),
|
|
4038
4044
|
retryDelaysMs: config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS,
|
|
@@ -4138,7 +4144,8 @@ var RestClient2 = class {
|
|
|
4138
4144
|
);
|
|
4139
4145
|
}
|
|
4140
4146
|
endpoint(path) {
|
|
4141
|
-
const { baseUrl, appId, extendableSlug } = this.options;
|
|
4147
|
+
const { apiUrl, baseUrl, appId, extendableSlug } = this.options;
|
|
4148
|
+
if (apiUrl) return `${apiUrl}/${path}`;
|
|
4142
4149
|
return `${baseUrl}/${appId}/${extendableSlug}/${path}`;
|
|
4143
4150
|
}
|
|
4144
4151
|
buildHeaders() {
|
|
@@ -4155,15 +4162,17 @@ var AcmeAccessService = class {
|
|
|
4155
4162
|
client;
|
|
4156
4163
|
productService;
|
|
4157
4164
|
constructor(config) {
|
|
4165
|
+
const hasApiUrl = !!config.apiUrl;
|
|
4158
4166
|
requireNonEmpty(config.installId, "installId", "AcmeAccessService");
|
|
4159
4167
|
requireNonEmpty(config.jwtSecret, "jwtSecret", "AcmeAccessService");
|
|
4160
4168
|
requireNonEmpty(config.issuer, "issuer", "AcmeAccessService");
|
|
4161
|
-
requireNonEmpty(config.appId, "appId", "AcmeAccessService");
|
|
4169
|
+
if (!hasApiUrl) requireNonEmpty(config.appId ?? "", "appId", "AcmeAccessService");
|
|
4162
4170
|
const logger = config.logger ?? noopLogger;
|
|
4163
4171
|
const tokens = createTokenManager(config);
|
|
4164
4172
|
this.client = new RestClient2({
|
|
4165
|
-
|
|
4166
|
-
|
|
4173
|
+
apiUrl: config.apiUrl,
|
|
4174
|
+
baseUrl: hasApiUrl ? void 0 : config.baseUrl ?? DEFAULT_BASE_URL3,
|
|
4175
|
+
appId: hasApiUrl ? void 0 : config.appId,
|
|
4167
4176
|
extendableSlug: ACME_EXTENDABLE_SLUG,
|
|
4168
4177
|
getToken: () => tokens.getToken(),
|
|
4169
4178
|
retryDelaysMs: config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS,
|
|
@@ -4187,6 +4196,23 @@ var AcmeAccessService = class {
|
|
|
4187
4196
|
}
|
|
4188
4197
|
};
|
|
4189
4198
|
|
|
4199
|
+
// src/access-service-factory.ts
|
|
4200
|
+
function createAccessServiceForInstall(install, config) {
|
|
4201
|
+
const shared = { ...config, installId: install.installId, apiUrl: install.apiUrl };
|
|
4202
|
+
switch (install.platform) {
|
|
4203
|
+
case "peek":
|
|
4204
|
+
return new PeekAccessService(shared);
|
|
4205
|
+
case "cng":
|
|
4206
|
+
return new CngAccessService(shared);
|
|
4207
|
+
case "acme":
|
|
4208
|
+
return new AcmeAccessService(shared);
|
|
4209
|
+
default:
|
|
4210
|
+
throw new Error(
|
|
4211
|
+
`createAccessServiceForInstall: unknown or missing platform "${install.platform}" for install "${install.installId}"`
|
|
4212
|
+
);
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4190
4216
|
// src/internal/peek/bookings/booking-webhook.ts
|
|
4191
4217
|
var PAYLOAD_BOOKING_KEY = "booking";
|
|
4192
4218
|
var VALUE_OPEN_TOKEN = "value {";
|
|
@@ -4310,20 +4336,27 @@ function toPeekPlatform(raw) {
|
|
|
4310
4336
|
// src/internal/peek/installs/install-webhook.ts
|
|
4311
4337
|
function parseInstallWebhook(token, body, secret) {
|
|
4312
4338
|
const payload = verifyPeekJwt(token, secret);
|
|
4313
|
-
const
|
|
4314
|
-
const
|
|
4339
|
+
const parsed = extractInstallWebhookBody(body);
|
|
4340
|
+
const account = parsed.account ?? {};
|
|
4341
|
+
const rawStatus = parsed.status || payload.status || "";
|
|
4315
4342
|
return {
|
|
4316
|
-
installId: payload.sub
|
|
4317
|
-
accountId: payload.account?.id
|
|
4343
|
+
installId: parsed.install_id || payload.sub || "",
|
|
4344
|
+
accountId: account.id || payload.account?.id || "",
|
|
4318
4345
|
accountName: account.name || "",
|
|
4319
4346
|
platform: toPeekPlatform(account.platform || ""),
|
|
4320
4347
|
isTest: Boolean(account.is_test),
|
|
4348
|
+
timezone: account.timezone || "",
|
|
4349
|
+
apiUrl: parsed.api?.url || "",
|
|
4321
4350
|
status: toInstallStatus(rawStatus),
|
|
4322
4351
|
rawStatus,
|
|
4323
|
-
displayVersion: payload.display_version
|
|
4324
|
-
user:
|
|
4352
|
+
displayVersion: parsed.display_version || payload.display_version || "",
|
|
4353
|
+
user: resolveInstallUser(parsed.modified_by, payload.user)
|
|
4325
4354
|
};
|
|
4326
4355
|
}
|
|
4356
|
+
function resolveInstallUser(bodyUser, tokenUser) {
|
|
4357
|
+
if (bodyUser?.id) return mapPeekTokenUser(bodyUser);
|
|
4358
|
+
return tokenUser ? mapPeekTokenUser(tokenUser) : null;
|
|
4359
|
+
}
|
|
4327
4360
|
function verifyInstallWebhook(token, secret) {
|
|
4328
4361
|
const payload = verifyPeekJwt(token, secret);
|
|
4329
4362
|
return {
|
|
@@ -4364,6 +4397,7 @@ exports.ResellerService = ResellerService;
|
|
|
4364
4397
|
exports.ResourcePoolService = ResourcePoolService;
|
|
4365
4398
|
exports.ReviewService = ReviewService;
|
|
4366
4399
|
exports.TimeslotService = TimeslotService;
|
|
4400
|
+
exports.createAccessServiceForInstall = createAccessServiceForInstall;
|
|
4367
4401
|
exports.noopLogger = noopLogger;
|
|
4368
4402
|
exports.parseBookingWebhook = parseBookingWebhook;
|
|
4369
4403
|
exports.parseInstallWebhook = parseInstallWebhook;
|