@peektravel/app-utilities 0.7.1 → 0.7.3
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 +21 -11
- package/dist/index.cjs +82 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +208 -69
- package/dist/index.d.ts +208 -69
- package/dist/index.js +82 -46
- 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 +110 -26
- package/llms.txt +38 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -225,15 +225,6 @@ var PeekHttpError = class extends Error {
|
|
|
225
225
|
this.body = body;
|
|
226
226
|
}
|
|
227
227
|
};
|
|
228
|
-
var InvalidPeekTokenError = class extends Error {
|
|
229
|
-
/** The token field that was missing or empty (e.g. `"user.id"`). */
|
|
230
|
-
field;
|
|
231
|
-
constructor(field) {
|
|
232
|
-
super(`Peek token is missing required field "${field}"`);
|
|
233
|
-
this.name = "InvalidPeekTokenError";
|
|
234
|
-
this.field = field;
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
228
|
var PiiAccessDisabledError = class extends Error {
|
|
238
229
|
/** The name of the operation that was blocked (e.g. `"makePayment"`). */
|
|
239
230
|
operation;
|
|
@@ -2022,7 +2013,8 @@ var GraphQLClient = class {
|
|
|
2022
2013
|
);
|
|
2023
2014
|
}
|
|
2024
2015
|
endpoint(endpointName) {
|
|
2025
|
-
const { baseUrl, appId, endpointPathPrefix } = this.options;
|
|
2016
|
+
const { apiUrl, baseUrl, appId, endpointPathPrefix } = this.options;
|
|
2017
|
+
if (apiUrl) return apiUrl;
|
|
2026
2018
|
const prefix = endpointPathPrefix ? `${endpointPathPrefix}/` : "";
|
|
2027
2019
|
return `${baseUrl}/${appId}/${prefix}${endpointName}`;
|
|
2028
2020
|
}
|
|
@@ -2037,6 +2029,11 @@ var GraphQLClient = class {
|
|
|
2037
2029
|
return headers;
|
|
2038
2030
|
}
|
|
2039
2031
|
};
|
|
2032
|
+
|
|
2033
|
+
// src/models/peek/auth-token.ts
|
|
2034
|
+
var PEEK_PLATFORMS = ["peek", "cng", "acme"];
|
|
2035
|
+
|
|
2036
|
+
// src/internal/peek/peek-auth-token.ts
|
|
2040
2037
|
var PEEK_TOKEN_ISSUER = "app_registry_v2";
|
|
2041
2038
|
var PEEK_TOKEN_AUDIENCE = "Joken";
|
|
2042
2039
|
var BEARER_PREFIX = "bearer ";
|
|
@@ -2044,6 +2041,16 @@ function stripBearer(token) {
|
|
|
2044
2041
|
const trimmed = token.trim();
|
|
2045
2042
|
return trimmed.toLowerCase().startsWith(BEARER_PREFIX) ? trimmed.slice(BEARER_PREFIX.length).trim() : trimmed;
|
|
2046
2043
|
}
|
|
2044
|
+
var NULLISH_STRINGS = /* @__PURE__ */ new Set(["", "null"]);
|
|
2045
|
+
function cleanTokenString(value) {
|
|
2046
|
+
if (typeof value !== "string") return null;
|
|
2047
|
+
const trimmed = value.trim();
|
|
2048
|
+
return NULLISH_STRINGS.has(trimmed.toLowerCase()) ? null : trimmed;
|
|
2049
|
+
}
|
|
2050
|
+
function cleanTokenPlatform(value) {
|
|
2051
|
+
const cleaned = cleanTokenString(value);
|
|
2052
|
+
return cleaned !== null && PEEK_PLATFORMS.includes(cleaned) ? cleaned : null;
|
|
2053
|
+
}
|
|
2047
2054
|
function verifyPeekJwt(token, secret) {
|
|
2048
2055
|
return jwt.verify(stripBearer(token), secret, {
|
|
2049
2056
|
issuer: PEEK_TOKEN_ISSUER,
|
|
@@ -2051,16 +2058,14 @@ function verifyPeekJwt(token, secret) {
|
|
|
2051
2058
|
});
|
|
2052
2059
|
}
|
|
2053
2060
|
function mapPeekTokenUser(u) {
|
|
2054
|
-
if (!u
|
|
2055
|
-
throw new InvalidPeekTokenError("user.id");
|
|
2056
|
-
}
|
|
2061
|
+
if (!u) return null;
|
|
2057
2062
|
return {
|
|
2058
|
-
email: u.email,
|
|
2059
|
-
id: u.id,
|
|
2060
|
-
isAdmin: u.is_admin,
|
|
2061
|
-
locale: u.locale,
|
|
2062
|
-
name: u.name,
|
|
2063
|
-
platform: u.platform
|
|
2063
|
+
email: cleanTokenString(u.email),
|
|
2064
|
+
id: cleanTokenString(u.id),
|
|
2065
|
+
isAdmin: typeof u.is_admin === "boolean" ? u.is_admin : null,
|
|
2066
|
+
locale: cleanTokenString(u.locale),
|
|
2067
|
+
name: cleanTokenString(u.name),
|
|
2068
|
+
platform: cleanTokenPlatform(u.platform)
|
|
2064
2069
|
};
|
|
2065
2070
|
}
|
|
2066
2071
|
|
|
@@ -3539,25 +3544,27 @@ var PeekAccessService = class {
|
|
|
3539
3544
|
bookingService;
|
|
3540
3545
|
reviewService;
|
|
3541
3546
|
constructor(config) {
|
|
3547
|
+
const hasApiUrl = !!config.apiUrl;
|
|
3542
3548
|
const isV2 = config.mode === "v2";
|
|
3543
3549
|
requireNonEmpty(config.installId, "installId", "PeekAccessService");
|
|
3544
3550
|
requireNonEmpty(config.jwtSecret, "jwtSecret", "PeekAccessService");
|
|
3545
3551
|
requireNonEmpty(config.issuer, "issuer", "PeekAccessService");
|
|
3546
|
-
requireNonEmpty(config.appId, "appId", "PeekAccessService");
|
|
3547
|
-
if (!isV2) requireNonEmpty(config.gatewayKey ?? "", "gatewayKey", "PeekAccessService");
|
|
3552
|
+
if (!hasApiUrl) requireNonEmpty(config.appId ?? "", "appId", "PeekAccessService");
|
|
3553
|
+
if (!hasApiUrl && !isV2) requireNonEmpty(config.gatewayKey ?? "", "gatewayKey", "PeekAccessService");
|
|
3548
3554
|
this.jwtSecret = config.jwtSecret;
|
|
3549
3555
|
const logger = config.logger ?? noopLogger;
|
|
3550
3556
|
const tokens = createTokenManager(config);
|
|
3551
3557
|
const defaultBaseUrl = isV2 ? DEFAULT_V2_BASE_URL : DEFAULT_BASE_URL;
|
|
3552
3558
|
this.client = new GraphQLClient({
|
|
3553
|
-
|
|
3554
|
-
|
|
3559
|
+
apiUrl: config.apiUrl,
|
|
3560
|
+
baseUrl: hasApiUrl ? void 0 : config.baseUrl ?? defaultBaseUrl,
|
|
3561
|
+
appId: hasApiUrl ? void 0 : config.appId,
|
|
3555
3562
|
gatewayKey: config.gatewayKey,
|
|
3556
3563
|
getToken: () => tokens.getToken(),
|
|
3557
3564
|
retryDelaysMs: config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS,
|
|
3558
3565
|
logger,
|
|
3559
3566
|
fetchFn: config.fetch ?? globalThis.fetch,
|
|
3560
|
-
endpointPathPrefix: isV2 ? V2_EXTENDABLE_SLUG : void 0
|
|
3567
|
+
endpointPathPrefix: !hasApiUrl && isV2 ? V2_EXTENDABLE_SLUG : void 0
|
|
3561
3568
|
});
|
|
3562
3569
|
this.productServiceOptions = {
|
|
3563
3570
|
itemOptionsPageSize: config.itemOptionsPageSize
|
|
@@ -3578,13 +3585,15 @@ var PeekAccessService = class {
|
|
|
3578
3585
|
* malformed
|
|
3579
3586
|
* - `TokenExpiredError` — past `exp`
|
|
3580
3587
|
* - `NotBeforeError` — before `nbf`
|
|
3581
|
-
*
|
|
3582
|
-
*
|
|
3588
|
+
*
|
|
3589
|
+
* Claim *extraction* is defensive: once the signature verifies, a token with
|
|
3590
|
+
* no `user` block yields `claims.user === null`, and any individual user field
|
|
3591
|
+
* that is absent or a sentinel (`""`, `"null"`) is normalized to `null`. This
|
|
3592
|
+
* method never throws on a shape problem — only on a verification failure.
|
|
3583
3593
|
*
|
|
3584
3594
|
* @throws {JsonWebTokenError} signature invalid or token malformed
|
|
3585
3595
|
* @throws {TokenExpiredError} token has expired
|
|
3586
3596
|
* @throws {NotBeforeError} token not yet valid
|
|
3587
|
-
* @throws {InvalidPeekTokenError} verified token has no `user.id`
|
|
3588
3597
|
*/
|
|
3589
3598
|
verifyPeekAuthToken(token) {
|
|
3590
3599
|
const payload = verifyPeekJwt(token, this.jwtSecret);
|
|
@@ -3985,7 +3994,8 @@ var RestClient = class {
|
|
|
3985
3994
|
);
|
|
3986
3995
|
}
|
|
3987
3996
|
endpoint(path) {
|
|
3988
|
-
const { baseUrl, appId, extendableSlug } = this.options;
|
|
3997
|
+
const { apiUrl, baseUrl, appId, extendableSlug } = this.options;
|
|
3998
|
+
if (apiUrl) return `${apiUrl}/${path}`;
|
|
3989
3999
|
return `${baseUrl}/${appId}/${extendableSlug}/${path}`;
|
|
3990
4000
|
}
|
|
3991
4001
|
buildHeaders() {
|
|
@@ -4002,15 +4012,17 @@ var CngAccessService = class {
|
|
|
4002
4012
|
client;
|
|
4003
4013
|
productService;
|
|
4004
4014
|
constructor(config) {
|
|
4015
|
+
const hasApiUrl = !!config.apiUrl;
|
|
4005
4016
|
requireNonEmpty(config.installId, "installId", "CngAccessService");
|
|
4006
4017
|
requireNonEmpty(config.jwtSecret, "jwtSecret", "CngAccessService");
|
|
4007
4018
|
requireNonEmpty(config.issuer, "issuer", "CngAccessService");
|
|
4008
|
-
requireNonEmpty(config.appId, "appId", "CngAccessService");
|
|
4019
|
+
if (!hasApiUrl) requireNonEmpty(config.appId ?? "", "appId", "CngAccessService");
|
|
4009
4020
|
const logger = config.logger ?? noopLogger;
|
|
4010
4021
|
const tokens = createTokenManager(config);
|
|
4011
4022
|
this.client = new RestClient({
|
|
4012
|
-
|
|
4013
|
-
|
|
4023
|
+
apiUrl: config.apiUrl,
|
|
4024
|
+
baseUrl: hasApiUrl ? void 0 : config.baseUrl ?? DEFAULT_BASE_URL2,
|
|
4025
|
+
appId: hasApiUrl ? void 0 : config.appId,
|
|
4014
4026
|
extendableSlug: CNG_EXTENDABLE_SLUG,
|
|
4015
4027
|
getToken: () => tokens.getToken(),
|
|
4016
4028
|
retryDelaysMs: config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS,
|
|
@@ -4116,7 +4128,8 @@ var RestClient2 = class {
|
|
|
4116
4128
|
);
|
|
4117
4129
|
}
|
|
4118
4130
|
endpoint(path) {
|
|
4119
|
-
const { baseUrl, appId, extendableSlug } = this.options;
|
|
4131
|
+
const { apiUrl, baseUrl, appId, extendableSlug } = this.options;
|
|
4132
|
+
if (apiUrl) return `${apiUrl}/${path}`;
|
|
4120
4133
|
return `${baseUrl}/${appId}/${extendableSlug}/${path}`;
|
|
4121
4134
|
}
|
|
4122
4135
|
buildHeaders() {
|
|
@@ -4133,15 +4146,17 @@ var AcmeAccessService = class {
|
|
|
4133
4146
|
client;
|
|
4134
4147
|
productService;
|
|
4135
4148
|
constructor(config) {
|
|
4149
|
+
const hasApiUrl = !!config.apiUrl;
|
|
4136
4150
|
requireNonEmpty(config.installId, "installId", "AcmeAccessService");
|
|
4137
4151
|
requireNonEmpty(config.jwtSecret, "jwtSecret", "AcmeAccessService");
|
|
4138
4152
|
requireNonEmpty(config.issuer, "issuer", "AcmeAccessService");
|
|
4139
|
-
requireNonEmpty(config.appId, "appId", "AcmeAccessService");
|
|
4153
|
+
if (!hasApiUrl) requireNonEmpty(config.appId ?? "", "appId", "AcmeAccessService");
|
|
4140
4154
|
const logger = config.logger ?? noopLogger;
|
|
4141
4155
|
const tokens = createTokenManager(config);
|
|
4142
4156
|
this.client = new RestClient2({
|
|
4143
|
-
|
|
4144
|
-
|
|
4157
|
+
apiUrl: config.apiUrl,
|
|
4158
|
+
baseUrl: hasApiUrl ? void 0 : config.baseUrl ?? DEFAULT_BASE_URL3,
|
|
4159
|
+
appId: hasApiUrl ? void 0 : config.appId,
|
|
4145
4160
|
extendableSlug: ACME_EXTENDABLE_SLUG,
|
|
4146
4161
|
getToken: () => tokens.getToken(),
|
|
4147
4162
|
retryDelaysMs: config.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS,
|
|
@@ -4165,6 +4180,23 @@ var AcmeAccessService = class {
|
|
|
4165
4180
|
}
|
|
4166
4181
|
};
|
|
4167
4182
|
|
|
4183
|
+
// src/access-service-factory.ts
|
|
4184
|
+
function createAccessServiceForInstall(install, config) {
|
|
4185
|
+
const shared = { ...config, installId: install.installId, apiUrl: install.apiUrl };
|
|
4186
|
+
switch (install.platform) {
|
|
4187
|
+
case "peek":
|
|
4188
|
+
return new PeekAccessService(shared);
|
|
4189
|
+
case "cng":
|
|
4190
|
+
return new CngAccessService(shared);
|
|
4191
|
+
case "acme":
|
|
4192
|
+
return new AcmeAccessService(shared);
|
|
4193
|
+
default:
|
|
4194
|
+
throw new Error(
|
|
4195
|
+
`createAccessServiceForInstall: unknown or missing platform "${install.platform}" for install "${install.installId}"`
|
|
4196
|
+
);
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4168
4200
|
// src/internal/peek/bookings/booking-webhook.ts
|
|
4169
4201
|
var PAYLOAD_BOOKING_KEY = "booking";
|
|
4170
4202
|
var VALUE_OPEN_TOKEN = "value {";
|
|
@@ -4255,9 +4287,6 @@ function extractWaiverNode(payload) {
|
|
|
4255
4287
|
return record;
|
|
4256
4288
|
}
|
|
4257
4289
|
|
|
4258
|
-
// src/models/peek/auth-token.ts
|
|
4259
|
-
var PEEK_PLATFORMS = ["peek", "cng", "acme"];
|
|
4260
|
-
|
|
4261
4290
|
// src/models/peek/install.ts
|
|
4262
4291
|
var INSTALL_STATUSES = [
|
|
4263
4292
|
"installed",
|
|
@@ -4288,20 +4317,27 @@ function toPeekPlatform(raw) {
|
|
|
4288
4317
|
// src/internal/peek/installs/install-webhook.ts
|
|
4289
4318
|
function parseInstallWebhook(token, body, secret) {
|
|
4290
4319
|
const payload = verifyPeekJwt(token, secret);
|
|
4291
|
-
const
|
|
4292
|
-
const
|
|
4320
|
+
const parsed = extractInstallWebhookBody(body);
|
|
4321
|
+
const account = parsed.account ?? {};
|
|
4322
|
+
const rawStatus = parsed.status || payload.status || "";
|
|
4293
4323
|
return {
|
|
4294
|
-
installId: payload.sub
|
|
4295
|
-
accountId: payload.account?.id
|
|
4324
|
+
installId: parsed.install_id || payload.sub || "",
|
|
4325
|
+
accountId: account.id || payload.account?.id || "",
|
|
4296
4326
|
accountName: account.name || "",
|
|
4297
4327
|
platform: toPeekPlatform(account.platform || ""),
|
|
4298
4328
|
isTest: Boolean(account.is_test),
|
|
4329
|
+
timezone: account.timezone || "",
|
|
4330
|
+
apiUrl: parsed.api?.url || "",
|
|
4299
4331
|
status: toInstallStatus(rawStatus),
|
|
4300
4332
|
rawStatus,
|
|
4301
|
-
displayVersion: payload.display_version
|
|
4302
|
-
user:
|
|
4333
|
+
displayVersion: parsed.display_version || payload.display_version || "",
|
|
4334
|
+
user: resolveInstallUser(parsed.modified_by, payload.user)
|
|
4303
4335
|
};
|
|
4304
4336
|
}
|
|
4337
|
+
function resolveInstallUser(bodyUser, tokenUser) {
|
|
4338
|
+
if (cleanTokenString(bodyUser?.id) !== null) return mapPeekTokenUser(bodyUser);
|
|
4339
|
+
return mapPeekTokenUser(tokenUser);
|
|
4340
|
+
}
|
|
4305
4341
|
function verifyInstallWebhook(token, secret) {
|
|
4306
4342
|
const payload = verifyPeekJwt(token, secret);
|
|
4307
4343
|
return {
|
|
@@ -4309,10 +4345,10 @@ function verifyInstallWebhook(token, secret) {
|
|
|
4309
4345
|
account: { id: payload.account?.id ?? "" },
|
|
4310
4346
|
status: payload.status ?? "",
|
|
4311
4347
|
displayVersion: payload.display_version ?? "",
|
|
4312
|
-
user:
|
|
4348
|
+
user: mapPeekTokenUser(payload.user)
|
|
4313
4349
|
};
|
|
4314
4350
|
}
|
|
4315
4351
|
|
|
4316
|
-
export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AcmeAccessService, AcmeApiError, AcmeProductService, AdminAccountRequiredError, AvailabilityService, BookingService, CngAccessService, CngApiError, CngProductService, DailyNoteService, INSTALL_STATUSES,
|
|
4352
|
+
export { ACTIVITY_PRODUCT_TYPE, ADD_ON_PRODUCT_TYPE, AccountUserService, AcmeAccessService, AcmeApiError, AcmeProductService, AdminAccountRequiredError, AvailabilityService, BookingService, CngAccessService, CngApiError, CngProductService, DailyNoteService, INSTALL_STATUSES, MembershipService, PeekAccessService, PeekGraphQLError, PeekHttpError, PiiAccessDisabledError, PricingService, ProductService, PromoCodeService, RENTAL_PRODUCT_TYPE, RateLimitError, ResellerService, ResourcePoolService, ReviewService, TimeslotService, createAccessServiceForInstall, noopLogger, parseBookingWebhook, parseInstallWebhook, parseWaiverWebhook, verifyInstallWebhook };
|
|
4317
4353
|
//# sourceMappingURL=index.js.map
|
|
4318
4354
|
//# sourceMappingURL=index.js.map
|