@vuoma/sdk 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,11 +10,11 @@ Install the public package in your own application; access to Vuoma's private
10
10
  repository is not required:
11
11
 
12
12
  ```sh
13
- npm install @vuoma/sdk@0.2.0
13
+ npm install @vuoma/sdk@0.2.2
14
14
  ```
15
15
 
16
- Use SvelteKit and SvelteKit UI with `@vuoma/web` for website account components
17
- and same-origin server sessions. Use `@vuoma/native-ui` for React Native account and booking UI and this SDK as
16
+ Use SvelteKit and SvelteKit UI with `@vuoma/svelte` for website account components
17
+ and same-origin server sessions. Use `@vuoma/react-native-ui` for React Native account and booking UI and this SDK as
18
18
  its framework-neutral API transport. The complete `@vuoma/react-native` studio
19
19
  app core remains a private workspace; independent apps do not depend on it.
20
20
  The public setup guide is at `/developers` and `/o/<organization_slug>/developers`
@@ -42,6 +42,11 @@ The organization slug is public routing configuration, built into each studio ap
42
42
  Sign-in authenticates a person for that studio. Private calls require their bearer
43
43
  token; the server verifies the live session, studio membership and permissions.
44
44
  Public schedule and catalog reads need no registration or app key.
45
+ Use `getCatalog({ includeUnavailable: true })` to show published memberships and
46
+ prices before online checkout is enabled. `purchases_available` remains the
47
+ checkout-readiness signal; a displayed offer never authorizes a purchase. This
48
+ option is explicit so existing clients retain their established empty-catalog
49
+ response while checkout is unavailable.
45
50
  Schedule timestamps are normalized to
46
51
  UTC, and ranges must be positive and at most 63 days. The response preserves
47
52
  `schedule.has_more`; when it is true, split the requested range and fetch each
@@ -102,7 +107,8 @@ the response text and against a supplied Content-Length header.
102
107
  ## Signup, activation and password reset
103
108
 
104
109
  Email confirmation and password creation stay within the host UI; the person
105
- reads the six-digit code in their email app. No access token, cookie or API key
110
+ reads the complete code in their email app. The client accepts the supported
111
+ six- and eight-digit codes and preserves leading zeroes. No access token, cookie or API key
106
112
  is sent by these methods. For new or automatically resolved accounts, collect a
107
113
  display name. Choose `auto` to let verified email ownership resolve a new profile,
108
114
  an imported profile or an existing studio account; `activate` performs the same
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuoma/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Framework-neutral client for Vuoma public organization and member account APIs",
5
5
  "type": "module",
6
6
  "private": false,
package/src/index.d.ts CHANGED
@@ -161,7 +161,7 @@ export interface VuomaClient {
161
161
  getBookingCatalog(options?: RequestOptions): Promise<MemberBookingCatalog>
162
162
  /** One half-open range, up to 14 days. Split when has_more is true. */
163
163
  getAppointmentAvailability(options: RequestOptions & { from: string; to: string }): Promise<MemberPublicAvailability>
164
- getCatalog(options?: RequestOptions & { includeCreditPurchases?: boolean }): Promise<MemberPublicCatalog>
164
+ getCatalog(options?: RequestOptions & { includeCreditPurchases?: boolean; includeUnavailable?: boolean }): Promise<MemberPublicCatalog>
165
165
  }
166
166
  readonly auth: {
167
167
  requestAccountCode(options: MemberAccountCodeRequest): Promise<{ ok: true; mode: 'signup' | 'recovery'; requires_email_code: true }>
package/src/index.js CHANGED
@@ -103,8 +103,7 @@ function catalog(value) {
103
103
  envelope(value)
104
104
  if (!timestamp(value.generated_at) || typeof value.purchases_available !== "boolean" ||
105
105
  !object(value.catalog) || !Array.isArray(value.catalog.offers) || value.catalog.offers.length > 100 ||
106
- value.catalog.offers.some(offer => !object(offer) || !uuid(offer.id) || typeof offer.name !== "string" || !Array.isArray(offer.prices) || offer.prices.some(price => !object(price))) ||
107
- (!value.purchases_available && value.catalog.offers.length)) throw invalidResponse()
106
+ value.catalog.offers.some(offer => !object(offer) || !uuid(offer.id) || typeof offer.name !== "string" || !Array.isArray(offer.prices) || offer.prices.some(price => !object(price)))) throw invalidResponse()
108
107
  return value
109
108
  }
110
109
 
@@ -256,8 +255,10 @@ export function createVuomaClient(options) {
256
255
  const range = availabilityRange({ from, to })
257
256
  return checked(await request(`${memberRoot}/availability`, { query: range, signal }), value => validAvailability(value, range))
258
257
  },
259
- async getCatalog({ includeCreditPurchases = false, signal } = {}) {
260
- return catalog(await request(`${memberRoot}/catalog`, { signal, query: includeCreditPurchases ? { include_credit_purchases: "true" } : undefined }))
258
+ async getCatalog({ includeCreditPurchases = false, includeUnavailable = false, signal } = {}) {
259
+ const query = { ...(includeCreditPurchases ? { include_credit_purchases: "true" } : {}),
260
+ ...(includeUnavailable ? { include_unavailable: "true" } : {}) }
261
+ return catalog(await request(`${memberRoot}/catalog`, { signal, query }))
261
262
  },
262
263
  }),
263
264
  auth: Object.freeze({
@@ -90,7 +90,7 @@ export function accountInput({ mode, email, signupIntent }) {
90
90
  if (mode === 'signup') check(['new', 'migrate', 'auto', 'activate'].includes(signupIntent), 'Choose a signup intent explicitly.')
91
91
  }
92
92
  export function displayName(value) { check(text(value, 160, 1) && Boolean(value.trim()) && !/[\u0000-\u001f\u007f]/u.test(value), 'Provide a display name.'); return value }
93
- export function emailCode(value) { check(typeof value === 'string' && /^\d{6}$/u.test(value.trim()), 'Provide the six-digit email code.'); return value.trim() }
93
+ export function emailCode(value) { check(typeof value === 'string' && /^(?:\d{6}|\d{8})$/u.test(value.trim()), 'Provide the email code from your message.'); return value.trim() }
94
94
  export function validCapability(value) { return typeof value === 'string' && /^[A-Za-z0-9_-]{43}$/u.test(value) }
95
95
  export function accountResolution(value) {
96
96
  check(object(value) && ['existing_account', 'existing_profile', 'new_profile'].includes(value.kind) && text(value.display_name, 160, 1) && Boolean(value.display_name.trim()) && !/[\u0000-\u001f\u007f]/u.test(value.display_name) && text(value.masked_email, 320) && /^[^@\s*]\*{3}@[^@\s*]\*{3}(?:\.[a-z0-9.-]+)?$/iu.test(value.masked_email) && (value.masked_phone === null || /^\*{3} \*{3} [0-9]{4}$/u.test(value.masked_phone ?? '')))