@praxium/sdk 0.2.4 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +49 -0
- package/package.json +7 -7
- package/src/errors.ts +0 -126
- package/src/generated/client/client.gen.ts +0 -288
- package/src/generated/client/index.ts +0 -25
- package/src/generated/client/types.gen.ts +0 -213
- package/src/generated/client/utils.gen.ts +0 -316
- package/src/generated/client.gen.ts +0 -16
- package/src/generated/core/auth.gen.ts +0 -41
- package/src/generated/core/bodySerializer.gen.ts +0 -84
- package/src/generated/core/params.gen.ts +0 -169
- package/src/generated/core/pathSerializer.gen.ts +0 -171
- package/src/generated/core/queryKeySerializer.gen.ts +0 -117
- package/src/generated/core/serverSentEvents.gen.ts +0 -243
- package/src/generated/core/types.gen.ts +0 -104
- package/src/generated/core/utils.gen.ts +0 -140
- package/src/generated/index.ts +0 -4
- package/src/generated/sdk.gen.ts +0 -177
- package/src/generated/types.gen.ts +0 -1263
- package/src/index.ts +0 -70
- package/src/revalidation.ts +0 -105
- package/src/tenant-client.ts +0 -180
- package/src/types/next-cache.d.ts +0 -24
package/src/index.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @praxium/sdk — TypeScript SDK for Praxium Public Tenant API
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* ```ts
|
|
6
|
-
* import { createTenantClient, PraxiumNotFoundError } from '@praxium/sdk'
|
|
7
|
-
*
|
|
8
|
-
* const client = createTenantClient({
|
|
9
|
-
* baseUrl: process.env.PRAXIUM_PLATFORM_URL!,
|
|
10
|
-
* apiKey: process.env.PRAXIUM_API_KEY!,
|
|
11
|
-
* tenantSlug: 'ijfysio',
|
|
12
|
-
* locale: 'nl',
|
|
13
|
-
* })
|
|
14
|
-
*
|
|
15
|
-
* // Returns data directly — throws PraxiumError on failure
|
|
16
|
-
* const hours = await client.getOpeningHours()
|
|
17
|
-
* const team = await client.getTeamMembers()
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
// ─── Client Factory ───
|
|
22
|
-
export { createTenantClient } from './tenant-client'
|
|
23
|
-
export type { TenantClient, TenantClientConfig } from './tenant-client'
|
|
24
|
-
|
|
25
|
-
// ─── Error Hierarchy ───
|
|
26
|
-
export {
|
|
27
|
-
PraxiumError,
|
|
28
|
-
PraxiumAuthError,
|
|
29
|
-
PraxiumForbiddenError,
|
|
30
|
-
PraxiumNotFoundError,
|
|
31
|
-
PraxiumRateLimitError,
|
|
32
|
-
PraxiumValidationError,
|
|
33
|
-
} from './errors'
|
|
34
|
-
export type { ValidationDetail } from './errors'
|
|
35
|
-
|
|
36
|
-
// ─── Domain Types (re-exported from generated) ───
|
|
37
|
-
export type {
|
|
38
|
-
// Domain models
|
|
39
|
-
OpeningHours,
|
|
40
|
-
PublicDaySchedule,
|
|
41
|
-
ContactDetails,
|
|
42
|
-
ContactFormResult,
|
|
43
|
-
Location,
|
|
44
|
-
BusinessName,
|
|
45
|
-
SocialLinks,
|
|
46
|
-
TeamMembers,
|
|
47
|
-
PublicTeamMember,
|
|
48
|
-
LocalizedSpecialization,
|
|
49
|
-
FaqContent,
|
|
50
|
-
FaqGroup,
|
|
51
|
-
FaqCategory,
|
|
52
|
-
FaqItem,
|
|
53
|
-
PricingVariants,
|
|
54
|
-
PricingVariant,
|
|
55
|
-
InsuranceList,
|
|
56
|
-
InsuranceInfo,
|
|
57
|
-
FeatureList,
|
|
58
|
-
FeatureItem,
|
|
59
|
-
PaymentMethodList,
|
|
60
|
-
PaymentMethod,
|
|
61
|
-
PolicyList,
|
|
62
|
-
PolicyInfo,
|
|
63
|
-
BookableServices,
|
|
64
|
-
BookableService,
|
|
65
|
-
BookableVariantInfo,
|
|
66
|
-
ServiceCategoryInfo,
|
|
67
|
-
BilingualText,
|
|
68
|
-
// Error type
|
|
69
|
-
ApiError,
|
|
70
|
-
} from './generated/types.gen'
|
package/src/revalidation.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ISR Revalidation Handler for Tenant Websites
|
|
3
|
-
*
|
|
4
|
-
* When admin updates data in the platform, a webhook triggers
|
|
5
|
-
* revalidation on the tenant's public website. This handler
|
|
6
|
-
* validates the secret and calls Next.js revalidatePath().
|
|
7
|
-
*
|
|
8
|
-
* Usage in tenant website's `app/api/revalidate/route.ts`:
|
|
9
|
-
*
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { createRevalidationHandler } from '@praxium/sdk/revalidation'
|
|
12
|
-
*
|
|
13
|
-
* export const POST = createRevalidationHandler({
|
|
14
|
-
* secret: process.env.PRAXIUM_REVALIDATION_SECRET!,
|
|
15
|
-
* })
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
interface RevalidationConfig {
|
|
20
|
-
/** Shared secret for webhook authentication */
|
|
21
|
-
secret: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface RevalidationBody {
|
|
25
|
-
/** Webhook authentication secret */
|
|
26
|
-
secret: string
|
|
27
|
-
/** Paths to revalidate (defaults to ['/'] if omitted) */
|
|
28
|
-
paths?: string[]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Timing-safe string comparison to prevent timing side-channel attacks.
|
|
33
|
-
*
|
|
34
|
-
* Uses XOR-based constant-time comparison with TextEncoder (universally
|
|
35
|
-
* available in all JS environments: Node.js, edge runtimes, browsers).
|
|
36
|
-
* This avoids Node.js-specific APIs (Buffer, crypto.timingSafeEqual) to
|
|
37
|
-
* keep the SDK platform-agnostic.
|
|
38
|
-
*
|
|
39
|
-
* The XOR approach is a well-established cryptographic pattern:
|
|
40
|
-
* - Encodes both strings as UTF-8 byte arrays
|
|
41
|
-
* - XORs every byte pair, accumulating mismatches via bitwise OR
|
|
42
|
-
* - Only checks the accumulated result AFTER the full loop
|
|
43
|
-
* - Prevents attackers from inferring secret content by measuring response times
|
|
44
|
-
*
|
|
45
|
-
* Note: Length difference is detected early (unavoidable information leak),
|
|
46
|
-
* but content comparison is always constant-time.
|
|
47
|
-
*/
|
|
48
|
-
function isSecretValid(provided: string, expected: string): boolean {
|
|
49
|
-
const encoder = new TextEncoder()
|
|
50
|
-
const a = encoder.encode(provided)
|
|
51
|
-
const b = encoder.encode(expected)
|
|
52
|
-
|
|
53
|
-
if (a.length !== b.length) {
|
|
54
|
-
return false
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
let mismatch = 0
|
|
58
|
-
for (let i = 0; i < a.length; i++) {
|
|
59
|
-
mismatch |= a[i] ^ b[i]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return mismatch === 0
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Creates a Next.js route handler that revalidates ISR pages
|
|
67
|
-
* when triggered by the Praxium platform webhook.
|
|
68
|
-
*/
|
|
69
|
-
export function createRevalidationHandler(config: RevalidationConfig) {
|
|
70
|
-
return async (request: Request): Promise<Response> => {
|
|
71
|
-
let body: RevalidationBody
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
body = await request.json()
|
|
75
|
-
} catch {
|
|
76
|
-
return Response.json({ error: 'Invalid JSON body' }, { status: 400 })
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!isSecretValid(body.secret, config.secret)) {
|
|
80
|
-
return Response.json(
|
|
81
|
-
{ error: 'Invalid revalidation secret' },
|
|
82
|
-
{ status: 401 }
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const paths = body.paths ?? ['/']
|
|
87
|
-
|
|
88
|
-
// Dynamic import to avoid bundling next/cache in non-Next.js environments.
|
|
89
|
-
// Type declarations for next/cache are provided by src/types/next-cache.d.ts
|
|
90
|
-
// so the DTS generator can emit correct types without next being installed.
|
|
91
|
-
try {
|
|
92
|
-
const { revalidatePath } = await import('next/cache')
|
|
93
|
-
for (const path of paths) {
|
|
94
|
-
revalidatePath(path)
|
|
95
|
-
}
|
|
96
|
-
} catch {
|
|
97
|
-
return Response.json(
|
|
98
|
-
{ error: 'Revalidation failed — next/cache not available' },
|
|
99
|
-
{ status: 500 }
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return Response.json({ revalidated: true, paths })
|
|
104
|
-
}
|
|
105
|
-
}
|
package/src/tenant-client.ts
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Praxium Tenant Client
|
|
3
|
-
*
|
|
4
|
-
* High-level wrapper around the auto-generated SDK that binds the
|
|
5
|
-
* tenant slug and API key, giving consumers a clean throw-on-error API:
|
|
6
|
-
*
|
|
7
|
-
* ```ts
|
|
8
|
-
* import { createTenantClient, PraxiumNotFoundError } from '@praxium/sdk'
|
|
9
|
-
*
|
|
10
|
-
* const client = createTenantClient({
|
|
11
|
-
* baseUrl: 'https://platform.praxium.nl',
|
|
12
|
-
* apiKey: process.env.PRAXIUM_API_KEY!,
|
|
13
|
-
* tenantSlug: 'ijfysio',
|
|
14
|
-
* })
|
|
15
|
-
*
|
|
16
|
-
* // Returns data directly — throws PraxiumError on failure
|
|
17
|
-
* const hours = await client.getOpeningHours()
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
import { createClient, createConfig } from './generated/client'
|
|
21
|
-
import type {
|
|
22
|
-
ClientOptions,
|
|
23
|
-
ApiError,
|
|
24
|
-
SubmitContactFormData,
|
|
25
|
-
OpeningHours,
|
|
26
|
-
ContactDetails,
|
|
27
|
-
ContactFormResult,
|
|
28
|
-
Location,
|
|
29
|
-
BusinessName,
|
|
30
|
-
SocialLinks,
|
|
31
|
-
TeamMembers,
|
|
32
|
-
FaqContent,
|
|
33
|
-
PricingVariants,
|
|
34
|
-
InsuranceList,
|
|
35
|
-
FeatureList,
|
|
36
|
-
PaymentMethodList,
|
|
37
|
-
PolicyList,
|
|
38
|
-
BookableServices,
|
|
39
|
-
} from './generated/types.gen'
|
|
40
|
-
import * as sdk from './generated/sdk.gen'
|
|
41
|
-
import { PraxiumError, STATUS_ERROR_MAP } from './errors'
|
|
42
|
-
|
|
43
|
-
export interface TenantClientConfig {
|
|
44
|
-
/** Platform base URL (e.g., 'https://platform.praxium.nl') */
|
|
45
|
-
baseUrl: string
|
|
46
|
-
/** HMAC-signed API key (format: hmac_v1_{slug}_{timestamp}_{signature}) */
|
|
47
|
-
apiKey: string
|
|
48
|
-
/** Tenant identifier slug (must match the slug in the API key) */
|
|
49
|
-
tenantSlug: string
|
|
50
|
-
/** Accept-Language header for locale-aware responses (e.g., 'nl', 'en') */
|
|
51
|
-
locale: string
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ─── Unwrap Helper ───────────────────────────────────────────────────
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* The auto-generated SDK returns `{ data, error, request, response }`.
|
|
58
|
-
*
|
|
59
|
-
* On success, `data` is the parsed JSON body which the API wraps as
|
|
60
|
-
* `{ data: <payload> }`. The response-type mapping gives us
|
|
61
|
-
* `{ data: SomeResponse }` for `data`.
|
|
62
|
-
*
|
|
63
|
-
* On error, `error` is the parsed `ApiError` JSON body.
|
|
64
|
-
*
|
|
65
|
-
* `unwrapOrThrow` extracts the inner payload on success, or throws a
|
|
66
|
-
* typed `PraxiumError` subclass mapped from the HTTP status code.
|
|
67
|
-
*/
|
|
68
|
-
function unwrapOrThrow<TPayload>(result: {
|
|
69
|
-
data?: { data: TPayload } | undefined
|
|
70
|
-
error?: ApiError | undefined
|
|
71
|
-
response: Response
|
|
72
|
-
}): TPayload {
|
|
73
|
-
// Success path — return the inner data payload
|
|
74
|
-
if (result.data !== undefined && result.error === undefined) {
|
|
75
|
-
return result.data.data
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// If both data and error are undefined, the generated client encountered
|
|
79
|
-
// a network-level failure; fall through to the error path below.
|
|
80
|
-
|
|
81
|
-
// Error path — map HTTP status to typed error
|
|
82
|
-
const status = result.response?.status ?? 500
|
|
83
|
-
const apiError = result.error as ApiError | undefined
|
|
84
|
-
|
|
85
|
-
const errorCode = apiError?.errorCode ?? 'UNKNOWN_ERROR'
|
|
86
|
-
const message = apiError?.message ?? `HTTP ${status} error`
|
|
87
|
-
const details = apiError?.details
|
|
88
|
-
|
|
89
|
-
const ErrorClass = STATUS_ERROR_MAP[status]
|
|
90
|
-
|
|
91
|
-
if (ErrorClass) {
|
|
92
|
-
throw new ErrorClass(errorCode, message, details)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Fallback for unmapped status codes
|
|
96
|
-
throw new PraxiumError(status, errorCode, message, details)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ─── Client Factory ──────────────────────────────────────────────────
|
|
100
|
-
|
|
101
|
-
export function createTenantClient(config: TenantClientConfig) {
|
|
102
|
-
const clientInstance = createClient(
|
|
103
|
-
createConfig<ClientOptions>({
|
|
104
|
-
baseUrl: config.baseUrl as ClientOptions['baseUrl'],
|
|
105
|
-
auth: config.apiKey,
|
|
106
|
-
})
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
// Set Accept-Language header on all requests for locale-aware responses
|
|
110
|
-
clientInstance.interceptors.request.use((request) => {
|
|
111
|
-
request.headers.set('Accept-Language', config.locale)
|
|
112
|
-
return request
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
const path = { tenantSlug: config.tenantSlug }
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
// ─── Layout Endpoints ───
|
|
119
|
-
getOpeningHours: (): Promise<OpeningHours> =>
|
|
120
|
-
sdk.getOpeningHours({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
121
|
-
|
|
122
|
-
getContactDetails: (): Promise<ContactDetails> =>
|
|
123
|
-
sdk
|
|
124
|
-
.getContactDetails({ client: clientInstance, path })
|
|
125
|
-
.then(unwrapOrThrow),
|
|
126
|
-
|
|
127
|
-
getLocation: (): Promise<Location> =>
|
|
128
|
-
sdk.getLocation({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
129
|
-
|
|
130
|
-
getBusinessName: (): Promise<BusinessName> =>
|
|
131
|
-
sdk.getBusinessName({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
132
|
-
|
|
133
|
-
getSocialLinks: (): Promise<SocialLinks> =>
|
|
134
|
-
sdk.getSocialLinks({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
135
|
-
|
|
136
|
-
// ─── Content Endpoints ───
|
|
137
|
-
getTeamMembers: (): Promise<TeamMembers> =>
|
|
138
|
-
sdk.getTeamMembers({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
139
|
-
|
|
140
|
-
getFaq: (): Promise<FaqContent> =>
|
|
141
|
-
sdk.getFaq({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
142
|
-
|
|
143
|
-
getServiceVariants: (): Promise<PricingVariants> =>
|
|
144
|
-
sdk
|
|
145
|
-
.getServiceVariants({ client: clientInstance, path })
|
|
146
|
-
.then(unwrapOrThrow),
|
|
147
|
-
|
|
148
|
-
getInsuranceInfo: (): Promise<InsuranceList> =>
|
|
149
|
-
sdk
|
|
150
|
-
.getInsuranceInfo({ client: clientInstance, path })
|
|
151
|
-
.then(unwrapOrThrow),
|
|
152
|
-
|
|
153
|
-
getFeatures: (): Promise<FeatureList> =>
|
|
154
|
-
sdk.getFeatures({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
155
|
-
|
|
156
|
-
getPaymentMethods: (): Promise<PaymentMethodList> =>
|
|
157
|
-
sdk
|
|
158
|
-
.getPaymentMethods({ client: clientInstance, path })
|
|
159
|
-
.then(unwrapOrThrow),
|
|
160
|
-
|
|
161
|
-
getPolicyInfo: (): Promise<PolicyList> =>
|
|
162
|
-
sdk.getPolicyInfo({ client: clientInstance, path }).then(unwrapOrThrow),
|
|
163
|
-
|
|
164
|
-
getBookableServices: (): Promise<BookableServices> =>
|
|
165
|
-
sdk
|
|
166
|
-
.getBookableServices({ client: clientInstance, path })
|
|
167
|
-
.then(unwrapOrThrow),
|
|
168
|
-
|
|
169
|
-
// ─── Contact Form ───
|
|
170
|
-
submitContactForm: (
|
|
171
|
-
body: SubmitContactFormData['body']
|
|
172
|
-
): Promise<ContactFormResult> =>
|
|
173
|
-
sdk
|
|
174
|
-
.submitContactForm({ client: clientInstance, path, body })
|
|
175
|
-
.then(unwrapOrThrow),
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/** Return type of createTenantClient for type inference */
|
|
180
|
-
export type TenantClient = ReturnType<typeof createTenantClient>
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type declarations for next/cache — optional peer dependency.
|
|
3
|
-
*
|
|
4
|
-
* next is declared as an optional peerDependency and is NOT installed
|
|
5
|
-
* during SDK CI builds. This stub provides type information for the
|
|
6
|
-
* dynamic import in revalidation.ts so TypeScript's DTS generator
|
|
7
|
-
* can emit correct declarations without requiring next to be installed.
|
|
8
|
-
*
|
|
9
|
-
* Only the functions actually used by the SDK are declared here.
|
|
10
|
-
* Consumers install next themselves (it's a peer dependency).
|
|
11
|
-
*/
|
|
12
|
-
declare module 'next/cache' {
|
|
13
|
-
/**
|
|
14
|
-
* Revalidates data associated with a specific path.
|
|
15
|
-
* @see https://nextjs.org/docs/app/api-reference/functions/revalidatePath
|
|
16
|
-
*/
|
|
17
|
-
export function revalidatePath(path: string, type?: 'page' | 'layout'): void
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Revalidates data associated with a cache tag.
|
|
21
|
-
* @see https://nextjs.org/docs/app/api-reference/functions/revalidateTag
|
|
22
|
-
*/
|
|
23
|
-
export function revalidateTag(tag: string): void
|
|
24
|
-
}
|