@playaos/api-client 0.10.0 → 0.12.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/README.md +4 -3
- package/dist/applications-schema.d.ts +2 -0
- package/dist/applications-schema.js +8 -1
- package/dist/applications-schema.js.map +1 -1
- package/dist/index.d.ts +187 -14
- package/dist/index.js +118 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,6 @@ import { createClient } from "@playaos/api-client";
|
|
|
15
15
|
|
|
16
16
|
const client = createClient({
|
|
17
17
|
baseUrl: "https://api.playaos.app",
|
|
18
|
-
embedBaseUrl: "https://your-camp.playaos.app",
|
|
19
18
|
apiKey: "pk_live_...",
|
|
20
19
|
});
|
|
21
20
|
|
|
@@ -68,11 +67,11 @@ If your deployment serves both route families from the same host, omit `embedBas
|
|
|
68
67
|
- `embedBaseUrl` optionally overrides the host for `/api/embed/v1/*` routes.
|
|
69
68
|
- When `embedBaseUrl` is omitted, embed requests default to `baseUrl`.
|
|
70
69
|
|
|
71
|
-
This is useful when a deployment splits API-key-authenticated routes onto
|
|
70
|
+
This is useful when a deployment splits API-key-authenticated routes and embed routes onto different hosts. PlayaOS production serves both route families from `https://api.playaos.app`, so most integrations can omit `embedBaseUrl`.
|
|
72
71
|
|
|
73
72
|
## Authentication
|
|
74
73
|
|
|
75
|
-
Generate an API key in **
|
|
74
|
+
Generate an API key in the PlayaOS platform console under your org's **Developer → API Keys** page. Keys use the format `pk_live_*` and are scoped to specific resources.
|
|
76
75
|
|
|
77
76
|
Required scopes per endpoint:
|
|
78
77
|
|
|
@@ -95,6 +94,8 @@ try {
|
|
|
95
94
|
if (err instanceof ApiClientError) {
|
|
96
95
|
console.error(err.status, err.code, err.message);
|
|
97
96
|
// 404, "NOT_FOUND", "Member not found"
|
|
97
|
+
// err.retryAfter is the Retry-After header in seconds when the server sends a
|
|
98
|
+
// numeric one (commonly on 429/503); undefined otherwise.
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
```
|
|
@@ -43,6 +43,7 @@ declare const step4Schema: z.ZodObject<{
|
|
|
43
43
|
build_skills: z.ZodOptional<z.ZodString>;
|
|
44
44
|
strike_available: z.ZodBoolean;
|
|
45
45
|
strike_help: z.ZodOptional<z.ZodString>;
|
|
46
|
+
commitments: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodBoolean]>>>;
|
|
46
47
|
}, z.core.$strip>;
|
|
47
48
|
declare const step5Schema: z.ZodObject<{
|
|
48
49
|
about_yourself: z.ZodString;
|
|
@@ -85,6 +86,7 @@ declare const applicationSchema: z.ZodObject<{
|
|
|
85
86
|
strike_help: z.ZodOptional<z.ZodString>;
|
|
86
87
|
about_yourself: z.ZodOptional<z.ZodString>;
|
|
87
88
|
agrees_to_principles: z.ZodOptional<z.ZodLiteral<true>>;
|
|
89
|
+
commitments: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodNumber, z.ZodBoolean]>>>;
|
|
88
90
|
custom_fields: z.ZodOptional<z.ZodObject<{}, z.core.$loose>>;
|
|
89
91
|
}, z.core.$strip>;
|
|
90
92
|
type ApplicationFormData = z.infer<typeof applicationSchema>;
|
|
@@ -31,7 +31,11 @@ var step4Schema = z.object({
|
|
|
31
31
|
build_available: z.boolean(),
|
|
32
32
|
build_skills: z.string().optional(),
|
|
33
33
|
strike_available: z.boolean(),
|
|
34
|
-
strike_help: z.string().optional()
|
|
34
|
+
strike_help: z.string().optional(),
|
|
35
|
+
// commitmentId → value. Integers only on the number branch (per dataType);
|
|
36
|
+
// per-commitment bounds (min/max) are validated against the live defs at
|
|
37
|
+
// form-render + on the server, not here (PLA-682).
|
|
38
|
+
commitments: z.record(z.string(), z.union([z.number().int(), z.boolean()])).optional()
|
|
35
39
|
}).refine((data) => !data.build_available || data.build_skills, {
|
|
36
40
|
message: "Please tell us about your build skills",
|
|
37
41
|
path: ["build_skills"]
|
|
@@ -65,6 +69,9 @@ var applicationSchema = z.object({
|
|
|
65
69
|
strike_help: z.string().optional(),
|
|
66
70
|
about_yourself: z.string().optional(),
|
|
67
71
|
agrees_to_principles: z.literal(true).optional(),
|
|
72
|
+
// Permissive at the API level (no bounds checks) — the server validates each
|
|
73
|
+
// value against the live commitment definition before seeding (PLA-682).
|
|
74
|
+
commitments: z.record(z.string(), z.union([z.number(), z.boolean()])).optional(),
|
|
68
75
|
custom_fields: z.object({}).passthrough().optional()
|
|
69
76
|
});
|
|
70
77
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/applications-schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\n// Single source of truth for the apply-wizard form shape.\n// The portal apply flow and the @playaos/react <ApplicationForm> SDK both import\n// from here so the validation contract can never drift between them (PLA-483).\n// `ApplicationCreatePayload` in ./types.ts is the zod-free wire mirror; a type\n// equivalence test (applications-schema.test.ts) keeps the two locked together.\n\n// Per-step validation schemas\nexport const step0Schema = z.object({\n acknowledged_expectations: z.literal(true, {\n error: \"Please acknowledge the expectations to continue\",\n }),\n});\n\nexport const step1Schema = z.object({\n first_name: z.string().min(1, \"First name is required\"),\n last_name: z.string().min(1, \"Last name is required\"),\n email: z.string().email(\"Invalid email address\"),\n phone: z.string().min(10, \"Phone number is required\"),\n referral_code: z.string().optional(),\n socials: z.string().optional(),\n birthday: z.string().min(1, \"Birthday is required\"),\n hometown: z.string().min(1, \"Location is required\"),\n});\n\nexport const step2Schema = z.object({\n how_heard: z.string().min(1, \"Please tell us how you heard about us\"),\n burning_man_before: z.enum([\"yes\", \"no\"], { error: \"Please select an option\" }),\n burning_man_years: z.string().optional(),\n});\n\nexport const step3Schema = z.object({\n shelter_type: z.enum([\"joining_rv\", \"bringing_rv\", \"shiftpod_tent\", \"car\", \"truck\", \"van\", \"unsure\"], {\n error: \"Please select a shelter type\",\n }),\n shelter_mates: z.string().optional(),\n ticket_status: z.enum([\"looking\", \"have_ticket\", \"unsure\"], { error: \"Please select your ticket status\" }),\n});\n\nexport const step4Schema = z\n .object({\n build_available: z.boolean(),\n build_skills: z.string().optional(),\n strike_available: z.boolean(),\n strike_help: z.string().optional(),\n })\n .refine((data) => !data.build_available || data.build_skills, {\n message: \"Please tell us about your build skills\",\n path: [\"build_skills\"],\n })\n .refine((data) => !data.strike_available || data.strike_help, {\n message: \"Please tell us how you can help with strike\",\n path: [\"strike_help\"],\n });\n\nexport const step5Schema = z.object({\n about_yourself: z.string().min(50, \"Please write at least 50 characters about yourself\"),\n agrees_to_principles: z.literal(true, { error: \"You must agree to the 10 Principles\" }),\n});\n\n// Combined schema for API-level validation — permissive so external camps\n// can submit applications with only first_name + last_name + email + custom_fields.\n// Step schemas (above) remain strict for the PlayaOS wizard UI.\nexport const applicationSchema = z.object({\n acknowledged_expectations: z.literal(true).optional(),\n first_name: z.string().min(1),\n last_name: z.string().min(1),\n email: z.string().email(),\n phone: z.string().optional(),\n referral_code: z.string().optional(),\n socials: z.string().optional(),\n birthday: z.string().optional(),\n hometown: z.string().optional(),\n how_heard: z.string().optional(),\n burning_man_before: z.enum([\"yes\", \"no\"]).optional(),\n burning_man_years: z.string().optional(),\n shelter_type: z.enum([\"joining_rv\", \"bringing_rv\", \"shiftpod_tent\", \"car\", \"truck\", \"van\", \"unsure\"]).optional(),\n shelter_mates: z.string().optional(),\n ticket_status: z.enum([\"looking\", \"have_ticket\", \"unsure\"]).optional(),\n build_available: z.boolean().optional(),\n build_skills: z.string().optional(),\n strike_available: z.boolean().optional(),\n strike_help: z.string().optional(),\n about_yourself: z.string().optional(),\n agrees_to_principles: z.literal(true).optional(),\n custom_fields: z.object({}).passthrough().optional(),\n});\n\nexport type ApplicationFormData = z.infer<typeof applicationSchema>;\nexport type Step0Data = z.infer<typeof step0Schema>;\nexport type Step1Data = z.infer<typeof step1Schema>;\nexport type Step2Data = z.infer<typeof step2Schema>;\nexport type Step3Data = z.infer<typeof step3Schema>;\nexport type Step4Data = z.infer<typeof step4Schema>;\nexport type Step5Data = z.infer<typeof step5Schema>;\n"],"mappings":";AAAA,SAAS,SAAS;AASX,IAAM,cAAc,EAAE,OAAO;AAAA,EAClC,2BAA2B,EAAE,QAAQ,MAAM;AAAA,IACzC,OAAO;AAAA,EACT,CAAC;AACH,CAAC;AAEM,IAAM,cAAc,EAAE,OAAO;AAAA,EAClC,YAAY,EAAE,OAAO,EAAE,IAAI,GAAG,wBAAwB;AAAA,EACtD,WAAW,EAAE,OAAO,EAAE,IAAI,GAAG,uBAAuB;AAAA,EACpD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB;AAAA,EAC/C,OAAO,EAAE,OAAO,EAAE,IAAI,IAAI,0BAA0B;AAAA,EACpD,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,sBAAsB;AAAA,EAClD,UAAU,EAAE,OAAO,EAAE,IAAI,GAAG,sBAAsB;AACpD,CAAC;AAEM,IAAM,cAAc,EAAE,OAAO;AAAA,EAClC,WAAW,EAAE,OAAO,EAAE,IAAI,GAAG,uCAAuC;AAAA,EACpE,oBAAoB,EAAE,KAAK,CAAC,OAAO,IAAI,GAAG,EAAE,OAAO,0BAA0B,CAAC;AAAA,EAC9E,mBAAmB,EAAE,OAAO,EAAE,SAAS;AACzC,CAAC;AAEM,IAAM,cAAc,EAAE,OAAO;AAAA,EAClC,cAAc,EAAE,KAAK,CAAC,cAAc,eAAe,iBAAiB,OAAO,SAAS,OAAO,QAAQ,GAAG;AAAA,IACpG,OAAO;AAAA,EACT,CAAC;AAAA,EACD,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,eAAe,EAAE,KAAK,CAAC,WAAW,eAAe,QAAQ,GAAG,EAAE,OAAO,mCAAmC,CAAC;AAC3G,CAAC;AAEM,IAAM,cAAc,EACxB,OAAO;AAAA,EACN,iBAAiB,EAAE,QAAQ;AAAA,EAC3B,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,kBAAkB,EAAE,QAAQ;AAAA,EAC5B,aAAa,EAAE,OAAO,EAAE,SAAS;
|
|
1
|
+
{"version":3,"sources":["../src/applications-schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\n// Single source of truth for the apply-wizard form shape.\n// The portal apply flow and the @playaos/react <ApplicationForm> SDK both import\n// from here so the validation contract can never drift between them (PLA-483).\n// `ApplicationCreatePayload` in ./types.ts is the zod-free wire mirror; a type\n// equivalence test (applications-schema.test.ts) keeps the two locked together.\n\n// Per-step validation schemas\nexport const step0Schema = z.object({\n acknowledged_expectations: z.literal(true, {\n error: \"Please acknowledge the expectations to continue\",\n }),\n});\n\nexport const step1Schema = z.object({\n first_name: z.string().min(1, \"First name is required\"),\n last_name: z.string().min(1, \"Last name is required\"),\n email: z.string().email(\"Invalid email address\"),\n phone: z.string().min(10, \"Phone number is required\"),\n referral_code: z.string().optional(),\n socials: z.string().optional(),\n birthday: z.string().min(1, \"Birthday is required\"),\n hometown: z.string().min(1, \"Location is required\"),\n});\n\nexport const step2Schema = z.object({\n how_heard: z.string().min(1, \"Please tell us how you heard about us\"),\n burning_man_before: z.enum([\"yes\", \"no\"], { error: \"Please select an option\" }),\n burning_man_years: z.string().optional(),\n});\n\nexport const step3Schema = z.object({\n shelter_type: z.enum([\"joining_rv\", \"bringing_rv\", \"shiftpod_tent\", \"car\", \"truck\", \"van\", \"unsure\"], {\n error: \"Please select a shelter type\",\n }),\n shelter_mates: z.string().optional(),\n ticket_status: z.enum([\"looking\", \"have_ticket\", \"unsure\"], { error: \"Please select your ticket status\" }),\n});\n\nexport const step4Schema = z\n .object({\n build_available: z.boolean(),\n build_skills: z.string().optional(),\n strike_available: z.boolean(),\n strike_help: z.string().optional(),\n // commitmentId → value. Integers only on the number branch (per dataType);\n // per-commitment bounds (min/max) are validated against the live defs at\n // form-render + on the server, not here (PLA-682).\n commitments: z.record(z.string(), z.union([z.number().int(), z.boolean()])).optional(),\n })\n .refine((data) => !data.build_available || data.build_skills, {\n message: \"Please tell us about your build skills\",\n path: [\"build_skills\"],\n })\n .refine((data) => !data.strike_available || data.strike_help, {\n message: \"Please tell us how you can help with strike\",\n path: [\"strike_help\"],\n });\n\nexport const step5Schema = z.object({\n about_yourself: z.string().min(50, \"Please write at least 50 characters about yourself\"),\n agrees_to_principles: z.literal(true, { error: \"You must agree to the 10 Principles\" }),\n});\n\n// Combined schema for API-level validation — permissive so external camps\n// can submit applications with only first_name + last_name + email + custom_fields.\n// Step schemas (above) remain strict for the PlayaOS wizard UI.\nexport const applicationSchema = z.object({\n acknowledged_expectations: z.literal(true).optional(),\n first_name: z.string().min(1),\n last_name: z.string().min(1),\n email: z.string().email(),\n phone: z.string().optional(),\n referral_code: z.string().optional(),\n socials: z.string().optional(),\n birthday: z.string().optional(),\n hometown: z.string().optional(),\n how_heard: z.string().optional(),\n burning_man_before: z.enum([\"yes\", \"no\"]).optional(),\n burning_man_years: z.string().optional(),\n shelter_type: z.enum([\"joining_rv\", \"bringing_rv\", \"shiftpod_tent\", \"car\", \"truck\", \"van\", \"unsure\"]).optional(),\n shelter_mates: z.string().optional(),\n ticket_status: z.enum([\"looking\", \"have_ticket\", \"unsure\"]).optional(),\n build_available: z.boolean().optional(),\n build_skills: z.string().optional(),\n strike_available: z.boolean().optional(),\n strike_help: z.string().optional(),\n about_yourself: z.string().optional(),\n agrees_to_principles: z.literal(true).optional(),\n // Permissive at the API level (no bounds checks) — the server validates each\n // value against the live commitment definition before seeding (PLA-682).\n commitments: z.record(z.string(), z.union([z.number(), z.boolean()])).optional(),\n custom_fields: z.object({}).passthrough().optional(),\n});\n\nexport type ApplicationFormData = z.infer<typeof applicationSchema>;\nexport type Step0Data = z.infer<typeof step0Schema>;\nexport type Step1Data = z.infer<typeof step1Schema>;\nexport type Step2Data = z.infer<typeof step2Schema>;\nexport type Step3Data = z.infer<typeof step3Schema>;\nexport type Step4Data = z.infer<typeof step4Schema>;\nexport type Step5Data = z.infer<typeof step5Schema>;\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,38 @@
|
|
|
1
1
|
declare class ApiClientError extends Error {
|
|
2
2
|
readonly status: number;
|
|
3
3
|
readonly code: string;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Seconds to wait before retrying, parsed from a numeric `Retry-After`
|
|
6
|
+
* response header when the server sends one (commonly on 429 and 503).
|
|
7
|
+
* Undefined when the header is absent or in HTTP-date form.
|
|
8
|
+
*/
|
|
9
|
+
readonly retryAfter?: number | undefined;
|
|
10
|
+
constructor(status: number, code: string, message: string,
|
|
11
|
+
/**
|
|
12
|
+
* Seconds to wait before retrying, parsed from a numeric `Retry-After`
|
|
13
|
+
* response header when the server sends one (commonly on 429 and 503).
|
|
14
|
+
* Undefined when the header is absent or in HTTP-date form.
|
|
15
|
+
*/
|
|
16
|
+
retryAfter?: number | undefined);
|
|
5
17
|
}
|
|
6
18
|
|
|
19
|
+
/** Cursor-paginated list envelope returned by v1 list endpoints (PLA-779). */
|
|
20
|
+
interface Paginated<T> {
|
|
21
|
+
data: T[];
|
|
22
|
+
/** Opaque cursor for the next page, or null when this is the last page. */
|
|
23
|
+
nextCursor: string | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Shared cursor-pagination request params for list methods. Declared as a `type`
|
|
27
|
+
* (not an `interface`) so intersections like `{ year?: number } & PaginationParams`
|
|
28
|
+
* stay assignable to `Record<string, …>` when serialized into a query string.
|
|
29
|
+
*/
|
|
30
|
+
type PaginationParams = {
|
|
31
|
+
/** Max rows to return (1-100, default 50). */
|
|
32
|
+
limit?: number;
|
|
33
|
+
/** Opaque cursor from a previous response's nextCursor. */
|
|
34
|
+
cursor?: string;
|
|
35
|
+
};
|
|
7
36
|
interface Member {
|
|
8
37
|
id: string;
|
|
9
38
|
firstName: string;
|
|
@@ -85,7 +114,22 @@ interface OrgConfig {
|
|
|
85
114
|
plan: string | null;
|
|
86
115
|
logoUrl: string | null;
|
|
87
116
|
}
|
|
88
|
-
type
|
|
117
|
+
type CommitmentDataType = "integer" | "boolean";
|
|
118
|
+
type CommitmentValue = number | boolean;
|
|
119
|
+
/** An admin-configured commitment definition (per org + year) the apply form renders. */
|
|
120
|
+
interface Commitment {
|
|
121
|
+
id: string;
|
|
122
|
+
name: string;
|
|
123
|
+
displayName: string;
|
|
124
|
+
description: string | null;
|
|
125
|
+
dataType: CommitmentDataType;
|
|
126
|
+
minValue: number | null;
|
|
127
|
+
maxValue: number | null;
|
|
128
|
+
}
|
|
129
|
+
interface CommitmentsListResponse {
|
|
130
|
+
commitments: Commitment[];
|
|
131
|
+
}
|
|
132
|
+
type ApplicationStatus = "draft" | "pending" | "invite_sent" | "interview_scheduled" | "interviewed" | "approved" | "waitlisted" | "rejected" | "spam";
|
|
89
133
|
type MemberRole = "member" | "admin" | "applicant" | "super_admin";
|
|
90
134
|
interface PaymentPageResponse {
|
|
91
135
|
provider: "givebutter" | "crowded";
|
|
@@ -114,6 +158,8 @@ interface ApplicationCreatePayload {
|
|
|
114
158
|
strike_help?: string;
|
|
115
159
|
about_yourself?: string;
|
|
116
160
|
agrees_to_principles?: true;
|
|
161
|
+
/** commitmentId → value (integer or boolean per the commitment dataType), collected at apply (PLA-682). */
|
|
162
|
+
commitments?: Record<string, number | boolean>;
|
|
117
163
|
custom_fields?: Record<string, unknown>;
|
|
118
164
|
}
|
|
119
165
|
interface ApplicationCreateResponse {
|
|
@@ -311,6 +357,21 @@ interface RescheduleInput {
|
|
|
311
357
|
interviewerId?: string;
|
|
312
358
|
timezone?: string;
|
|
313
359
|
}
|
|
360
|
+
interface CalendarStatus {
|
|
361
|
+
connected: boolean;
|
|
362
|
+
provider: string;
|
|
363
|
+
calendarId: string | null;
|
|
364
|
+
calendarName?: string | null;
|
|
365
|
+
}
|
|
366
|
+
interface GoogleCalendar {
|
|
367
|
+
id: string;
|
|
368
|
+
summary: string;
|
|
369
|
+
primary?: boolean;
|
|
370
|
+
}
|
|
371
|
+
interface SelectCalendarInput {
|
|
372
|
+
calendarId: string;
|
|
373
|
+
calendarName?: string;
|
|
374
|
+
}
|
|
314
375
|
|
|
315
376
|
interface ClientOptions {
|
|
316
377
|
/** Base URL for authenticated /api/v1 routes, e.g. https://api.playaos.app */
|
|
@@ -329,9 +390,9 @@ declare class PlayaOSClient {
|
|
|
329
390
|
list: (params?: {
|
|
330
391
|
role?: MemberRole;
|
|
331
392
|
status?: string;
|
|
332
|
-
}, opts?: {
|
|
393
|
+
} & PaginationParams, opts?: {
|
|
333
394
|
signal?: AbortSignal;
|
|
334
|
-
}) => Promise<Member
|
|
395
|
+
}) => Promise<Paginated<Member>>;
|
|
335
396
|
get: (id: string, opts?: {
|
|
336
397
|
signal?: AbortSignal;
|
|
337
398
|
}) => Promise<Member>;
|
|
@@ -378,9 +439,9 @@ declare class PlayaOSClient {
|
|
|
378
439
|
list: (params?: {
|
|
379
440
|
status?: ApplicationStatus;
|
|
380
441
|
year?: number;
|
|
381
|
-
}, opts?: {
|
|
442
|
+
} & PaginationParams, opts?: {
|
|
382
443
|
signal?: AbortSignal;
|
|
383
|
-
}) => Promise<Application
|
|
444
|
+
}) => Promise<Paginated<Application>>;
|
|
384
445
|
create: (payload: ApplicationCreatePayload, opts?: {
|
|
385
446
|
accessToken?: string;
|
|
386
447
|
signal?: AbortSignal;
|
|
@@ -415,9 +476,10 @@ declare class PlayaOSClient {
|
|
|
415
476
|
list: (params?: {
|
|
416
477
|
userId?: string;
|
|
417
478
|
year?: number;
|
|
418
|
-
|
|
479
|
+
paymentStatus?: "unpaid" | "partial" | "paid";
|
|
480
|
+
} & PaginationParams, opts?: {
|
|
419
481
|
signal?: AbortSignal;
|
|
420
|
-
}) => Promise<DuesStatus
|
|
482
|
+
}) => Promise<Paginated<DuesStatus>>;
|
|
421
483
|
};
|
|
422
484
|
readonly shifts: {
|
|
423
485
|
list: (params?: {
|
|
@@ -425,15 +487,21 @@ declare class PlayaOSClient {
|
|
|
425
487
|
fromDate?: string;
|
|
426
488
|
toDate?: string;
|
|
427
489
|
publishedOnly?: boolean;
|
|
428
|
-
|
|
490
|
+
openOnly?: boolean;
|
|
491
|
+
} & PaginationParams, opts?: {
|
|
429
492
|
signal?: AbortSignal;
|
|
430
|
-
}) => Promise<Shift
|
|
493
|
+
}) => Promise<Paginated<Shift>>;
|
|
431
494
|
};
|
|
432
495
|
readonly org: {
|
|
433
496
|
get: (opts?: {
|
|
434
497
|
signal?: AbortSignal;
|
|
435
498
|
}) => Promise<OrgConfig>;
|
|
436
499
|
};
|
|
500
|
+
readonly commitments: {
|
|
501
|
+
list: (opts?: {
|
|
502
|
+
signal?: AbortSignal;
|
|
503
|
+
}) => Promise<CommitmentsListResponse>;
|
|
504
|
+
};
|
|
437
505
|
readonly payments: {
|
|
438
506
|
page: (memberId: string, duesCollectionId?: string, opts?: {
|
|
439
507
|
signal?: AbortSignal;
|
|
@@ -486,9 +554,9 @@ declare class PlayaOSClient {
|
|
|
486
554
|
}) => Promise<unknown[]>;
|
|
487
555
|
create: (campaignId: number, payload: {
|
|
488
556
|
code: string;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
557
|
+
value: number;
|
|
558
|
+
type: "percent" | "fixed";
|
|
559
|
+
expirationDate?: string;
|
|
492
560
|
}, opts: {
|
|
493
561
|
accessToken: string;
|
|
494
562
|
signal?: AbortSignal;
|
|
@@ -559,6 +627,34 @@ declare class PlayaOSClient {
|
|
|
559
627
|
signal?: AbortSignal;
|
|
560
628
|
}) => Promise<SetAvailabilityResponse>;
|
|
561
629
|
};
|
|
630
|
+
calendar: {
|
|
631
|
+
/** Get Google Calendar connection status for an interviewer (PLA-726). Requires admin Bearer. */
|
|
632
|
+
status: (interviewerId: string, opts: {
|
|
633
|
+
accessToken: string;
|
|
634
|
+
signal?: AbortSignal;
|
|
635
|
+
}) => Promise<CalendarStatus>;
|
|
636
|
+
/** List available Google Calendars (PLA-726). Requires admin Bearer. */
|
|
637
|
+
listCalendars: (interviewerId: string, opts: {
|
|
638
|
+
accessToken: string;
|
|
639
|
+
signal?: AbortSignal;
|
|
640
|
+
}) => Promise<{
|
|
641
|
+
calendars: GoogleCalendar[];
|
|
642
|
+
}>;
|
|
643
|
+
/** Select which Google Calendar to use (PLA-726). Requires admin Bearer. */
|
|
644
|
+
select: (interviewerId: string, payload: SelectCalendarInput, opts: {
|
|
645
|
+
accessToken: string;
|
|
646
|
+
signal?: AbortSignal;
|
|
647
|
+
}) => Promise<{
|
|
648
|
+
calendarId: string;
|
|
649
|
+
}>;
|
|
650
|
+
/** Disconnect Google Calendar from interviewer (PLA-726). Requires admin Bearer. */
|
|
651
|
+
disconnect: (interviewerId: string, opts: {
|
|
652
|
+
accessToken: string;
|
|
653
|
+
signal?: AbortSignal;
|
|
654
|
+
}) => Promise<{
|
|
655
|
+
disconnected: boolean;
|
|
656
|
+
}>;
|
|
657
|
+
};
|
|
562
658
|
bookingTokens: {
|
|
563
659
|
/** Create a booking token for an application's interview link (PLA-714). Requires admin Bearer token. */
|
|
564
660
|
create: (payload: CreateBookingTokenInput, opts: {
|
|
@@ -627,4 +723,81 @@ interface ExchangeResult {
|
|
|
627
723
|
}
|
|
628
724
|
declare function exchangeCode(params: ExchangeParams): Promise<ExchangeResult>;
|
|
629
725
|
|
|
630
|
-
|
|
726
|
+
/** A listing holds at most this many images (matches the DB's position 1–6 check). */
|
|
727
|
+
declare const MAX_IMAGE_POSITION = 6;
|
|
728
|
+
/**
|
|
729
|
+
* Typed client for the anonymous gift-layer marketplace API (`/api/public/marketplace/*`).
|
|
730
|
+
*
|
|
731
|
+
* Unlike `PlayaOSClient` (api-key auth for `/api/v1`), this surface authenticates
|
|
732
|
+
* writes with a Supabase **Bearer JWT** belonging to a public `global_accounts`
|
|
733
|
+
* user. The token is supplied by an injected `getToken` provider so the same
|
|
734
|
+
* client works from any consumer (the marketing UI supplies the browser
|
|
735
|
+
* Supabase session token; a future surface could supply its own). Keeps the
|
|
736
|
+
* public API as the single write contract — the UI is just another consumer.
|
|
737
|
+
*/
|
|
738
|
+
interface PublicMarketplaceClientOptions {
|
|
739
|
+
/** API base, e.g. `https://api.playaos.app`. */
|
|
740
|
+
baseUrl: string;
|
|
741
|
+
/** Supplies the Supabase access token (Bearer) for authenticated calls; null/undefined when signed out. */
|
|
742
|
+
getToken: () => Promise<string | null | undefined> | string | null | undefined;
|
|
743
|
+
}
|
|
744
|
+
interface NewListingInput {
|
|
745
|
+
category: string;
|
|
746
|
+
subcategory?: string;
|
|
747
|
+
title: string;
|
|
748
|
+
body: string;
|
|
749
|
+
price?: number;
|
|
750
|
+
location?: string;
|
|
751
|
+
replyEmail: string;
|
|
752
|
+
}
|
|
753
|
+
interface ImageUploadTarget {
|
|
754
|
+
/** Supabase signed upload URL — PUT the file bytes here. */
|
|
755
|
+
uploadUrl: string;
|
|
756
|
+
/** Storage path to pass back to `attachImage` once the PUT succeeds. */
|
|
757
|
+
storagePath: string;
|
|
758
|
+
}
|
|
759
|
+
/** Moderation flag reasons accepted by the API (mirrors the DB enum). */
|
|
760
|
+
type MarketplaceFlagReason = "spam" | "prohibited" | "other";
|
|
761
|
+
interface ListingImage {
|
|
762
|
+
/** Short-lived signed download URL for the image. */
|
|
763
|
+
url: string;
|
|
764
|
+
/** 1-based display order. */
|
|
765
|
+
position: number;
|
|
766
|
+
}
|
|
767
|
+
interface PublicMarketplaceClient {
|
|
768
|
+
/**
|
|
769
|
+
* Create a listing as the signed-in account. Throws a plain `Error` when there
|
|
770
|
+
* is no token (signed out), or `ApiClientError` on a non-2xx response.
|
|
771
|
+
*/
|
|
772
|
+
createListing(input: NewListingInput, signal?: AbortSignal): Promise<{
|
|
773
|
+
id: string;
|
|
774
|
+
}>;
|
|
775
|
+
/** Request a signed upload URL for an image on a listing the caller owns. */
|
|
776
|
+
getImageUploadUrl(listingId: string, signal?: AbortSignal): Promise<ImageUploadTarget>;
|
|
777
|
+
/** Attach an already-uploaded image (by storage path) to a listing at a 1–6 position. */
|
|
778
|
+
attachImage(listingId: string, input: {
|
|
779
|
+
storagePath: string;
|
|
780
|
+
position: number;
|
|
781
|
+
}, signal?: AbortSignal): Promise<{
|
|
782
|
+
id: string;
|
|
783
|
+
}>;
|
|
784
|
+
/** Anonymous: list an active listing's images as short-lived signed URLs (for public display). */
|
|
785
|
+
listListingImages(listingId: string, signal?: AbortSignal): Promise<{
|
|
786
|
+
images: ListingImage[];
|
|
787
|
+
}>;
|
|
788
|
+
/** Anonymous: batch-fetch first-image signed thumbnail URLs (listingId -> url) for browse cards. */
|
|
789
|
+
listThumbnails(listingIds: string[], signal?: AbortSignal): Promise<{
|
|
790
|
+
thumbnails: Record<string, string>;
|
|
791
|
+
}>;
|
|
792
|
+
/** Flag a listing for moderation (signed-in). Idempotent per (listing, user). */
|
|
793
|
+
flagListing(listingId: string, reason: MarketplaceFlagReason, signal?: AbortSignal): Promise<{
|
|
794
|
+
ok: boolean;
|
|
795
|
+
}>;
|
|
796
|
+
/** Contact a listing's owner (signed-in); records the reply + queues a notification email. */
|
|
797
|
+
replyToListing(listingId: string, message: string | undefined, signal?: AbortSignal): Promise<{
|
|
798
|
+
ok: boolean;
|
|
799
|
+
}>;
|
|
800
|
+
}
|
|
801
|
+
declare function createPublicMarketplaceClient(opts: PublicMarketplaceClientOptions): PublicMarketplaceClient;
|
|
802
|
+
|
|
803
|
+
export { ApiClientError, type Application, type ApplicationAnnotations, type ApplicationCreatePayload, type ApplicationCreateResponse, type ApplicationNote, type ApplicationScore, type ApplicationStatus, type ApplicationTransitionPayload, type ApplicationTransitionResponse, type Commitment, type CommitmentDataType, type CommitmentValue, type CommitmentsListResponse, type DuesStatus, type ExchangeParams, type ExchangeResult, type ImageUploadTarget, type ListingImage, MAX_IMAGE_POSITION, type MarketplaceFlagReason, type Member, type MemberCreatePayload, type MemberCreateResponse, type MemberDeactivateResponse, type MemberRole, type MemberUpdatePayload, type MemberUpdateResponse, type NewListingInput, type OrgConfig, type PaymentPageResponse, PlayaOSClient, type PublicMarketplaceClient, type PublicMarketplaceClientOptions, type Shift, type ShiftSignup, type ShiftType, type Waiver, type WaiverContent, type WaiverSection, type WaiverSignPayload, type WaiverSignResponse, type WaiverStatus, createClient, createPublicMarketplaceClient, exchangeCode };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
// src/error.ts
|
|
2
2
|
var ApiClientError = class extends Error {
|
|
3
|
-
constructor(status, code, message) {
|
|
3
|
+
constructor(status, code, message, retryAfter) {
|
|
4
4
|
super(message);
|
|
5
5
|
this.status = status;
|
|
6
6
|
this.code = code;
|
|
7
|
+
this.retryAfter = retryAfter;
|
|
7
8
|
this.name = "ApiClientError";
|
|
8
9
|
}
|
|
9
10
|
status;
|
|
10
11
|
code;
|
|
12
|
+
retryAfter;
|
|
11
13
|
};
|
|
14
|
+
function parseRetryAfter(res) {
|
|
15
|
+
const header = res.headers?.get?.("retry-after")?.trim();
|
|
16
|
+
return header && /^\d+$/.test(header) ? parseInt(header, 10) : void 0;
|
|
17
|
+
}
|
|
12
18
|
async function parseError(res) {
|
|
19
|
+
const retryAfter = parseRetryAfter(res);
|
|
13
20
|
try {
|
|
14
21
|
const body = await res.json();
|
|
15
|
-
return new ApiClientError(res.status, body.code ?? "UNKNOWN", body.error ?? res.statusText);
|
|
22
|
+
return new ApiClientError(res.status, body.code ?? "UNKNOWN", body.error ?? res.statusText, retryAfter);
|
|
16
23
|
} catch {
|
|
17
|
-
return new ApiClientError(res.status, "UNKNOWN", res.statusText);
|
|
24
|
+
return new ApiClientError(res.status, "UNKNOWN", res.statusText, retryAfter);
|
|
18
25
|
}
|
|
19
26
|
}
|
|
20
27
|
|
|
@@ -31,8 +38,13 @@ function buildUrl(base, path, params) {
|
|
|
31
38
|
function buildEmbedUrl(base, path) {
|
|
32
39
|
return `${base}/api/embed/v1${path}`;
|
|
33
40
|
}
|
|
41
|
+
function withCampKey(url, campKey) {
|
|
42
|
+
const embedUrl = new URL(url);
|
|
43
|
+
embedUrl.searchParams.set("camp_key", campKey);
|
|
44
|
+
return embedUrl.toString();
|
|
45
|
+
}
|
|
34
46
|
async function getEmbed(url, campKey, signal) {
|
|
35
|
-
const res = await fetch(url, {
|
|
47
|
+
const res = await fetch(withCampKey(url, campKey), {
|
|
36
48
|
headers: { "X-Camp-Key": campKey },
|
|
37
49
|
signal
|
|
38
50
|
});
|
|
@@ -42,7 +54,7 @@ async function getEmbed(url, campKey, signal) {
|
|
|
42
54
|
async function getEmbedAuthed(url, campKey, opts) {
|
|
43
55
|
const headers = { "X-Camp-Key": campKey };
|
|
44
56
|
if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;
|
|
45
|
-
const res = await fetch(url, { headers, signal: opts?.signal });
|
|
57
|
+
const res = await fetch(withCampKey(url, campKey), { headers, signal: opts?.signal });
|
|
46
58
|
if (!res.ok) throw await parseError(res);
|
|
47
59
|
return res.json();
|
|
48
60
|
}
|
|
@@ -70,7 +82,7 @@ async function postEmbed(url, campKey, body, opts) {
|
|
|
70
82
|
"Content-Type": "application/json"
|
|
71
83
|
};
|
|
72
84
|
if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;
|
|
73
|
-
const res = await fetch(url, {
|
|
85
|
+
const res = await fetch(withCampKey(url, campKey), {
|
|
74
86
|
method: "POST",
|
|
75
87
|
headers,
|
|
76
88
|
body: JSON.stringify(body),
|
|
@@ -85,7 +97,7 @@ async function putEmbed(url, campKey, body, opts) {
|
|
|
85
97
|
"Content-Type": "application/json"
|
|
86
98
|
};
|
|
87
99
|
if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;
|
|
88
|
-
const res = await fetch(url, {
|
|
100
|
+
const res = await fetch(withCampKey(url, campKey), {
|
|
89
101
|
method: "PUT",
|
|
90
102
|
headers,
|
|
91
103
|
body: JSON.stringify(body),
|
|
@@ -97,7 +109,7 @@ async function putEmbed(url, campKey, body, opts) {
|
|
|
97
109
|
async function deleteEmbed(url, campKey, opts) {
|
|
98
110
|
const headers = { "X-Camp-Key": campKey };
|
|
99
111
|
if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;
|
|
100
|
-
const res = await fetch(url, { method: "DELETE", headers, signal: opts?.signal });
|
|
112
|
+
const res = await fetch(withCampKey(url, campKey), { method: "DELETE", headers, signal: opts?.signal });
|
|
101
113
|
if (!res.ok) throw await parseError(res);
|
|
102
114
|
return res.json();
|
|
103
115
|
}
|
|
@@ -174,6 +186,12 @@ var PlayaOSClient = class {
|
|
|
174
186
|
org = {
|
|
175
187
|
get: (opts) => request(buildUrl(this.baseUrl, "/org"), this.apiKey, opts?.signal)
|
|
176
188
|
};
|
|
189
|
+
// Public embed surface: active commitment definitions for the org's current
|
|
190
|
+
// year, so the <ApplicationForm> can render + validate them (PLA-682). Uses
|
|
191
|
+
// the X-Camp-Key (the apiKey doubles as the camp key), same as applications.create.
|
|
192
|
+
commitments = {
|
|
193
|
+
list: (opts) => getEmbed(buildEmbedUrl(this.embedBaseUrl, "/commitments"), this.apiKey, opts?.signal)
|
|
194
|
+
};
|
|
177
195
|
payments = {
|
|
178
196
|
page: (memberId, duesCollectionId, opts) => post(buildUrl(this.baseUrl, "/payments/page"), this.apiKey, { memberId, duesCollectionId }, opts?.signal)
|
|
179
197
|
};
|
|
@@ -324,6 +342,46 @@ var PlayaOSClient = class {
|
|
|
324
342
|
opts.signal
|
|
325
343
|
)
|
|
326
344
|
},
|
|
345
|
+
calendar: {
|
|
346
|
+
/** Get Google Calendar connection status for an interviewer (PLA-726). Requires admin Bearer. */
|
|
347
|
+
status: (interviewerId, opts) => adminEmbed(
|
|
348
|
+
"GET",
|
|
349
|
+
buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/status`),
|
|
350
|
+
opts.accessToken,
|
|
351
|
+
void 0,
|
|
352
|
+
opts.signal
|
|
353
|
+
),
|
|
354
|
+
/** List available Google Calendars (PLA-726). Requires admin Bearer. */
|
|
355
|
+
listCalendars: (interviewerId, opts) => adminEmbed(
|
|
356
|
+
"GET",
|
|
357
|
+
buildEmbedUrl(
|
|
358
|
+
this.embedBaseUrl,
|
|
359
|
+
`/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/calendars`
|
|
360
|
+
),
|
|
361
|
+
opts.accessToken,
|
|
362
|
+
void 0,
|
|
363
|
+
opts.signal
|
|
364
|
+
),
|
|
365
|
+
/** Select which Google Calendar to use (PLA-726). Requires admin Bearer. */
|
|
366
|
+
select: (interviewerId, payload, opts) => adminEmbed(
|
|
367
|
+
"PUT",
|
|
368
|
+
buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/select`),
|
|
369
|
+
opts.accessToken,
|
|
370
|
+
payload,
|
|
371
|
+
opts.signal
|
|
372
|
+
),
|
|
373
|
+
/** Disconnect Google Calendar from interviewer (PLA-726). Requires admin Bearer. */
|
|
374
|
+
disconnect: (interviewerId, opts) => adminEmbed(
|
|
375
|
+
"POST",
|
|
376
|
+
buildEmbedUrl(
|
|
377
|
+
this.embedBaseUrl,
|
|
378
|
+
`/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/disconnect`
|
|
379
|
+
),
|
|
380
|
+
opts.accessToken,
|
|
381
|
+
void 0,
|
|
382
|
+
opts.signal
|
|
383
|
+
)
|
|
384
|
+
},
|
|
327
385
|
bookingTokens: {
|
|
328
386
|
/** Create a booking token for an application's interview link (PLA-714). Requires admin Bearer token. */
|
|
329
387
|
create: (payload, opts) => adminEmbed(
|
|
@@ -350,8 +408,8 @@ var PlayaOSClient = class {
|
|
|
350
408
|
/** List available interview slots for a booking token (PLA-714). Public. */
|
|
351
409
|
getSlots: (token, opts) => {
|
|
352
410
|
const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}`;
|
|
353
|
-
return fetch(url, { signal: opts?.signal }).then((res) => {
|
|
354
|
-
if (!res.ok) throw parseError(res);
|
|
411
|
+
return fetch(url, { signal: opts?.signal }).then(async (res) => {
|
|
412
|
+
if (!res.ok) throw await parseError(res);
|
|
355
413
|
return res.json();
|
|
356
414
|
});
|
|
357
415
|
},
|
|
@@ -363,16 +421,16 @@ var PlayaOSClient = class {
|
|
|
363
421
|
headers: { "Content-Type": "application/json" },
|
|
364
422
|
body: JSON.stringify(payload),
|
|
365
423
|
signal: opts?.signal
|
|
366
|
-
}).then((res) => {
|
|
367
|
-
if (!res.ok) throw parseError(res);
|
|
424
|
+
}).then(async (res) => {
|
|
425
|
+
if (!res.ok) throw await parseError(res);
|
|
368
426
|
return res.json();
|
|
369
427
|
});
|
|
370
428
|
},
|
|
371
429
|
/** Cancel an interview booking (PLA-714). Public. */
|
|
372
430
|
cancel: (token, opts) => {
|
|
373
431
|
const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}/cancel`;
|
|
374
|
-
return fetch(url, { method: "POST", signal: opts?.signal }).then((res) => {
|
|
375
|
-
if (!res.ok) throw parseError(res);
|
|
432
|
+
return fetch(url, { method: "POST", signal: opts?.signal }).then(async (res) => {
|
|
433
|
+
if (!res.ok) throw await parseError(res);
|
|
376
434
|
return res.json();
|
|
377
435
|
});
|
|
378
436
|
},
|
|
@@ -384,8 +442,8 @@ var PlayaOSClient = class {
|
|
|
384
442
|
headers: { "Content-Type": "application/json" },
|
|
385
443
|
body: JSON.stringify(payload),
|
|
386
444
|
signal: opts?.signal
|
|
387
|
-
}).then((res) => {
|
|
388
|
-
if (!res.ok) throw parseError(res);
|
|
445
|
+
}).then(async (res) => {
|
|
446
|
+
if (!res.ok) throw await parseError(res);
|
|
389
447
|
return res.json();
|
|
390
448
|
});
|
|
391
449
|
}
|
|
@@ -433,10 +491,54 @@ async function exchangeCode(params) {
|
|
|
433
491
|
...refreshToken !== void 0 ? { refreshToken } : {}
|
|
434
492
|
};
|
|
435
493
|
}
|
|
494
|
+
|
|
495
|
+
// src/public-marketplace.ts
|
|
496
|
+
var MAX_IMAGE_POSITION = 6;
|
|
497
|
+
function createPublicMarketplaceClient(opts) {
|
|
498
|
+
const base = opts.baseUrl.replace(/\/+$/, "");
|
|
499
|
+
if (!base) {
|
|
500
|
+
throw new Error("createPublicMarketplaceClient: baseUrl is required");
|
|
501
|
+
}
|
|
502
|
+
async function authedPost(path, body, signal) {
|
|
503
|
+
const token = await opts.getToken();
|
|
504
|
+
if (!token) throw new Error("Not signed in");
|
|
505
|
+
const res = await fetch(`${base}/api/public/marketplace${path}`, {
|
|
506
|
+
method: "POST",
|
|
507
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
508
|
+
body: JSON.stringify(body),
|
|
509
|
+
signal
|
|
510
|
+
});
|
|
511
|
+
if (!res.ok) throw await parseError(res);
|
|
512
|
+
return res.json();
|
|
513
|
+
}
|
|
514
|
+
async function publicGet(path, signal) {
|
|
515
|
+
const init = { signal, cache: "no-store" };
|
|
516
|
+
const res = await fetch(`${base}/api/public/marketplace${path}`, init);
|
|
517
|
+
if (!res.ok) throw await parseError(res);
|
|
518
|
+
return res.json();
|
|
519
|
+
}
|
|
520
|
+
const seg = (listingId) => `/listings/${encodeURIComponent(listingId)}`;
|
|
521
|
+
return {
|
|
522
|
+
createListing: (input, signal) => authedPost("/listings", input, signal),
|
|
523
|
+
getImageUploadUrl: (listingId, signal) => authedPost(`${seg(listingId)}/image-upload-url`, {}, signal),
|
|
524
|
+
attachImage: (listingId, input, signal) => {
|
|
525
|
+
if (!Number.isInteger(input.position) || input.position < 1 || input.position > MAX_IMAGE_POSITION) {
|
|
526
|
+
return Promise.reject(new Error(`attachImage: position must be 1\u2013${MAX_IMAGE_POSITION}`));
|
|
527
|
+
}
|
|
528
|
+
return authedPost(`${seg(listingId)}/images`, input, signal);
|
|
529
|
+
},
|
|
530
|
+
listListingImages: (listingId, signal) => publicGet(`${seg(listingId)}/images`, signal),
|
|
531
|
+
listThumbnails: (listingIds, signal) => publicGet(`/thumbnails?ids=${encodeURIComponent(listingIds.join(","))}`, signal),
|
|
532
|
+
flagListing: (listingId, reason, signal) => authedPost(`${seg(listingId)}/flag`, { reason }, signal),
|
|
533
|
+
replyToListing: (listingId, message, signal) => authedPost(`${seg(listingId)}/reply`, { message }, signal)
|
|
534
|
+
};
|
|
535
|
+
}
|
|
436
536
|
export {
|
|
437
537
|
ApiClientError,
|
|
538
|
+
MAX_IMAGE_POSITION,
|
|
438
539
|
PlayaOSClient,
|
|
439
540
|
createClient,
|
|
541
|
+
createPublicMarketplaceClient,
|
|
440
542
|
exchangeCode
|
|
441
543
|
};
|
|
442
544
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/error.ts","../src/client.ts","../src/exchange.ts"],"sourcesContent":["export class ApiClientError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n ) {\n super(message);\n this.name = \"ApiClientError\";\n }\n}\n\nexport async function parseError(res: Response): Promise<ApiClientError> {\n try {\n const body = (await res.json()) as { error?: string; code?: string };\n return new ApiClientError(res.status, body.code ?? \"UNKNOWN\", body.error ?? res.statusText);\n } catch {\n return new ApiClientError(res.status, \"UNKNOWN\", res.statusText);\n }\n}\n","import { type ApiClientError, parseError } from \"./error.js\";\nimport type {\n AnnouncementSendPayload,\n AnnouncementSendResponse,\n Application,\n ApplicationAnnotations,\n ApplicationCreatePayload,\n ApplicationCreateResponse,\n ApplicationHistoryResponse,\n ApplicationNote,\n ApplicationScore,\n ApplicationStatus,\n ApplicationTransitionPayload,\n ApplicationTransitionResponse,\n BookingToken,\n BookSlotInput,\n BookSlotResponse,\n CreateBookingTokenInput,\n DuesCreatePayload,\n DuesCreateResponse,\n DuesStatus,\n DuesUpdatePayload,\n DuesUpdateResponse,\n Member,\n MemberCreatePayload,\n MemberCreateResponse,\n MemberDeactivateResponse,\n MemberRole,\n MemberUpdatePayload,\n MemberUpdateResponse,\n OrgConfig,\n OrgUpdatePayload,\n OrgUpdateResponse,\n PaymentPageResponse,\n RescheduleInput,\n SetAvailabilityInput,\n SetAvailabilityResponse,\n Shift,\n Waiver,\n WaiverSignPayload,\n WaiverSignResponse,\n WaiverStatus,\n} from \"./types.js\";\n\nexport type { ApiClientError };\n\ninterface ClientOptions {\n /** Base URL for authenticated /api/v1 routes, e.g. https://api.playaos.app */\n baseUrl: string;\n /** Optional base URL for /api/embed/v1 routes when they live on a different host. */\n embedBaseUrl?: string;\n /** API key in the format pk_live_* */\n apiKey: string;\n}\n\nfunction buildUrl(base: string, path: string, params?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(`${base}/api/v1${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n // Skip undefined and false booleans — z.coerce.boolean() on the server uses Boolean(),\n // which treats any non-empty string (including \"false\") as true. Omitting a false boolean\n // param has the same effect as the server default (unfiltered).\n if (v !== undefined && v !== false) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\nfunction buildEmbedUrl(base: string, path: string): string {\n return `${base}/api/embed/v1${path}`;\n}\n\nasync function getEmbed<T>(url: string, campKey: string, signal?: AbortSignal): Promise<T> {\n const res = await fetch(url, {\n headers: { \"X-Camp-Key\": campKey },\n signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function getEmbedAuthed<T>(\n url: string,\n campKey: string,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = { \"X-Camp-Key\": campKey };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(url, { headers, signal: opts?.signal });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function request<T>(url: string, apiKey: string, signal?: AbortSignal): Promise<T> {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${apiKey}` },\n signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function post<T>(url: string, apiKey: string, body: unknown, signal?: AbortSignal): Promise<T> {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${apiKey}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function postEmbed<T>(\n url: string,\n campKey: string,\n body: unknown,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = {\n \"X-Camp-Key\": campKey,\n \"Content-Type\": \"application/json\",\n };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: opts?.signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function putEmbed<T>(\n url: string,\n campKey: string,\n body: unknown,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = {\n \"X-Camp-Key\": campKey,\n \"Content-Type\": \"application/json\",\n };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(url, {\n method: \"PUT\",\n headers,\n body: JSON.stringify(body),\n signal: opts?.signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function deleteEmbed<T>(\n url: string,\n campKey: string,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = { \"X-Camp-Key\": campKey };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(url, { method: \"DELETE\", headers, signal: opts?.signal });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\n/**\n * Bearer-only helper for /api/embed/v1/admin/* (PLA-489). The admin token is\n * the sole credential (no X-Camp-Key — orgId comes from the JWT claims\n * server-side). `method` can be GET, POST, PATCH, or PUT; `body` is optional\n * (omitted for GET reads like getAnnotations).\n */\nasync function adminEmbed<T>(\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\",\n url: string,\n accessToken: string,\n body?: unknown,\n signal?: AbortSignal,\n): Promise<T> {\n const headers: Record<string, string> = { Authorization: `Bearer ${accessToken}` };\n const init: RequestInit = { method, headers, signal };\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n init.body = JSON.stringify(body);\n }\n const res = await fetch(url, init);\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nexport class PlayaOSClient {\n private readonly baseUrl: string;\n private readonly embedBaseUrl: string;\n private readonly apiKey: string;\n\n constructor(opts: ClientOptions) {\n this.baseUrl = opts.baseUrl.replace(/\\/+$/, \"\");\n this.embedBaseUrl = (opts.embedBaseUrl ?? opts.baseUrl).replace(/\\/+$/, \"\");\n this.apiKey = opts.apiKey;\n }\n\n readonly members = {\n list: (params?: { role?: MemberRole; status?: string }, opts?: { signal?: AbortSignal }): Promise<Member[]> =>\n request(buildUrl(this.baseUrl, \"/members\", params), this.apiKey, opts?.signal),\n\n get: (id: string, opts?: { signal?: AbortSignal }): Promise<Member> =>\n request(buildUrl(this.baseUrl, `/members/${encodeURIComponent(id)}`), this.apiKey, opts?.signal),\n\n /** Member-self profile update (PLA-700). Requires camp key + member Bearer token. */\n selfUpdate: (\n payload: {\n bio?: string | null;\n playaName?: string | null;\n socialMedia?: Record<string, unknown> | null;\n showSocialsInDirectory?: boolean;\n phone?: string | null;\n emergencyContactName?: string | null;\n emergencyContactRelationship?: string | null;\n emergencyContactPhone?: string | null;\n profilePhotoPath?: string | null;\n yearJoined?: number | null;\n },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ profileId: string }> =>\n putEmbed(buildEmbedUrl(this.embedBaseUrl, \"/members/self\"), this.apiKey, payload, opts),\n\n /** Member-self shift signup + cancel (PLA-705). Requires camp key + member Bearer. */\n shifts: {\n signup: (\n payload: { shiftId: string; role?: string },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ signupId: string; waitlisted: boolean }> =>\n putEmbed(buildEmbedUrl(this.embedBaseUrl, \"/shifts/signup\"), this.apiKey, payload, opts),\n cancel: (\n signupId: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ canceled: boolean; promotedSignupId: string | null }> =>\n deleteEmbed(\n buildEmbedUrl(this.embedBaseUrl, `/shifts/signups/${encodeURIComponent(signupId)}`),\n this.apiKey,\n opts,\n ),\n },\n };\n\n readonly applications = {\n list: (\n params?: { status?: ApplicationStatus; year?: number },\n opts?: { signal?: AbortSignal },\n ): Promise<Application[]> => request(buildUrl(this.baseUrl, \"/applications\", params), this.apiKey, opts?.signal),\n\n // Anonymous external-camp submission against POST /api/embed/v1/applications\n // (PLA-481). Uses the X-Camp-Key header rather than Bearer auth. Pass\n // `accessToken` to additionally send a PlayaOS IdP JWT (PLA-573 path) so\n // the server trusts the verified profile claim and skips the email-based\n // bootstrap.\n create: (\n payload: ApplicationCreatePayload,\n opts?: { accessToken?: string; signal?: AbortSignal },\n ): Promise<ApplicationCreateResponse> =>\n postEmbed(buildEmbedUrl(this.embedBaseUrl, \"/applications\"), this.apiKey, payload, opts),\n };\n\n readonly waivers = {\n /** Get the active waiver for an org for the current year. */\n active: (orgId: string, opts?: { signal?: AbortSignal }): Promise<Waiver> =>\n getEmbed(buildEmbedUrl(this.embedBaseUrl, `/waivers/${orgId}/active`), this.apiKey, opts?.signal),\n\n /** Sign a waiver. Requires X-Camp-Key; optional accessToken for Bearer path. */\n sign: (\n payload: WaiverSignPayload,\n opts?: { accessToken?: string; signal?: AbortSignal },\n ): Promise<WaiverSignResponse> =>\n postEmbed(buildEmbedUrl(this.embedBaseUrl, \"/waivers/sign\"), this.apiKey, payload, opts),\n\n /** Has this applicant/member signed the active waiver? Anchor: applicationId query or Bearer accessToken. */\n status: (\n orgId: string,\n opts?: { applicationId?: string; accessToken?: string; signal?: AbortSignal },\n ): Promise<WaiverStatus> => {\n const query = opts?.applicationId ? `?applicationId=${encodeURIComponent(opts.applicationId)}` : \"\";\n return getEmbedAuthed(buildEmbedUrl(this.embedBaseUrl, `/waivers/${orgId}/status${query}`), this.apiKey, opts);\n },\n\n /** Get a short-lived download URL for a completed signature's PDF. Anchor: applicationId query or Bearer. */\n signedPdf: (\n orgId: string,\n signatureId: string,\n opts?: { applicationId?: string; accessToken?: string; signal?: AbortSignal },\n ): Promise<{ url: string }> => {\n const query = opts?.applicationId ? `?applicationId=${encodeURIComponent(opts.applicationId)}` : \"\";\n return getEmbedAuthed(\n buildEmbedUrl(this.embedBaseUrl, `/waivers/${orgId}/signatures/${encodeURIComponent(signatureId)}/pdf${query}`),\n this.apiKey,\n opts,\n );\n },\n };\n\n readonly dues = {\n list: (params?: { userId?: string; year?: number }, opts?: { signal?: AbortSignal }): Promise<DuesStatus[]> =>\n request(buildUrl(this.baseUrl, \"/dues\", params), this.apiKey, opts?.signal),\n };\n\n readonly shifts = {\n list: (\n params?: { year?: number; fromDate?: string; toDate?: string; publishedOnly?: boolean },\n opts?: { signal?: AbortSignal },\n ): Promise<Shift[]> => request(buildUrl(this.baseUrl, \"/shifts\", params), this.apiKey, opts?.signal),\n };\n\n readonly org = {\n get: (opts?: { signal?: AbortSignal }): Promise<OrgConfig> =>\n request(buildUrl(this.baseUrl, \"/org\"), this.apiKey, opts?.signal),\n };\n\n readonly payments = {\n page: (\n memberId: string,\n duesCollectionId?: string,\n opts?: { signal?: AbortSignal },\n ): Promise<PaymentPageResponse> =>\n post(buildUrl(this.baseUrl, \"/payments/page\"), this.apiKey, { memberId, duesCollectionId }, opts?.signal),\n };\n\n // Admin write APIs (PLA-489). Each method requires `accessToken` — a Supabase\n // JWT for a camp admin/super_admin. The org boundary is enforced server-side\n // from the JWT claims; these endpoints do not use the X-Camp-Key.\n readonly admin = {\n applications: {\n /** Get application audit timeline (PLA-712). Requires admin Bearer token. */\n history: (id: string, opts: { accessToken: string; signal?: AbortSignal }): Promise<ApplicationHistoryResponse> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/history`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n transition: (\n id: string,\n payload: ApplicationTransitionPayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationTransitionResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n getAnnotations: (\n id: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationAnnotations> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/annotations`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n addNote: (\n id: string,\n payload: { body: string },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationNote> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/notes`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n setTags: (\n id: string,\n payload: { tags: string[] },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<string[]> =>\n adminEmbed(\n \"PUT\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/tags`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n setScore: (\n id: string,\n payload: { value: number },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationScore> =>\n adminEmbed(\n \"PUT\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/scores`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n givebutter: {\n campaigns: {\n list: (opts: { accessToken: string; signal?: AbortSignal }): Promise<unknown[]> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, \"/admin/givebutter/campaigns\"),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n discountCodes: {\n list: (campaignId: number, opts: { accessToken: string; signal?: AbortSignal }): Promise<unknown[]> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/givebutter/campaigns/${campaignId}/discount-codes`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n create: (\n campaignId: number,\n payload: { code: string; type: string; amount: number; active?: boolean },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<unknown> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/givebutter/campaigns/${campaignId}/discount-codes`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n tickets: {\n create: (\n campaignId: number,\n payload: { name: string; price: number; totalQuantity?: number },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<unknown> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/givebutter/campaigns/${campaignId}/tickets`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n },\n },\n members: {\n create: (\n payload: MemberCreatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<MemberCreateResponse> =>\n adminEmbed(\"POST\", buildEmbedUrl(this.embedBaseUrl, \"/admin/members\"), opts.accessToken, payload, opts.signal),\n\n update: (\n id: string,\n patch: MemberUpdatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<MemberUpdateResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/members/${encodeURIComponent(id)}`),\n opts.accessToken,\n patch,\n opts.signal,\n ),\n\n deactivate: (\n id: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<MemberDeactivateResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/members/${encodeURIComponent(id)}`),\n opts.accessToken,\n { status: \"inactive\" },\n opts.signal,\n ),\n },\n announcements: {\n /** Send an email blast to camp members (PLA-706). Requires admin Bearer token. */\n send: (\n payload: AnnouncementSendPayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<AnnouncementSendResponse> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, \"/admin/announcements/send\"),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n dues: {\n /** Create a manual dues/payment record (PLA-708). Requires admin Bearer token. */\n create: (\n payload: DuesCreatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<DuesCreateResponse> =>\n adminEmbed(\"POST\", buildEmbedUrl(this.embedBaseUrl, \"/admin/dues\"), opts.accessToken, payload, opts.signal),\n\n /** Update a dues/payment record (PLA-708). Requires admin Bearer token. */\n update: (\n id: string,\n payload: DuesUpdatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<DuesUpdateResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/dues/${encodeURIComponent(id)}`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n org: {\n /** Update camp configuration settings (PLA-709). Requires admin Bearer token. */\n update: (\n payload: OrgUpdatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<OrgUpdateResponse> =>\n adminEmbed(\"PATCH\", buildEmbedUrl(this.embedBaseUrl, \"/admin/org\"), opts.accessToken, payload, opts.signal),\n },\n scheduling: {\n availability: {\n /** Get interview availability for an interviewer (PLA-713). Requires admin Bearer token. */\n get: (\n interviewerId: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<SetAvailabilityResponse> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/availability/${encodeURIComponent(interviewerId)}`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n /** Set interview availability for an interviewer (PLA-713). Requires admin Bearer token. */\n set: (\n interviewerId: string,\n payload: SetAvailabilityInput,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<SetAvailabilityResponse> =>\n adminEmbed(\n \"PUT\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/availability/${encodeURIComponent(interviewerId)}`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n bookingTokens: {\n /** Create a booking token for an application's interview link (PLA-714). Requires admin Bearer token. */\n create: (\n payload: CreateBookingTokenInput,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<BookingToken> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, \"/admin/booking-tokens\"),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n /** Get an existing booking token for an application (PLA-714). Requires admin Bearer token. */\n get: (applicationId: string, opts: { accessToken: string; signal?: AbortSignal }): Promise<BookingToken> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/booking-tokens/${encodeURIComponent(applicationId)}`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n },\n },\n };\n\n // Public embed-booking endpoints (PLA-714). No auth — these are the\n // applicant-facing schedule surface using the hashed booking token.\n readonly schedule = {\n /** List available interview slots for a booking token (PLA-714). Public. */\n getSlots: (\n token: string,\n opts?: { signal?: AbortSignal },\n ): Promise<{ slots: { startsAt: string; endsAt: string }[] }> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}`;\n return fetch(url, { signal: opts?.signal }).then((res) => {\n if (!res.ok) throw parseError(res);\n return res.json() as Promise<{ slots: { startsAt: string; endsAt: string }[] }>;\n });\n },\n /** Book an interview slot (PLA-714). Public. */\n book: (token: string, payload: BookSlotInput, opts?: { signal?: AbortSignal }): Promise<BookSlotResponse> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}/book`;\n return fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: opts?.signal,\n }).then((res) => {\n if (!res.ok) throw parseError(res);\n return res.json() as Promise<BookSlotResponse>;\n });\n },\n /** Cancel an interview booking (PLA-714). Public. */\n cancel: (token: string, opts?: { signal?: AbortSignal }): Promise<BookSlotResponse> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}/cancel`;\n return fetch(url, { method: \"POST\", signal: opts?.signal }).then((res) => {\n if (!res.ok) throw parseError(res);\n return res.json() as Promise<BookSlotResponse>;\n });\n },\n /** Reschedule an interview booking (PLA-714). Public. */\n reschedule: (\n token: string,\n payload: RescheduleInput,\n opts?: { signal?: AbortSignal },\n ): Promise<BookSlotResponse> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}/reschedule`;\n return fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: opts?.signal,\n }).then((res) => {\n if (!res.ok) throw parseError(res);\n return res.json() as Promise<BookSlotResponse>;\n });\n },\n };\n}\n\n/** Convenience factory — equivalent to `new PlayaOSClient(opts)`. */\nexport function createClient(opts: ClientOptions): PlayaOSClient {\n return new PlayaOSClient(opts);\n}\n","/**\n * Server-side OAuth-code → ID-token exchange.\n *\n * Swaps a one-time authorization code from the PlayaOS IdP for a short-lived\n * ID token. Confidential clients pass `clientSecret`, which is sent to the\n * token endpoint server-side. Public/PKCE-only clients omit it — the\n * authorization-code + code_verifier pair is the sole credential. Public\n * clients can also exchange directly from the browser via `usePlayaOSAuth`\n * in `@playaos/react`.\n */\n\nexport interface ExchangeParams {\n authBaseUrl?: string;\n code: string;\n codeVerifier: string;\n clientId: string;\n /** Confidential clients only. Omit for public/PKCE-only clients. */\n clientSecret?: string;\n redirectUri: string;\n}\n\nexport interface ExchangeResult {\n idToken: string;\n expiresAt: string;\n /** Confidential clients only. Undefined for public clients. */\n refreshToken?: string;\n}\n\nexport async function exchangeCode(params: ExchangeParams): Promise<ExchangeResult> {\n const base = params.authBaseUrl ?? \"https://auth.playaos.app\";\n // A confidential client is identified by passing clientSecret at all — even an\n // empty string. We forward it as-is so the server validates it (an empty or\n // wrong secret is rejected there); public clients omit the field entirely.\n const isConfidential = params.clientSecret !== undefined;\n const res = await fetch(`${base}/api/auth/v1/exchange`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n code: params.code,\n code_verifier: params.codeVerifier,\n client_id: params.clientId,\n ...(isConfidential ? { client_secret: params.clientSecret } : {}),\n redirect_uri: params.redirectUri,\n }),\n });\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n throw new Error(`Exchange failed: non-JSON response (${res.status})`);\n }\n\n if (!res.ok) {\n const errorReason =\n typeof body === \"object\" && body !== null && \"error\" in body && typeof body.error === \"string\"\n ? body.error\n : String(res.status);\n throw new Error(`Exchange failed: ${errorReason}`);\n }\n\n if (\n typeof body !== \"object\" ||\n body === null ||\n !(\"idToken\" in body) ||\n typeof body.idToken !== \"string\" ||\n !(\"expiresAt\" in body) ||\n typeof body.expiresAt !== \"string\"\n ) {\n throw new Error(\"Exchange failed: malformed success response\");\n }\n\n // The server mints a refresh token only for confidential clients (it sets\n // `withRefreshToken: client.type === \"confidential\"`); public clients get\n // none. Surface it when present, otherwise leave it undefined.\n const refreshToken = \"refreshToken\" in body && typeof body.refreshToken === \"string\" ? body.refreshToken : undefined;\n\n // A confidential client that gets no refresh token means the server response\n // is malformed or the rotation flow is broken — surface it rather than\n // silently returning a result the caller can't refresh.\n if (isConfidential && refreshToken === undefined) {\n throw new Error(\"Exchange failed: confidential client response missing refreshToken\");\n }\n\n return {\n idToken: body.idToken,\n expiresAt: body.expiresAt,\n ...(refreshToken !== undefined ? { refreshToken } : {}),\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/error.ts","../src/client.ts","../src/exchange.ts","../src/public-marketplace.ts"],"sourcesContent":["export class ApiClientError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n /**\n * Seconds to wait before retrying, parsed from a numeric `Retry-After`\n * response header when the server sends one (commonly on 429 and 503).\n * Undefined when the header is absent or in HTTP-date form.\n */\n public readonly retryAfter?: number,\n ) {\n super(message);\n this.name = \"ApiClientError\";\n }\n}\n\n/**\n * Parse the `Retry-After` header into integer seconds. Only the delta-seconds\n * form is honored; the HTTP-date form is ignored (returns undefined) since\n * callers expect a seconds hint.\n */\nfunction parseRetryAfter(res: Response): number | undefined {\n const header = res.headers?.get?.(\"retry-after\")?.trim();\n return header && /^\\d+$/.test(header) ? parseInt(header, 10) : undefined;\n}\n\nexport async function parseError(res: Response): Promise<ApiClientError> {\n const retryAfter = parseRetryAfter(res);\n try {\n const body = (await res.json()) as { error?: string; code?: string };\n return new ApiClientError(res.status, body.code ?? \"UNKNOWN\", body.error ?? res.statusText, retryAfter);\n } catch {\n return new ApiClientError(res.status, \"UNKNOWN\", res.statusText, retryAfter);\n }\n}\n","import { type ApiClientError, parseError } from \"./error.js\";\nimport type {\n AnnouncementSendPayload,\n AnnouncementSendResponse,\n Application,\n ApplicationAnnotations,\n ApplicationCreatePayload,\n ApplicationCreateResponse,\n ApplicationHistoryResponse,\n ApplicationNote,\n ApplicationScore,\n ApplicationStatus,\n ApplicationTransitionPayload,\n ApplicationTransitionResponse,\n BookingToken,\n BookSlotInput,\n BookSlotResponse,\n CalendarStatus,\n CommitmentsListResponse,\n CreateBookingTokenInput,\n DuesCreatePayload,\n DuesCreateResponse,\n DuesStatus,\n DuesUpdatePayload,\n DuesUpdateResponse,\n GoogleCalendar,\n Member,\n MemberCreatePayload,\n MemberCreateResponse,\n MemberDeactivateResponse,\n MemberRole,\n MemberUpdatePayload,\n MemberUpdateResponse,\n OrgConfig,\n OrgUpdatePayload,\n OrgUpdateResponse,\n Paginated,\n PaginationParams,\n PaymentPageResponse,\n RescheduleInput,\n SelectCalendarInput,\n SetAvailabilityInput,\n SetAvailabilityResponse,\n Shift,\n Waiver,\n WaiverSignPayload,\n WaiverSignResponse,\n WaiverStatus,\n} from \"./types.js\";\n\nexport type { ApiClientError };\n\ninterface ClientOptions {\n /** Base URL for authenticated /api/v1 routes, e.g. https://api.playaos.app */\n baseUrl: string;\n /** Optional base URL for /api/embed/v1 routes when they live on a different host. */\n embedBaseUrl?: string;\n /** API key in the format pk_live_* */\n apiKey: string;\n}\n\nfunction buildUrl(base: string, path: string, params?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(`${base}/api/v1${path}`);\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n // Skip undefined and false booleans — z.coerce.boolean() on the server uses Boolean(),\n // which treats any non-empty string (including \"false\") as true. Omitting a false boolean\n // param has the same effect as the server default (unfiltered).\n if (v !== undefined && v !== false) url.searchParams.set(k, String(v));\n }\n }\n return url.toString();\n}\n\nfunction buildEmbedUrl(base: string, path: string): string {\n return `${base}/api/embed/v1${path}`;\n}\n\nfunction withCampKey(url: string, campKey: string): string {\n const embedUrl = new URL(url);\n embedUrl.searchParams.set(\"camp_key\", campKey);\n return embedUrl.toString();\n}\n\nasync function getEmbed<T>(url: string, campKey: string, signal?: AbortSignal): Promise<T> {\n const res = await fetch(withCampKey(url, campKey), {\n headers: { \"X-Camp-Key\": campKey },\n signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function getEmbedAuthed<T>(\n url: string,\n campKey: string,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = { \"X-Camp-Key\": campKey };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(withCampKey(url, campKey), { headers, signal: opts?.signal });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function request<T>(url: string, apiKey: string, signal?: AbortSignal): Promise<T> {\n const res = await fetch(url, {\n headers: { Authorization: `Bearer ${apiKey}` },\n signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function post<T>(url: string, apiKey: string, body: unknown, signal?: AbortSignal): Promise<T> {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${apiKey}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function postEmbed<T>(\n url: string,\n campKey: string,\n body: unknown,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = {\n \"X-Camp-Key\": campKey,\n \"Content-Type\": \"application/json\",\n };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(withCampKey(url, campKey), {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: opts?.signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function putEmbed<T>(\n url: string,\n campKey: string,\n body: unknown,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = {\n \"X-Camp-Key\": campKey,\n \"Content-Type\": \"application/json\",\n };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(withCampKey(url, campKey), {\n method: \"PUT\",\n headers,\n body: JSON.stringify(body),\n signal: opts?.signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nasync function deleteEmbed<T>(\n url: string,\n campKey: string,\n opts?: { accessToken?: string; signal?: AbortSignal },\n): Promise<T> {\n const headers: Record<string, string> = { \"X-Camp-Key\": campKey };\n if (opts?.accessToken) headers.Authorization = `Bearer ${opts.accessToken}`;\n const res = await fetch(withCampKey(url, campKey), { method: \"DELETE\", headers, signal: opts?.signal });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\n/**\n * Bearer-only helper for /api/embed/v1/admin/* (PLA-489). The admin token is\n * the sole credential (no X-Camp-Key — orgId comes from the JWT claims\n * server-side). `method` can be GET, POST, PATCH, or PUT; `body` is optional\n * (omitted for GET reads like getAnnotations).\n */\nasync function adminEmbed<T>(\n method: \"GET\" | \"POST\" | \"PATCH\" | \"PUT\",\n url: string,\n accessToken: string,\n body?: unknown,\n signal?: AbortSignal,\n): Promise<T> {\n const headers: Record<string, string> = { Authorization: `Bearer ${accessToken}` };\n const init: RequestInit = { method, headers, signal };\n if (body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n init.body = JSON.stringify(body);\n }\n const res = await fetch(url, init);\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n}\n\nexport class PlayaOSClient {\n private readonly baseUrl: string;\n private readonly embedBaseUrl: string;\n private readonly apiKey: string;\n\n constructor(opts: ClientOptions) {\n this.baseUrl = opts.baseUrl.replace(/\\/+$/, \"\");\n this.embedBaseUrl = (opts.embedBaseUrl ?? opts.baseUrl).replace(/\\/+$/, \"\");\n this.apiKey = opts.apiKey;\n }\n\n readonly members = {\n list: (\n params?: { role?: MemberRole; status?: string } & PaginationParams,\n opts?: { signal?: AbortSignal },\n ): Promise<Paginated<Member>> => request(buildUrl(this.baseUrl, \"/members\", params), this.apiKey, opts?.signal),\n\n get: (id: string, opts?: { signal?: AbortSignal }): Promise<Member> =>\n request(buildUrl(this.baseUrl, `/members/${encodeURIComponent(id)}`), this.apiKey, opts?.signal),\n\n /** Member-self profile update (PLA-700). Requires camp key + member Bearer token. */\n selfUpdate: (\n payload: {\n bio?: string | null;\n playaName?: string | null;\n socialMedia?: Record<string, unknown> | null;\n showSocialsInDirectory?: boolean;\n phone?: string | null;\n emergencyContactName?: string | null;\n emergencyContactRelationship?: string | null;\n emergencyContactPhone?: string | null;\n profilePhotoPath?: string | null;\n yearJoined?: number | null;\n },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ profileId: string }> =>\n putEmbed(buildEmbedUrl(this.embedBaseUrl, \"/members/self\"), this.apiKey, payload, opts),\n\n /** Member-self shift signup + cancel (PLA-705). Requires camp key + member Bearer. */\n shifts: {\n signup: (\n payload: { shiftId: string; role?: string },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ signupId: string; waitlisted: boolean }> =>\n putEmbed(buildEmbedUrl(this.embedBaseUrl, \"/shifts/signup\"), this.apiKey, payload, opts),\n cancel: (\n signupId: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ canceled: boolean; promotedSignupId: string | null }> =>\n deleteEmbed(\n buildEmbedUrl(this.embedBaseUrl, `/shifts/signups/${encodeURIComponent(signupId)}`),\n this.apiKey,\n opts,\n ),\n },\n };\n\n readonly applications = {\n list: (\n params?: { status?: ApplicationStatus; year?: number } & PaginationParams,\n opts?: { signal?: AbortSignal },\n ): Promise<Paginated<Application>> =>\n request(buildUrl(this.baseUrl, \"/applications\", params), this.apiKey, opts?.signal),\n\n // Anonymous external-camp submission against POST /api/embed/v1/applications\n // (PLA-481). Uses the X-Camp-Key header rather than Bearer auth. Pass\n // `accessToken` to additionally send a PlayaOS IdP JWT (PLA-573 path) so\n // the server trusts the verified profile claim and skips the email-based\n // bootstrap.\n create: (\n payload: ApplicationCreatePayload,\n opts?: { accessToken?: string; signal?: AbortSignal },\n ): Promise<ApplicationCreateResponse> =>\n postEmbed(buildEmbedUrl(this.embedBaseUrl, \"/applications\"), this.apiKey, payload, opts),\n };\n\n readonly waivers = {\n /** Get the active waiver for an org for the current year. */\n active: (orgId: string, opts?: { signal?: AbortSignal }): Promise<Waiver> =>\n getEmbed(buildEmbedUrl(this.embedBaseUrl, `/waivers/${orgId}/active`), this.apiKey, opts?.signal),\n\n /** Sign a waiver. Requires X-Camp-Key; optional accessToken for Bearer path. */\n sign: (\n payload: WaiverSignPayload,\n opts?: { accessToken?: string; signal?: AbortSignal },\n ): Promise<WaiverSignResponse> =>\n postEmbed(buildEmbedUrl(this.embedBaseUrl, \"/waivers/sign\"), this.apiKey, payload, opts),\n\n /** Has this applicant/member signed the active waiver? Anchor: applicationId query or Bearer accessToken. */\n status: (\n orgId: string,\n opts?: { applicationId?: string; accessToken?: string; signal?: AbortSignal },\n ): Promise<WaiverStatus> => {\n const query = opts?.applicationId ? `?applicationId=${encodeURIComponent(opts.applicationId)}` : \"\";\n return getEmbedAuthed(buildEmbedUrl(this.embedBaseUrl, `/waivers/${orgId}/status${query}`), this.apiKey, opts);\n },\n\n /** Get a short-lived download URL for a completed signature's PDF. Anchor: applicationId query or Bearer. */\n signedPdf: (\n orgId: string,\n signatureId: string,\n opts?: { applicationId?: string; accessToken?: string; signal?: AbortSignal },\n ): Promise<{ url: string }> => {\n const query = opts?.applicationId ? `?applicationId=${encodeURIComponent(opts.applicationId)}` : \"\";\n return getEmbedAuthed(\n buildEmbedUrl(this.embedBaseUrl, `/waivers/${orgId}/signatures/${encodeURIComponent(signatureId)}/pdf${query}`),\n this.apiKey,\n opts,\n );\n },\n };\n\n readonly dues = {\n list: (\n params?: { userId?: string; year?: number; paymentStatus?: \"unpaid\" | \"partial\" | \"paid\" } & PaginationParams,\n opts?: { signal?: AbortSignal },\n ): Promise<Paginated<DuesStatus>> => request(buildUrl(this.baseUrl, \"/dues\", params), this.apiKey, opts?.signal),\n };\n\n readonly shifts = {\n list: (\n params?: {\n year?: number;\n fromDate?: string;\n toDate?: string;\n publishedOnly?: boolean;\n openOnly?: boolean;\n } & PaginationParams,\n opts?: { signal?: AbortSignal },\n ): Promise<Paginated<Shift>> => request(buildUrl(this.baseUrl, \"/shifts\", params), this.apiKey, opts?.signal),\n };\n\n readonly org = {\n get: (opts?: { signal?: AbortSignal }): Promise<OrgConfig> =>\n request(buildUrl(this.baseUrl, \"/org\"), this.apiKey, opts?.signal),\n };\n\n // Public embed surface: active commitment definitions for the org's current\n // year, so the <ApplicationForm> can render + validate them (PLA-682). Uses\n // the X-Camp-Key (the apiKey doubles as the camp key), same as applications.create.\n readonly commitments = {\n list: (opts?: { signal?: AbortSignal }): Promise<CommitmentsListResponse> =>\n getEmbed(buildEmbedUrl(this.embedBaseUrl, \"/commitments\"), this.apiKey, opts?.signal),\n };\n\n readonly payments = {\n page: (\n memberId: string,\n duesCollectionId?: string,\n opts?: { signal?: AbortSignal },\n ): Promise<PaymentPageResponse> =>\n post(buildUrl(this.baseUrl, \"/payments/page\"), this.apiKey, { memberId, duesCollectionId }, opts?.signal),\n };\n\n // Admin write APIs (PLA-489). Each method requires `accessToken` — a Supabase\n // JWT for a camp admin/super_admin. The org boundary is enforced server-side\n // from the JWT claims; these endpoints do not use the X-Camp-Key.\n readonly admin = {\n applications: {\n /** Get application audit timeline (PLA-712). Requires admin Bearer token. */\n history: (id: string, opts: { accessToken: string; signal?: AbortSignal }): Promise<ApplicationHistoryResponse> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/history`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n transition: (\n id: string,\n payload: ApplicationTransitionPayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationTransitionResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n getAnnotations: (\n id: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationAnnotations> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/annotations`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n addNote: (\n id: string,\n payload: { body: string },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationNote> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/notes`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n setTags: (\n id: string,\n payload: { tags: string[] },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<string[]> =>\n adminEmbed(\n \"PUT\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/tags`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n setScore: (\n id: string,\n payload: { value: number },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<ApplicationScore> =>\n adminEmbed(\n \"PUT\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/applications/${encodeURIComponent(id)}/scores`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n givebutter: {\n campaigns: {\n list: (opts: { accessToken: string; signal?: AbortSignal }): Promise<unknown[]> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, \"/admin/givebutter/campaigns\"),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n discountCodes: {\n list: (campaignId: number, opts: { accessToken: string; signal?: AbortSignal }): Promise<unknown[]> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/givebutter/campaigns/${campaignId}/discount-codes`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n create: (\n campaignId: number,\n payload: { code: string; value: number; type: \"percent\" | \"fixed\"; expirationDate?: string },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<unknown> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/givebutter/campaigns/${campaignId}/discount-codes`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n tickets: {\n create: (\n campaignId: number,\n payload: { name: string; price: number; totalQuantity?: number },\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<unknown> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/givebutter/campaigns/${campaignId}/tickets`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n },\n },\n members: {\n create: (\n payload: MemberCreatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<MemberCreateResponse> =>\n adminEmbed(\"POST\", buildEmbedUrl(this.embedBaseUrl, \"/admin/members\"), opts.accessToken, payload, opts.signal),\n\n update: (\n id: string,\n patch: MemberUpdatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<MemberUpdateResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/members/${encodeURIComponent(id)}`),\n opts.accessToken,\n patch,\n opts.signal,\n ),\n\n deactivate: (\n id: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<MemberDeactivateResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/members/${encodeURIComponent(id)}`),\n opts.accessToken,\n { status: \"inactive\" },\n opts.signal,\n ),\n },\n announcements: {\n /** Send an email blast to camp members (PLA-706). Requires admin Bearer token. */\n send: (\n payload: AnnouncementSendPayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<AnnouncementSendResponse> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, \"/admin/announcements/send\"),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n dues: {\n /** Create a manual dues/payment record (PLA-708). Requires admin Bearer token. */\n create: (\n payload: DuesCreatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<DuesCreateResponse> =>\n adminEmbed(\"POST\", buildEmbedUrl(this.embedBaseUrl, \"/admin/dues\"), opts.accessToken, payload, opts.signal),\n\n /** Update a dues/payment record (PLA-708). Requires admin Bearer token. */\n update: (\n id: string,\n payload: DuesUpdatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<DuesUpdateResponse> =>\n adminEmbed(\n \"PATCH\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/dues/${encodeURIComponent(id)}`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n org: {\n /** Update camp configuration settings (PLA-709). Requires admin Bearer token. */\n update: (\n payload: OrgUpdatePayload,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<OrgUpdateResponse> =>\n adminEmbed(\"PATCH\", buildEmbedUrl(this.embedBaseUrl, \"/admin/org\"), opts.accessToken, payload, opts.signal),\n },\n scheduling: {\n availability: {\n /** Get interview availability for an interviewer (PLA-713). Requires admin Bearer token. */\n get: (\n interviewerId: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<SetAvailabilityResponse> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/availability/${encodeURIComponent(interviewerId)}`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n /** Set interview availability for an interviewer (PLA-713). Requires admin Bearer token. */\n set: (\n interviewerId: string,\n payload: SetAvailabilityInput,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<SetAvailabilityResponse> =>\n adminEmbed(\n \"PUT\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/availability/${encodeURIComponent(interviewerId)}`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n },\n calendar: {\n /** Get Google Calendar connection status for an interviewer (PLA-726). Requires admin Bearer. */\n status: (interviewerId: string, opts: { accessToken: string; signal?: AbortSignal }): Promise<CalendarStatus> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/status`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n /** List available Google Calendars (PLA-726). Requires admin Bearer. */\n listCalendars: (\n interviewerId: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ calendars: GoogleCalendar[] }> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(\n this.embedBaseUrl,\n `/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/calendars`,\n ),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n /** Select which Google Calendar to use (PLA-726). Requires admin Bearer. */\n select: (\n interviewerId: string,\n payload: SelectCalendarInput,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ calendarId: string }> =>\n adminEmbed(\n \"PUT\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/select`),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n /** Disconnect Google Calendar from interviewer (PLA-726). Requires admin Bearer. */\n disconnect: (\n interviewerId: string,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<{ disconnected: boolean }> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(\n this.embedBaseUrl,\n `/admin/scheduling/calendar/${encodeURIComponent(interviewerId)}/disconnect`,\n ),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n },\n bookingTokens: {\n /** Create a booking token for an application's interview link (PLA-714). Requires admin Bearer token. */\n create: (\n payload: CreateBookingTokenInput,\n opts: { accessToken: string; signal?: AbortSignal },\n ): Promise<BookingToken> =>\n adminEmbed(\n \"POST\",\n buildEmbedUrl(this.embedBaseUrl, \"/admin/booking-tokens\"),\n opts.accessToken,\n payload,\n opts.signal,\n ),\n /** Get an existing booking token for an application (PLA-714). Requires admin Bearer token. */\n get: (applicationId: string, opts: { accessToken: string; signal?: AbortSignal }): Promise<BookingToken> =>\n adminEmbed(\n \"GET\",\n buildEmbedUrl(this.embedBaseUrl, `/admin/booking-tokens/${encodeURIComponent(applicationId)}`),\n opts.accessToken,\n undefined,\n opts.signal,\n ),\n },\n },\n };\n\n // Public embed-booking endpoints (PLA-714). No auth — these are the\n // applicant-facing schedule surface using the hashed booking token.\n readonly schedule = {\n /** List available interview slots for a booking token (PLA-714). Public. */\n getSlots: (\n token: string,\n opts?: { signal?: AbortSignal },\n ): Promise<{ slots: { startsAt: string; endsAt: string }[] }> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}`;\n return fetch(url, { signal: opts?.signal }).then(async (res) => {\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<{ slots: { startsAt: string; endsAt: string }[] }>;\n });\n },\n /** Book an interview slot (PLA-714). Public. */\n book: (token: string, payload: BookSlotInput, opts?: { signal?: AbortSignal }): Promise<BookSlotResponse> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}/book`;\n return fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: opts?.signal,\n }).then(async (res) => {\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<BookSlotResponse>;\n });\n },\n /** Cancel an interview booking (PLA-714). Public. */\n cancel: (token: string, opts?: { signal?: AbortSignal }): Promise<BookSlotResponse> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}/cancel`;\n return fetch(url, { method: \"POST\", signal: opts?.signal }).then(async (res) => {\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<BookSlotResponse>;\n });\n },\n /** Reschedule an interview booking (PLA-714). Public. */\n reschedule: (\n token: string,\n payload: RescheduleInput,\n opts?: { signal?: AbortSignal },\n ): Promise<BookSlotResponse> => {\n const url = `${this.embedBaseUrl}/api/embed/v1/schedule/${encodeURIComponent(token)}/reschedule`;\n return fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: opts?.signal,\n }).then(async (res) => {\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<BookSlotResponse>;\n });\n },\n };\n}\n\n/** Convenience factory — equivalent to `new PlayaOSClient(opts)`. */\nexport function createClient(opts: ClientOptions): PlayaOSClient {\n return new PlayaOSClient(opts);\n}\n","/**\n * Server-side OAuth-code → ID-token exchange.\n *\n * Swaps a one-time authorization code from the PlayaOS IdP for a short-lived\n * ID token. Confidential clients pass `clientSecret`, which is sent to the\n * token endpoint server-side. Public/PKCE-only clients omit it — the\n * authorization-code + code_verifier pair is the sole credential. Public\n * clients can also exchange directly from the browser via `usePlayaOSAuth`\n * in `@playaos/react`.\n */\n\nexport interface ExchangeParams {\n authBaseUrl?: string;\n code: string;\n codeVerifier: string;\n clientId: string;\n /** Confidential clients only. Omit for public/PKCE-only clients. */\n clientSecret?: string;\n redirectUri: string;\n}\n\nexport interface ExchangeResult {\n idToken: string;\n expiresAt: string;\n /** Confidential clients only. Undefined for public clients. */\n refreshToken?: string;\n}\n\nexport async function exchangeCode(params: ExchangeParams): Promise<ExchangeResult> {\n const base = params.authBaseUrl ?? \"https://auth.playaos.app\";\n // A confidential client is identified by passing clientSecret at all — even an\n // empty string. We forward it as-is so the server validates it (an empty or\n // wrong secret is rejected there); public clients omit the field entirely.\n const isConfidential = params.clientSecret !== undefined;\n const res = await fetch(`${base}/api/auth/v1/exchange`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n code: params.code,\n code_verifier: params.codeVerifier,\n client_id: params.clientId,\n ...(isConfidential ? { client_secret: params.clientSecret } : {}),\n redirect_uri: params.redirectUri,\n }),\n });\n let body: unknown;\n try {\n body = await res.json();\n } catch {\n throw new Error(`Exchange failed: non-JSON response (${res.status})`);\n }\n\n if (!res.ok) {\n const errorReason =\n typeof body === \"object\" && body !== null && \"error\" in body && typeof body.error === \"string\"\n ? body.error\n : String(res.status);\n throw new Error(`Exchange failed: ${errorReason}`);\n }\n\n if (\n typeof body !== \"object\" ||\n body === null ||\n !(\"idToken\" in body) ||\n typeof body.idToken !== \"string\" ||\n !(\"expiresAt\" in body) ||\n typeof body.expiresAt !== \"string\"\n ) {\n throw new Error(\"Exchange failed: malformed success response\");\n }\n\n // The server mints a refresh token only for confidential clients (it sets\n // `withRefreshToken: client.type === \"confidential\"`); public clients get\n // none. Surface it when present, otherwise leave it undefined.\n const refreshToken = \"refreshToken\" in body && typeof body.refreshToken === \"string\" ? body.refreshToken : undefined;\n\n // A confidential client that gets no refresh token means the server response\n // is malformed or the rotation flow is broken — surface it rather than\n // silently returning a result the caller can't refresh.\n if (isConfidential && refreshToken === undefined) {\n throw new Error(\"Exchange failed: confidential client response missing refreshToken\");\n }\n\n return {\n idToken: body.idToken,\n expiresAt: body.expiresAt,\n ...(refreshToken !== undefined ? { refreshToken } : {}),\n };\n}\n","import { parseError } from \"./error.js\";\n\n/** A listing holds at most this many images (matches the DB's position 1–6 check). */\nexport const MAX_IMAGE_POSITION = 6;\n\n/**\n * Typed client for the anonymous gift-layer marketplace API (`/api/public/marketplace/*`).\n *\n * Unlike `PlayaOSClient` (api-key auth for `/api/v1`), this surface authenticates\n * writes with a Supabase **Bearer JWT** belonging to a public `global_accounts`\n * user. The token is supplied by an injected `getToken` provider so the same\n * client works from any consumer (the marketing UI supplies the browser\n * Supabase session token; a future surface could supply its own). Keeps the\n * public API as the single write contract — the UI is just another consumer.\n */\nexport interface PublicMarketplaceClientOptions {\n /** API base, e.g. `https://api.playaos.app`. */\n baseUrl: string;\n /** Supplies the Supabase access token (Bearer) for authenticated calls; null/undefined when signed out. */\n getToken: () => Promise<string | null | undefined> | string | null | undefined;\n}\n\nexport interface NewListingInput {\n category: string;\n subcategory?: string;\n title: string;\n body: string;\n price?: number;\n location?: string;\n replyEmail: string;\n}\n\nexport interface ImageUploadTarget {\n /** Supabase signed upload URL — PUT the file bytes here. */\n uploadUrl: string;\n /** Storage path to pass back to `attachImage` once the PUT succeeds. */\n storagePath: string;\n}\n\n/** Moderation flag reasons accepted by the API (mirrors the DB enum). */\nexport type MarketplaceFlagReason = \"spam\" | \"prohibited\" | \"other\";\n\nexport interface ListingImage {\n /** Short-lived signed download URL for the image. */\n url: string;\n /** 1-based display order. */\n position: number;\n}\n\nexport interface PublicMarketplaceClient {\n /**\n * Create a listing as the signed-in account. Throws a plain `Error` when there\n * is no token (signed out), or `ApiClientError` on a non-2xx response.\n */\n createListing(input: NewListingInput, signal?: AbortSignal): Promise<{ id: string }>;\n /** Request a signed upload URL for an image on a listing the caller owns. */\n getImageUploadUrl(listingId: string, signal?: AbortSignal): Promise<ImageUploadTarget>;\n /** Attach an already-uploaded image (by storage path) to a listing at a 1–6 position. */\n attachImage(\n listingId: string,\n input: { storagePath: string; position: number },\n signal?: AbortSignal,\n ): Promise<{ id: string }>;\n /** Anonymous: list an active listing's images as short-lived signed URLs (for public display). */\n listListingImages(listingId: string, signal?: AbortSignal): Promise<{ images: ListingImage[] }>;\n /** Anonymous: batch-fetch first-image signed thumbnail URLs (listingId -> url) for browse cards. */\n listThumbnails(listingIds: string[], signal?: AbortSignal): Promise<{ thumbnails: Record<string, string> }>;\n /** Flag a listing for moderation (signed-in). Idempotent per (listing, user). */\n flagListing(listingId: string, reason: MarketplaceFlagReason, signal?: AbortSignal): Promise<{ ok: boolean }>;\n /** Contact a listing's owner (signed-in); records the reply + queues a notification email. */\n replyToListing(listingId: string, message: string | undefined, signal?: AbortSignal): Promise<{ ok: boolean }>;\n}\n\nexport function createPublicMarketplaceClient(opts: PublicMarketplaceClientOptions): PublicMarketplaceClient {\n const base = opts.baseUrl.replace(/\\/+$/, \"\");\n if (!base) {\n // An empty base would issue relative requests to the caller's own origin.\n throw new Error(\"createPublicMarketplaceClient: baseUrl is required\");\n }\n\n async function authedPost<T>(path: string, body: unknown, signal?: AbortSignal): Promise<T> {\n const token = await opts.getToken();\n if (!token) throw new Error(\"Not signed in\");\n const res = await fetch(`${base}/api/public/marketplace${path}`, {\n method: \"POST\",\n headers: { Authorization: `Bearer ${token}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal,\n });\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n }\n\n async function publicGet<T>(path: string, signal?: AbortSignal): Promise<T> {\n // no-store: responses carry short-lived signed URLs — never let the framework\n // (e.g. Next's fetch cache) memoize them and serve expired URLs. `cache` is a\n // valid fetch option at runtime but absent from @types/node's RequestInit,\n // so the init is typed with an intersection.\n const init: RequestInit & { cache: \"no-store\" } = { signal, cache: \"no-store\" };\n const res = await fetch(`${base}/api/public/marketplace${path}`, init);\n if (!res.ok) throw await parseError(res);\n return res.json() as Promise<T>;\n }\n\n const seg = (listingId: string) => `/listings/${encodeURIComponent(listingId)}`;\n\n return {\n createListing: (input, signal) => authedPost(\"/listings\", input, signal),\n getImageUploadUrl: (listingId, signal) => authedPost(`${seg(listingId)}/image-upload-url`, {}, signal),\n attachImage: (listingId, input, signal) => {\n // Fail fast on a misuse the API would otherwise reject with a vague 400.\n if (!Number.isInteger(input.position) || input.position < 1 || input.position > MAX_IMAGE_POSITION) {\n return Promise.reject(new Error(`attachImage: position must be 1–${MAX_IMAGE_POSITION}`));\n }\n return authedPost(`${seg(listingId)}/images`, input, signal);\n },\n listListingImages: (listingId, signal) => publicGet(`${seg(listingId)}/images`, signal),\n listThumbnails: (listingIds, signal) =>\n publicGet(`/thumbnails?ids=${encodeURIComponent(listingIds.join(\",\"))}`, signal),\n flagListing: (listingId, reason, signal) => authedPost(`${seg(listingId)}/flag`, { reason }, signal),\n replyToListing: (listingId, message, signal) => authedPost(`${seg(listingId)}/reply`, { message }, signal),\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playaos/api-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Typed API client for PlayaOS \u2014 manage camp members, dues, shifts, applications, waivers, and annotations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"tsup": "^8.5.1",
|
|
37
37
|
"typescript": "^6.0.3",
|
|
38
|
-
"vitest": "^4.1.
|
|
38
|
+
"vitest": "^4.1.10",
|
|
39
39
|
"zod": "^4.4.3"
|
|
40
40
|
}
|
|
41
41
|
}
|