order3000 0.7.2 → 0.8.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/dist/client.cjs +248 -7
- package/dist/client.d.cts +222 -16
- package/dist/client.d.mts +222 -16
- package/dist/client.mjs +248 -7
- package/package.json +3 -3
package/dist/client.cjs
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
var
|
|
3
|
+
var supabaseJs = require('@supabase/supabase-js');
|
|
4
|
+
|
|
5
|
+
var __defProp$3 = Object.defineProperty;
|
|
6
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
8
|
class TokenManager {
|
|
7
9
|
constructor(clientId, clientSecret, tokenEndpoint, userVenueId) {
|
|
8
10
|
this.clientId = clientId;
|
|
9
11
|
this.clientSecret = clientSecret;
|
|
10
12
|
this.tokenEndpoint = tokenEndpoint;
|
|
11
13
|
this.userVenueId = userVenueId;
|
|
12
|
-
__publicField$
|
|
13
|
-
__publicField$
|
|
14
|
-
__publicField$
|
|
15
|
-
__publicField$
|
|
14
|
+
__publicField$3(this, "accessToken", null);
|
|
15
|
+
__publicField$3(this, "tokenExpiresAt", 0);
|
|
16
|
+
__publicField$3(this, "refreshPromise", null);
|
|
17
|
+
__publicField$3(this, "defaultVenueId");
|
|
16
18
|
}
|
|
17
19
|
async initialize() {
|
|
18
20
|
await this.refreshToken();
|
|
@@ -172,6 +174,36 @@ class EventsResource extends BaseResource {
|
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
177
|
+
class MenusResource extends BaseResource {
|
|
178
|
+
constructor(tokenManager, baseUrl) {
|
|
179
|
+
super(tokenManager, baseUrl, "/menus");
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get a menu by ID
|
|
183
|
+
* @param id - The menu UUID
|
|
184
|
+
* @param options - Optional expand options
|
|
185
|
+
* @returns The menu with optional items and assets
|
|
186
|
+
*/
|
|
187
|
+
async get(id, options) {
|
|
188
|
+
const queryString = this.buildQueryString(options);
|
|
189
|
+
return this.request("GET", `/${id}${queryString}`);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get a menu by accessor
|
|
193
|
+
* @param accessor - The menu accessor (slug)
|
|
194
|
+
* @param options - Optional expand options
|
|
195
|
+
* @returns The menu with optional items and assets
|
|
196
|
+
*/
|
|
197
|
+
async getByAccessor(accessor, options) {
|
|
198
|
+
const queryString = this.buildQueryString(options);
|
|
199
|
+
return this.request("GET", `/${accessor}${queryString}`);
|
|
200
|
+
}
|
|
201
|
+
buildQueryString(options) {
|
|
202
|
+
if (!options?.expand?.length) return "";
|
|
203
|
+
return `?expand=${options.expand.join(",")}`;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
175
207
|
class OrdersResource extends BaseResource {
|
|
176
208
|
constructor(tokenManager, baseUrl) {
|
|
177
209
|
super(tokenManager, baseUrl, "/orders");
|
|
@@ -206,14 +238,221 @@ class ReservationsResource extends BaseResource {
|
|
|
206
238
|
}
|
|
207
239
|
}
|
|
208
240
|
|
|
241
|
+
var __defProp$2 = Object.defineProperty;
|
|
242
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
243
|
+
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
244
|
+
class BaseRealtimeChannel {
|
|
245
|
+
constructor(manager, tableName) {
|
|
246
|
+
this.manager = manager;
|
|
247
|
+
this.tableName = tableName;
|
|
248
|
+
__publicField$2(this, "channels", /* @__PURE__ */ new Map());
|
|
249
|
+
__publicField$2(this, "supabaseClient", null);
|
|
250
|
+
}
|
|
251
|
+
async subscribe(options = {}) {
|
|
252
|
+
const client = await this.manager.getSupabaseClient();
|
|
253
|
+
this.supabaseClient = client;
|
|
254
|
+
const venueId = this.manager.getVenueId();
|
|
255
|
+
const channelName = `${this.tableName}:${venueId}:${Date.now()}`;
|
|
256
|
+
const filter = this.buildFilter(venueId, options.filter);
|
|
257
|
+
const requestedEvent = options.event || "*";
|
|
258
|
+
const channel = client.channel(channelName).on(
|
|
259
|
+
"postgres_changes",
|
|
260
|
+
{
|
|
261
|
+
event: "*",
|
|
262
|
+
schema: "public",
|
|
263
|
+
table: this.tableName,
|
|
264
|
+
filter: filter || void 0
|
|
265
|
+
},
|
|
266
|
+
(payload) => {
|
|
267
|
+
if (requestedEvent !== "*" && payload.eventType !== requestedEvent) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.handlePayload(payload, options);
|
|
271
|
+
}
|
|
272
|
+
).subscribe((status) => {
|
|
273
|
+
options.onStatusChange?.(status);
|
|
274
|
+
if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
|
|
275
|
+
options.onError?.(new Error(`Channel ${status}: ${channelName}`));
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
this.channels.set(channelName, channel);
|
|
279
|
+
return {
|
|
280
|
+
channelName,
|
|
281
|
+
unsubscribe: async () => {
|
|
282
|
+
await client.removeChannel(channel);
|
|
283
|
+
this.channels.delete(channelName);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
buildFilter(venueId, additionalFilter) {
|
|
288
|
+
const baseFilter = `venue_id=eq.${venueId}`;
|
|
289
|
+
return additionalFilter ? `${baseFilter},${additionalFilter}` : baseFilter;
|
|
290
|
+
}
|
|
291
|
+
handlePayload(payload, options) {
|
|
292
|
+
options.onChange?.(payload);
|
|
293
|
+
switch (payload.eventType) {
|
|
294
|
+
case "INSERT":
|
|
295
|
+
options.onInsert?.(payload.new);
|
|
296
|
+
break;
|
|
297
|
+
case "UPDATE":
|
|
298
|
+
options.onUpdate?.(payload.new, payload.old);
|
|
299
|
+
break;
|
|
300
|
+
case "DELETE":
|
|
301
|
+
options.onDelete?.(payload.old);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async unsubscribeAll() {
|
|
306
|
+
if (!this.supabaseClient) return;
|
|
307
|
+
for (const [, channel] of this.channels) {
|
|
308
|
+
await this.supabaseClient.removeChannel(channel);
|
|
309
|
+
}
|
|
310
|
+
this.channels.clear();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
class MenuItemsChannel extends BaseRealtimeChannel {
|
|
315
|
+
constructor(manager) {
|
|
316
|
+
super(manager, "menu_items");
|
|
317
|
+
}
|
|
318
|
+
// menu_items doesn't have venue_id directly, so we filter by menu_id
|
|
319
|
+
// RLS policy handles security, client filters by menu_id
|
|
320
|
+
buildFilter(_venueId, additionalFilter) {
|
|
321
|
+
return additionalFilter || "";
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Subscribe to changes for a specific menu's items
|
|
325
|
+
*/
|
|
326
|
+
async subscribeToMenu(menuId, options = {}) {
|
|
327
|
+
return this.subscribe({
|
|
328
|
+
...options,
|
|
329
|
+
filter: `menu_id=eq.${menuId}`
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
class EventsChannel extends BaseRealtimeChannel {
|
|
335
|
+
constructor(manager) {
|
|
336
|
+
super(manager, "events");
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Subscribe to changes for a specific event
|
|
340
|
+
*/
|
|
341
|
+
async subscribeToEvent(eventId, options = {}) {
|
|
342
|
+
return this.subscribe({
|
|
343
|
+
...options,
|
|
344
|
+
filter: `id=eq.${eventId}`
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
class ScheduleOverridesChannel extends BaseRealtimeChannel {
|
|
350
|
+
constructor(manager) {
|
|
351
|
+
super(manager, "schedule_overrides");
|
|
352
|
+
}
|
|
353
|
+
// schedule_overrides doesn't have venue_id
|
|
354
|
+
buildFilter(_venueId, additionalFilter) {
|
|
355
|
+
return additionalFilter || "";
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Subscribe to changes for a specific schedule's overrides
|
|
359
|
+
*/
|
|
360
|
+
async subscribeToSchedule(scheduleId, options = {}) {
|
|
361
|
+
return this.subscribe({
|
|
362
|
+
...options,
|
|
363
|
+
filter: `schedule_id=eq.${scheduleId}`
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
var __defProp$1 = Object.defineProperty;
|
|
369
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
370
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
371
|
+
class RealtimeManager {
|
|
372
|
+
constructor(tokenManager, baseUrl) {
|
|
373
|
+
this.tokenManager = tokenManager;
|
|
374
|
+
this.baseUrl = baseUrl;
|
|
375
|
+
__publicField$1(this, "supabaseClient", null);
|
|
376
|
+
__publicField$1(this, "configPromise", null);
|
|
377
|
+
__publicField$1(this, "menuItems");
|
|
378
|
+
__publicField$1(this, "events");
|
|
379
|
+
__publicField$1(this, "scheduleOverrides");
|
|
380
|
+
this.menuItems = new MenuItemsChannel(this);
|
|
381
|
+
this.events = new EventsChannel(this);
|
|
382
|
+
this.scheduleOverrides = new ScheduleOverridesChannel(this);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Fetch realtime config from the order3000 backend.
|
|
386
|
+
* This keeps Supabase as an internal implementation detail.
|
|
387
|
+
*/
|
|
388
|
+
async fetchRealtimeConfig() {
|
|
389
|
+
if (!this.configPromise) {
|
|
390
|
+
this.configPromise = (async () => {
|
|
391
|
+
const token = await this.tokenManager.getToken();
|
|
392
|
+
const response = await fetch(
|
|
393
|
+
`${this.baseUrl}/m2m/realtime-config`,
|
|
394
|
+
{
|
|
395
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
396
|
+
}
|
|
397
|
+
);
|
|
398
|
+
if (!response.ok) {
|
|
399
|
+
throw new Error(`Failed to fetch realtime config: ${response.status}`);
|
|
400
|
+
}
|
|
401
|
+
return response.json();
|
|
402
|
+
})();
|
|
403
|
+
}
|
|
404
|
+
return this.configPromise;
|
|
405
|
+
}
|
|
406
|
+
async getSupabaseClient() {
|
|
407
|
+
if (!this.supabaseClient) {
|
|
408
|
+
const config = await this.fetchRealtimeConfig();
|
|
409
|
+
this.supabaseClient = supabaseJs.createClient(config.url, config.anonKey, {
|
|
410
|
+
auth: {
|
|
411
|
+
persistSession: false,
|
|
412
|
+
autoRefreshToken: false
|
|
413
|
+
},
|
|
414
|
+
realtime: {
|
|
415
|
+
params: {
|
|
416
|
+
eventsPerSecond: 10
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
return this.supabaseClient;
|
|
422
|
+
}
|
|
423
|
+
getVenueId() {
|
|
424
|
+
return this.tokenManager.getVenueId();
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get the realtime configuration (Supabase URL and anon key).
|
|
428
|
+
* Use this to pass the config to client-side code for establishing
|
|
429
|
+
* realtime connections from the browser.
|
|
430
|
+
*/
|
|
431
|
+
async getConfig() {
|
|
432
|
+
return this.fetchRealtimeConfig();
|
|
433
|
+
}
|
|
434
|
+
async disconnectAll() {
|
|
435
|
+
await Promise.all([
|
|
436
|
+
this.menuItems.unsubscribeAll(),
|
|
437
|
+
this.events.unsubscribeAll(),
|
|
438
|
+
this.scheduleOverrides.unsubscribeAll()
|
|
439
|
+
]);
|
|
440
|
+
if (this.supabaseClient) {
|
|
441
|
+
await this.supabaseClient.removeAllChannels();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
209
446
|
var __defProp = Object.defineProperty;
|
|
210
447
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
211
448
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
212
449
|
class Order3000Client {
|
|
213
450
|
constructor(config) {
|
|
214
451
|
__publicField(this, "events");
|
|
452
|
+
__publicField(this, "menus");
|
|
215
453
|
__publicField(this, "orders");
|
|
216
454
|
__publicField(this, "reservations");
|
|
455
|
+
__publicField(this, "realtime");
|
|
217
456
|
__publicField(this, "baseUrl");
|
|
218
457
|
__publicField(this, "tokenManager");
|
|
219
458
|
this.baseUrl = config.baseUrl || "https://order3000.com/api";
|
|
@@ -225,11 +464,13 @@ class Order3000Client {
|
|
|
225
464
|
config.venueId
|
|
226
465
|
);
|
|
227
466
|
this.events = new EventsResource(this.tokenManager, this.baseUrl);
|
|
467
|
+
this.menus = new MenusResource(this.tokenManager, this.baseUrl);
|
|
228
468
|
this.orders = new OrdersResource(this.tokenManager, this.baseUrl);
|
|
229
469
|
this.reservations = new ReservationsResource(
|
|
230
470
|
this.tokenManager,
|
|
231
471
|
this.baseUrl
|
|
232
472
|
);
|
|
473
|
+
this.realtime = new RealtimeManager(this.tokenManager, this.baseUrl);
|
|
233
474
|
}
|
|
234
475
|
}
|
|
235
476
|
|
package/dist/client.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RealtimePostgresChangesPayload, RealtimeChannel, SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
|
|
1
3
|
declare class TokenManager {
|
|
2
4
|
private readonly clientId;
|
|
3
5
|
private readonly clientSecret;
|
|
@@ -34,13 +36,14 @@ interface components {
|
|
|
34
36
|
venueId: string;
|
|
35
37
|
basketId: string;
|
|
36
38
|
/** @enum {string} */
|
|
37
|
-
status?: "
|
|
39
|
+
status?: "draft" | "pending" | "paid" | "fulfilled" | "cancelled" | "refunded";
|
|
38
40
|
paymentIntentId?: string | null;
|
|
39
41
|
/** @enum {string|null} */
|
|
40
|
-
paymentMethod?: "
|
|
41
|
-
taxAmount:
|
|
42
|
-
totalAmount:
|
|
43
|
-
|
|
42
|
+
paymentMethod?: "card" | "cash" | "voucher" | null;
|
|
43
|
+
taxAmount: number;
|
|
44
|
+
totalAmount: number;
|
|
45
|
+
/** @enum {string} */
|
|
46
|
+
currency?: "EUR" | "CHF";
|
|
44
47
|
orderedAt?: unknown;
|
|
45
48
|
orderedBy: string;
|
|
46
49
|
updatedAt?: unknown;
|
|
@@ -52,13 +55,14 @@ interface components {
|
|
|
52
55
|
venueId: string;
|
|
53
56
|
basketId: string;
|
|
54
57
|
/** @enum {string} */
|
|
55
|
-
status: "
|
|
58
|
+
status: "draft" | "pending" | "paid" | "fulfilled" | "cancelled" | "refunded";
|
|
56
59
|
paymentIntentId: string | null;
|
|
57
60
|
/** @enum {string|null} */
|
|
58
|
-
paymentMethod: "
|
|
59
|
-
taxAmount:
|
|
60
|
-
totalAmount:
|
|
61
|
-
|
|
61
|
+
paymentMethod: "card" | "cash" | "voucher" | null;
|
|
62
|
+
taxAmount: number;
|
|
63
|
+
totalAmount: number;
|
|
64
|
+
/** @enum {string} */
|
|
65
|
+
currency: "EUR" | "CHF";
|
|
62
66
|
orderedAt: unknown;
|
|
63
67
|
orderedBy: string;
|
|
64
68
|
updatedAt: unknown;
|
|
@@ -206,6 +210,7 @@ interface components {
|
|
|
206
210
|
* @description Request payload for creating a new event
|
|
207
211
|
*/
|
|
208
212
|
CreateEventRequest: {
|
|
213
|
+
accessor?: string | null;
|
|
209
214
|
name: (string | number | boolean | (null)) | {
|
|
210
215
|
[key: string]: unknown;
|
|
211
216
|
} | unknown[];
|
|
@@ -223,16 +228,18 @@ interface components {
|
|
|
223
228
|
bookingOpensAtDays?: number | null;
|
|
224
229
|
bookingClosesAtHours?: number | null;
|
|
225
230
|
isActive?: boolean;
|
|
226
|
-
|
|
231
|
+
isFeatured?: boolean;
|
|
227
232
|
priority?: number;
|
|
228
233
|
schedule?: {
|
|
229
234
|
rrule?: string | null;
|
|
230
235
|
timezone?: string | null;
|
|
231
236
|
validFrom?: unknown;
|
|
232
237
|
validTo?: unknown;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
timeRanges: {
|
|
239
|
+
startTime: string;
|
|
240
|
+
endTime: string;
|
|
241
|
+
lastSeatingTime?: string | null;
|
|
242
|
+
}[];
|
|
236
243
|
/** @enum {string|null} */
|
|
237
244
|
slotMode?: "interval" | "duration" | null;
|
|
238
245
|
intervalMinutes?: number | null;
|
|
@@ -246,6 +253,7 @@ interface components {
|
|
|
246
253
|
CreateEventResponse: {
|
|
247
254
|
id: string;
|
|
248
255
|
venueId: string;
|
|
256
|
+
accessor: string | null;
|
|
249
257
|
name: (string | number | boolean | (null)) | {
|
|
250
258
|
[key: string]: unknown;
|
|
251
259
|
} | unknown[];
|
|
@@ -263,7 +271,7 @@ interface components {
|
|
|
263
271
|
bookingOpensAtDays: number | null;
|
|
264
272
|
bookingClosesAtHours: number | null;
|
|
265
273
|
isActive: boolean;
|
|
266
|
-
|
|
274
|
+
isFeatured: boolean;
|
|
267
275
|
priority: number;
|
|
268
276
|
createdBy: string | null;
|
|
269
277
|
updatedBy: string | null;
|
|
@@ -368,6 +376,8 @@ interface Event {
|
|
|
368
376
|
bookingOpensAtDays: number | null;
|
|
369
377
|
bookingClosesAtHours: number | null;
|
|
370
378
|
isActive: boolean;
|
|
379
|
+
/** Whether this event should be displayed prominently in public-facing areas */
|
|
380
|
+
isFeatured: boolean;
|
|
371
381
|
/** Priority for conflict resolution (higher = takes precedence) */
|
|
372
382
|
priority: number;
|
|
373
383
|
createdBy: string | null;
|
|
@@ -397,6 +407,60 @@ interface ListEventsOptions {
|
|
|
397
407
|
upcomingWithinDays?: number;
|
|
398
408
|
}
|
|
399
409
|
type ListEventsResponse = Event[];
|
|
410
|
+
type MenuItemType = 'dish' | 'beverage';
|
|
411
|
+
interface MenuAsset {
|
|
412
|
+
id: string;
|
|
413
|
+
type: 'image' | 'video' | 'document';
|
|
414
|
+
tags: string[] | null;
|
|
415
|
+
filename: string;
|
|
416
|
+
contentType: string;
|
|
417
|
+
metadata: {
|
|
418
|
+
width?: number;
|
|
419
|
+
height?: number;
|
|
420
|
+
duration?: number;
|
|
421
|
+
} | null;
|
|
422
|
+
publicUrl: string;
|
|
423
|
+
}
|
|
424
|
+
interface MenuDish {
|
|
425
|
+
id: string;
|
|
426
|
+
accessor: string;
|
|
427
|
+
name: Record<string, string>;
|
|
428
|
+
description: Record<string, string> | null;
|
|
429
|
+
isVegetarian: boolean | null;
|
|
430
|
+
isVegan: boolean | null;
|
|
431
|
+
isGlutenFree: boolean | null;
|
|
432
|
+
isDairyFree: boolean | null;
|
|
433
|
+
assets?: MenuAsset[];
|
|
434
|
+
}
|
|
435
|
+
interface MenuBeverage {
|
|
436
|
+
id: string;
|
|
437
|
+
accessor: string;
|
|
438
|
+
name: Record<string, string>;
|
|
439
|
+
description: Record<string, string> | null;
|
|
440
|
+
}
|
|
441
|
+
interface MenuItem {
|
|
442
|
+
id: string;
|
|
443
|
+
type: MenuItemType;
|
|
444
|
+
tags: string[] | null;
|
|
445
|
+
price: number;
|
|
446
|
+
currency: string;
|
|
447
|
+
isActive: boolean;
|
|
448
|
+
sortIndex: string | null;
|
|
449
|
+
dish: MenuDish | null;
|
|
450
|
+
beverage: MenuBeverage | null;
|
|
451
|
+
}
|
|
452
|
+
interface Menu {
|
|
453
|
+
id: string;
|
|
454
|
+
venueId: string;
|
|
455
|
+
accessor: string;
|
|
456
|
+
name: Record<string, string>;
|
|
457
|
+
description: Record<string, string> | null;
|
|
458
|
+
items?: MenuItem[];
|
|
459
|
+
}
|
|
460
|
+
interface GetMenuOptions {
|
|
461
|
+
/** Expand related resources (items, assets) */
|
|
462
|
+
expand?: ('items' | 'assets')[];
|
|
463
|
+
}
|
|
400
464
|
|
|
401
465
|
declare class EventsResource extends BaseResource {
|
|
402
466
|
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
@@ -420,6 +484,25 @@ declare class EventsResource extends BaseResource {
|
|
|
420
484
|
get(eventId: string): Promise<Event>;
|
|
421
485
|
}
|
|
422
486
|
|
|
487
|
+
declare class MenusResource extends BaseResource {
|
|
488
|
+
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
489
|
+
/**
|
|
490
|
+
* Get a menu by ID
|
|
491
|
+
* @param id - The menu UUID
|
|
492
|
+
* @param options - Optional expand options
|
|
493
|
+
* @returns The menu with optional items and assets
|
|
494
|
+
*/
|
|
495
|
+
get(id: string, options?: GetMenuOptions): Promise<Menu>;
|
|
496
|
+
/**
|
|
497
|
+
* Get a menu by accessor
|
|
498
|
+
* @param accessor - The menu accessor (slug)
|
|
499
|
+
* @param options - Optional expand options
|
|
500
|
+
* @returns The menu with optional items and assets
|
|
501
|
+
*/
|
|
502
|
+
getByAccessor(accessor: string, options?: GetMenuOptions): Promise<Menu>;
|
|
503
|
+
private buildQueryString;
|
|
504
|
+
}
|
|
505
|
+
|
|
423
506
|
declare class OrdersResource extends BaseResource {
|
|
424
507
|
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
425
508
|
place(order: PlaceOrderRequest): Promise<PlaceOrderResponse>;
|
|
@@ -442,6 +525,127 @@ declare class ReservationsResource extends BaseResource {
|
|
|
442
525
|
get(reservationId: string): Promise<MakeReservationResponse>;
|
|
443
526
|
}
|
|
444
527
|
|
|
528
|
+
type RealtimeEvent = 'INSERT' | 'UPDATE' | 'DELETE' | '*';
|
|
529
|
+
type RealtimeRow = {
|
|
530
|
+
[key: string]: any;
|
|
531
|
+
};
|
|
532
|
+
interface SubscriptionOptions<T extends RealtimeRow> {
|
|
533
|
+
event?: RealtimeEvent;
|
|
534
|
+
filter?: string;
|
|
535
|
+
onInsert?: (record: T) => void;
|
|
536
|
+
onUpdate?: (newRecord: T, oldRecord: T) => void;
|
|
537
|
+
onDelete?: (oldRecord: T) => void;
|
|
538
|
+
onChange?: (payload: RealtimePostgresChangesPayload<T>) => void;
|
|
539
|
+
onError?: (error: Error) => void;
|
|
540
|
+
onStatusChange?: (status: 'SUBSCRIBED' | 'CLOSED' | 'CHANNEL_ERROR' | 'TIMED_OUT') => void;
|
|
541
|
+
}
|
|
542
|
+
interface RealtimeSubscription {
|
|
543
|
+
unsubscribe: () => Promise<void>;
|
|
544
|
+
channelName: string;
|
|
545
|
+
}
|
|
546
|
+
interface MenuItemRow {
|
|
547
|
+
id: string;
|
|
548
|
+
menu_id: string;
|
|
549
|
+
type: 'dish' | 'beverage';
|
|
550
|
+
dish_id: string | null;
|
|
551
|
+
beverage_id: string | null;
|
|
552
|
+
price: number;
|
|
553
|
+
currency: string;
|
|
554
|
+
is_active: boolean;
|
|
555
|
+
tags: string[] | null;
|
|
556
|
+
sort_index: string | null;
|
|
557
|
+
}
|
|
558
|
+
interface EventRow {
|
|
559
|
+
id: string;
|
|
560
|
+
venue_id: string;
|
|
561
|
+
accessor: string;
|
|
562
|
+
name: Record<string, string>;
|
|
563
|
+
is_public: boolean;
|
|
564
|
+
is_active: boolean;
|
|
565
|
+
schedule_id: string | null;
|
|
566
|
+
}
|
|
567
|
+
interface ScheduleRow {
|
|
568
|
+
id: string;
|
|
569
|
+
name: Record<string, string>;
|
|
570
|
+
is_active: boolean;
|
|
571
|
+
valid_from: string | null;
|
|
572
|
+
valid_to: string | null;
|
|
573
|
+
}
|
|
574
|
+
interface ScheduleOverrideRow {
|
|
575
|
+
id: string;
|
|
576
|
+
schedule_id: string;
|
|
577
|
+
date: string;
|
|
578
|
+
reason: Record<string, string> | null;
|
|
579
|
+
is_active: boolean;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
declare abstract class BaseRealtimeChannel<T extends RealtimeRow> {
|
|
583
|
+
protected readonly manager: RealtimeManager;
|
|
584
|
+
protected readonly tableName: string;
|
|
585
|
+
protected channels: Map<string, RealtimeChannel>;
|
|
586
|
+
private supabaseClient;
|
|
587
|
+
constructor(manager: RealtimeManager, tableName: string);
|
|
588
|
+
subscribe(options?: SubscriptionOptions<T>): Promise<RealtimeSubscription>;
|
|
589
|
+
protected buildFilter(venueId: string, additionalFilter?: string): string;
|
|
590
|
+
private handlePayload;
|
|
591
|
+
unsubscribeAll(): Promise<void>;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
declare class MenuItemsChannel extends BaseRealtimeChannel<MenuItemRow> {
|
|
595
|
+
constructor(manager: RealtimeManager);
|
|
596
|
+
protected buildFilter(_venueId: string, additionalFilter?: string): string;
|
|
597
|
+
/**
|
|
598
|
+
* Subscribe to changes for a specific menu's items
|
|
599
|
+
*/
|
|
600
|
+
subscribeToMenu(menuId: string, options?: Omit<SubscriptionOptions<MenuItemRow>, 'filter'>): Promise<RealtimeSubscription>;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
declare class EventsChannel extends BaseRealtimeChannel<EventRow> {
|
|
604
|
+
constructor(manager: RealtimeManager);
|
|
605
|
+
/**
|
|
606
|
+
* Subscribe to changes for a specific event
|
|
607
|
+
*/
|
|
608
|
+
subscribeToEvent(eventId: string, options?: Omit<SubscriptionOptions<EventRow>, 'filter'>): Promise<RealtimeSubscription>;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
declare class ScheduleOverridesChannel extends BaseRealtimeChannel<ScheduleOverrideRow> {
|
|
612
|
+
constructor(manager: RealtimeManager);
|
|
613
|
+
protected buildFilter(_venueId: string, additionalFilter?: string): string;
|
|
614
|
+
/**
|
|
615
|
+
* Subscribe to changes for a specific schedule's overrides
|
|
616
|
+
*/
|
|
617
|
+
subscribeToSchedule(scheduleId: string, options?: Omit<SubscriptionOptions<ScheduleOverrideRow>, 'filter'>): Promise<RealtimeSubscription>;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
interface RealtimeConfig {
|
|
621
|
+
url: string;
|
|
622
|
+
anonKey: string;
|
|
623
|
+
}
|
|
624
|
+
declare class RealtimeManager {
|
|
625
|
+
private readonly tokenManager;
|
|
626
|
+
private readonly baseUrl;
|
|
627
|
+
private supabaseClient;
|
|
628
|
+
private configPromise;
|
|
629
|
+
menuItems: MenuItemsChannel;
|
|
630
|
+
events: EventsChannel;
|
|
631
|
+
scheduleOverrides: ScheduleOverridesChannel;
|
|
632
|
+
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
633
|
+
/**
|
|
634
|
+
* Fetch realtime config from the order3000 backend.
|
|
635
|
+
* This keeps Supabase as an internal implementation detail.
|
|
636
|
+
*/
|
|
637
|
+
private fetchRealtimeConfig;
|
|
638
|
+
getSupabaseClient(): Promise<SupabaseClient>;
|
|
639
|
+
getVenueId(): string;
|
|
640
|
+
/**
|
|
641
|
+
* Get the realtime configuration (Supabase URL and anon key).
|
|
642
|
+
* Use this to pass the config to client-side code for establishing
|
|
643
|
+
* realtime connections from the browser.
|
|
644
|
+
*/
|
|
645
|
+
getConfig(): Promise<RealtimeConfig>;
|
|
646
|
+
disconnectAll(): Promise<void>;
|
|
647
|
+
}
|
|
648
|
+
|
|
445
649
|
interface Order3000ClientConfig {
|
|
446
650
|
clientId: string;
|
|
447
651
|
clientSecret: string;
|
|
@@ -459,11 +663,13 @@ interface Order3000ClientConfig {
|
|
|
459
663
|
}
|
|
460
664
|
declare class Order3000Client {
|
|
461
665
|
events: EventsResource;
|
|
666
|
+
menus: MenusResource;
|
|
462
667
|
orders: OrdersResource;
|
|
463
668
|
reservations: ReservationsResource;
|
|
669
|
+
realtime: RealtimeManager;
|
|
464
670
|
private readonly baseUrl;
|
|
465
671
|
private readonly tokenManager;
|
|
466
672
|
constructor(config: Order3000ClientConfig);
|
|
467
673
|
}
|
|
468
674
|
|
|
469
|
-
export { type Event, type EventPoster, type ListEventsOptions, type ListEventsResponse, Order3000Client, type Order3000ClientConfig, type Promotion, type Schedule, type ScheduleOverride, type TimeSlot };
|
|
675
|
+
export { type Event, type EventPoster, type EventRow, type GetMenuOptions, type ListEventsOptions, type ListEventsResponse, type Menu, type MenuAsset, type MenuBeverage, type MenuDish, type MenuItem, type MenuItemRow, type MenuItemType, Order3000Client, type Order3000ClientConfig, type Promotion, type RealtimeConfig, type RealtimeEvent, type RealtimeSubscription, type Schedule, type ScheduleOverride, type ScheduleOverrideRow, type ScheduleRow, type SubscriptionOptions, type TimeSlot };
|
package/dist/client.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { RealtimePostgresChangesPayload, RealtimeChannel, SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
|
|
1
3
|
declare class TokenManager {
|
|
2
4
|
private readonly clientId;
|
|
3
5
|
private readonly clientSecret;
|
|
@@ -34,13 +36,14 @@ interface components {
|
|
|
34
36
|
venueId: string;
|
|
35
37
|
basketId: string;
|
|
36
38
|
/** @enum {string} */
|
|
37
|
-
status?: "
|
|
39
|
+
status?: "draft" | "pending" | "paid" | "fulfilled" | "cancelled" | "refunded";
|
|
38
40
|
paymentIntentId?: string | null;
|
|
39
41
|
/** @enum {string|null} */
|
|
40
|
-
paymentMethod?: "
|
|
41
|
-
taxAmount:
|
|
42
|
-
totalAmount:
|
|
43
|
-
|
|
42
|
+
paymentMethod?: "card" | "cash" | "voucher" | null;
|
|
43
|
+
taxAmount: number;
|
|
44
|
+
totalAmount: number;
|
|
45
|
+
/** @enum {string} */
|
|
46
|
+
currency?: "EUR" | "CHF";
|
|
44
47
|
orderedAt?: unknown;
|
|
45
48
|
orderedBy: string;
|
|
46
49
|
updatedAt?: unknown;
|
|
@@ -52,13 +55,14 @@ interface components {
|
|
|
52
55
|
venueId: string;
|
|
53
56
|
basketId: string;
|
|
54
57
|
/** @enum {string} */
|
|
55
|
-
status: "
|
|
58
|
+
status: "draft" | "pending" | "paid" | "fulfilled" | "cancelled" | "refunded";
|
|
56
59
|
paymentIntentId: string | null;
|
|
57
60
|
/** @enum {string|null} */
|
|
58
|
-
paymentMethod: "
|
|
59
|
-
taxAmount:
|
|
60
|
-
totalAmount:
|
|
61
|
-
|
|
61
|
+
paymentMethod: "card" | "cash" | "voucher" | null;
|
|
62
|
+
taxAmount: number;
|
|
63
|
+
totalAmount: number;
|
|
64
|
+
/** @enum {string} */
|
|
65
|
+
currency: "EUR" | "CHF";
|
|
62
66
|
orderedAt: unknown;
|
|
63
67
|
orderedBy: string;
|
|
64
68
|
updatedAt: unknown;
|
|
@@ -206,6 +210,7 @@ interface components {
|
|
|
206
210
|
* @description Request payload for creating a new event
|
|
207
211
|
*/
|
|
208
212
|
CreateEventRequest: {
|
|
213
|
+
accessor?: string | null;
|
|
209
214
|
name: (string | number | boolean | (null)) | {
|
|
210
215
|
[key: string]: unknown;
|
|
211
216
|
} | unknown[];
|
|
@@ -223,16 +228,18 @@ interface components {
|
|
|
223
228
|
bookingOpensAtDays?: number | null;
|
|
224
229
|
bookingClosesAtHours?: number | null;
|
|
225
230
|
isActive?: boolean;
|
|
226
|
-
|
|
231
|
+
isFeatured?: boolean;
|
|
227
232
|
priority?: number;
|
|
228
233
|
schedule?: {
|
|
229
234
|
rrule?: string | null;
|
|
230
235
|
timezone?: string | null;
|
|
231
236
|
validFrom?: unknown;
|
|
232
237
|
validTo?: unknown;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
timeRanges: {
|
|
239
|
+
startTime: string;
|
|
240
|
+
endTime: string;
|
|
241
|
+
lastSeatingTime?: string | null;
|
|
242
|
+
}[];
|
|
236
243
|
/** @enum {string|null} */
|
|
237
244
|
slotMode?: "interval" | "duration" | null;
|
|
238
245
|
intervalMinutes?: number | null;
|
|
@@ -246,6 +253,7 @@ interface components {
|
|
|
246
253
|
CreateEventResponse: {
|
|
247
254
|
id: string;
|
|
248
255
|
venueId: string;
|
|
256
|
+
accessor: string | null;
|
|
249
257
|
name: (string | number | boolean | (null)) | {
|
|
250
258
|
[key: string]: unknown;
|
|
251
259
|
} | unknown[];
|
|
@@ -263,7 +271,7 @@ interface components {
|
|
|
263
271
|
bookingOpensAtDays: number | null;
|
|
264
272
|
bookingClosesAtHours: number | null;
|
|
265
273
|
isActive: boolean;
|
|
266
|
-
|
|
274
|
+
isFeatured: boolean;
|
|
267
275
|
priority: number;
|
|
268
276
|
createdBy: string | null;
|
|
269
277
|
updatedBy: string | null;
|
|
@@ -368,6 +376,8 @@ interface Event {
|
|
|
368
376
|
bookingOpensAtDays: number | null;
|
|
369
377
|
bookingClosesAtHours: number | null;
|
|
370
378
|
isActive: boolean;
|
|
379
|
+
/** Whether this event should be displayed prominently in public-facing areas */
|
|
380
|
+
isFeatured: boolean;
|
|
371
381
|
/** Priority for conflict resolution (higher = takes precedence) */
|
|
372
382
|
priority: number;
|
|
373
383
|
createdBy: string | null;
|
|
@@ -397,6 +407,60 @@ interface ListEventsOptions {
|
|
|
397
407
|
upcomingWithinDays?: number;
|
|
398
408
|
}
|
|
399
409
|
type ListEventsResponse = Event[];
|
|
410
|
+
type MenuItemType = 'dish' | 'beverage';
|
|
411
|
+
interface MenuAsset {
|
|
412
|
+
id: string;
|
|
413
|
+
type: 'image' | 'video' | 'document';
|
|
414
|
+
tags: string[] | null;
|
|
415
|
+
filename: string;
|
|
416
|
+
contentType: string;
|
|
417
|
+
metadata: {
|
|
418
|
+
width?: number;
|
|
419
|
+
height?: number;
|
|
420
|
+
duration?: number;
|
|
421
|
+
} | null;
|
|
422
|
+
publicUrl: string;
|
|
423
|
+
}
|
|
424
|
+
interface MenuDish {
|
|
425
|
+
id: string;
|
|
426
|
+
accessor: string;
|
|
427
|
+
name: Record<string, string>;
|
|
428
|
+
description: Record<string, string> | null;
|
|
429
|
+
isVegetarian: boolean | null;
|
|
430
|
+
isVegan: boolean | null;
|
|
431
|
+
isGlutenFree: boolean | null;
|
|
432
|
+
isDairyFree: boolean | null;
|
|
433
|
+
assets?: MenuAsset[];
|
|
434
|
+
}
|
|
435
|
+
interface MenuBeverage {
|
|
436
|
+
id: string;
|
|
437
|
+
accessor: string;
|
|
438
|
+
name: Record<string, string>;
|
|
439
|
+
description: Record<string, string> | null;
|
|
440
|
+
}
|
|
441
|
+
interface MenuItem {
|
|
442
|
+
id: string;
|
|
443
|
+
type: MenuItemType;
|
|
444
|
+
tags: string[] | null;
|
|
445
|
+
price: number;
|
|
446
|
+
currency: string;
|
|
447
|
+
isActive: boolean;
|
|
448
|
+
sortIndex: string | null;
|
|
449
|
+
dish: MenuDish | null;
|
|
450
|
+
beverage: MenuBeverage | null;
|
|
451
|
+
}
|
|
452
|
+
interface Menu {
|
|
453
|
+
id: string;
|
|
454
|
+
venueId: string;
|
|
455
|
+
accessor: string;
|
|
456
|
+
name: Record<string, string>;
|
|
457
|
+
description: Record<string, string> | null;
|
|
458
|
+
items?: MenuItem[];
|
|
459
|
+
}
|
|
460
|
+
interface GetMenuOptions {
|
|
461
|
+
/** Expand related resources (items, assets) */
|
|
462
|
+
expand?: ('items' | 'assets')[];
|
|
463
|
+
}
|
|
400
464
|
|
|
401
465
|
declare class EventsResource extends BaseResource {
|
|
402
466
|
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
@@ -420,6 +484,25 @@ declare class EventsResource extends BaseResource {
|
|
|
420
484
|
get(eventId: string): Promise<Event>;
|
|
421
485
|
}
|
|
422
486
|
|
|
487
|
+
declare class MenusResource extends BaseResource {
|
|
488
|
+
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
489
|
+
/**
|
|
490
|
+
* Get a menu by ID
|
|
491
|
+
* @param id - The menu UUID
|
|
492
|
+
* @param options - Optional expand options
|
|
493
|
+
* @returns The menu with optional items and assets
|
|
494
|
+
*/
|
|
495
|
+
get(id: string, options?: GetMenuOptions): Promise<Menu>;
|
|
496
|
+
/**
|
|
497
|
+
* Get a menu by accessor
|
|
498
|
+
* @param accessor - The menu accessor (slug)
|
|
499
|
+
* @param options - Optional expand options
|
|
500
|
+
* @returns The menu with optional items and assets
|
|
501
|
+
*/
|
|
502
|
+
getByAccessor(accessor: string, options?: GetMenuOptions): Promise<Menu>;
|
|
503
|
+
private buildQueryString;
|
|
504
|
+
}
|
|
505
|
+
|
|
423
506
|
declare class OrdersResource extends BaseResource {
|
|
424
507
|
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
425
508
|
place(order: PlaceOrderRequest): Promise<PlaceOrderResponse>;
|
|
@@ -442,6 +525,127 @@ declare class ReservationsResource extends BaseResource {
|
|
|
442
525
|
get(reservationId: string): Promise<MakeReservationResponse>;
|
|
443
526
|
}
|
|
444
527
|
|
|
528
|
+
type RealtimeEvent = 'INSERT' | 'UPDATE' | 'DELETE' | '*';
|
|
529
|
+
type RealtimeRow = {
|
|
530
|
+
[key: string]: any;
|
|
531
|
+
};
|
|
532
|
+
interface SubscriptionOptions<T extends RealtimeRow> {
|
|
533
|
+
event?: RealtimeEvent;
|
|
534
|
+
filter?: string;
|
|
535
|
+
onInsert?: (record: T) => void;
|
|
536
|
+
onUpdate?: (newRecord: T, oldRecord: T) => void;
|
|
537
|
+
onDelete?: (oldRecord: T) => void;
|
|
538
|
+
onChange?: (payload: RealtimePostgresChangesPayload<T>) => void;
|
|
539
|
+
onError?: (error: Error) => void;
|
|
540
|
+
onStatusChange?: (status: 'SUBSCRIBED' | 'CLOSED' | 'CHANNEL_ERROR' | 'TIMED_OUT') => void;
|
|
541
|
+
}
|
|
542
|
+
interface RealtimeSubscription {
|
|
543
|
+
unsubscribe: () => Promise<void>;
|
|
544
|
+
channelName: string;
|
|
545
|
+
}
|
|
546
|
+
interface MenuItemRow {
|
|
547
|
+
id: string;
|
|
548
|
+
menu_id: string;
|
|
549
|
+
type: 'dish' | 'beverage';
|
|
550
|
+
dish_id: string | null;
|
|
551
|
+
beverage_id: string | null;
|
|
552
|
+
price: number;
|
|
553
|
+
currency: string;
|
|
554
|
+
is_active: boolean;
|
|
555
|
+
tags: string[] | null;
|
|
556
|
+
sort_index: string | null;
|
|
557
|
+
}
|
|
558
|
+
interface EventRow {
|
|
559
|
+
id: string;
|
|
560
|
+
venue_id: string;
|
|
561
|
+
accessor: string;
|
|
562
|
+
name: Record<string, string>;
|
|
563
|
+
is_public: boolean;
|
|
564
|
+
is_active: boolean;
|
|
565
|
+
schedule_id: string | null;
|
|
566
|
+
}
|
|
567
|
+
interface ScheduleRow {
|
|
568
|
+
id: string;
|
|
569
|
+
name: Record<string, string>;
|
|
570
|
+
is_active: boolean;
|
|
571
|
+
valid_from: string | null;
|
|
572
|
+
valid_to: string | null;
|
|
573
|
+
}
|
|
574
|
+
interface ScheduleOverrideRow {
|
|
575
|
+
id: string;
|
|
576
|
+
schedule_id: string;
|
|
577
|
+
date: string;
|
|
578
|
+
reason: Record<string, string> | null;
|
|
579
|
+
is_active: boolean;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
declare abstract class BaseRealtimeChannel<T extends RealtimeRow> {
|
|
583
|
+
protected readonly manager: RealtimeManager;
|
|
584
|
+
protected readonly tableName: string;
|
|
585
|
+
protected channels: Map<string, RealtimeChannel>;
|
|
586
|
+
private supabaseClient;
|
|
587
|
+
constructor(manager: RealtimeManager, tableName: string);
|
|
588
|
+
subscribe(options?: SubscriptionOptions<T>): Promise<RealtimeSubscription>;
|
|
589
|
+
protected buildFilter(venueId: string, additionalFilter?: string): string;
|
|
590
|
+
private handlePayload;
|
|
591
|
+
unsubscribeAll(): Promise<void>;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
declare class MenuItemsChannel extends BaseRealtimeChannel<MenuItemRow> {
|
|
595
|
+
constructor(manager: RealtimeManager);
|
|
596
|
+
protected buildFilter(_venueId: string, additionalFilter?: string): string;
|
|
597
|
+
/**
|
|
598
|
+
* Subscribe to changes for a specific menu's items
|
|
599
|
+
*/
|
|
600
|
+
subscribeToMenu(menuId: string, options?: Omit<SubscriptionOptions<MenuItemRow>, 'filter'>): Promise<RealtimeSubscription>;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
declare class EventsChannel extends BaseRealtimeChannel<EventRow> {
|
|
604
|
+
constructor(manager: RealtimeManager);
|
|
605
|
+
/**
|
|
606
|
+
* Subscribe to changes for a specific event
|
|
607
|
+
*/
|
|
608
|
+
subscribeToEvent(eventId: string, options?: Omit<SubscriptionOptions<EventRow>, 'filter'>): Promise<RealtimeSubscription>;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
declare class ScheduleOverridesChannel extends BaseRealtimeChannel<ScheduleOverrideRow> {
|
|
612
|
+
constructor(manager: RealtimeManager);
|
|
613
|
+
protected buildFilter(_venueId: string, additionalFilter?: string): string;
|
|
614
|
+
/**
|
|
615
|
+
* Subscribe to changes for a specific schedule's overrides
|
|
616
|
+
*/
|
|
617
|
+
subscribeToSchedule(scheduleId: string, options?: Omit<SubscriptionOptions<ScheduleOverrideRow>, 'filter'>): Promise<RealtimeSubscription>;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
interface RealtimeConfig {
|
|
621
|
+
url: string;
|
|
622
|
+
anonKey: string;
|
|
623
|
+
}
|
|
624
|
+
declare class RealtimeManager {
|
|
625
|
+
private readonly tokenManager;
|
|
626
|
+
private readonly baseUrl;
|
|
627
|
+
private supabaseClient;
|
|
628
|
+
private configPromise;
|
|
629
|
+
menuItems: MenuItemsChannel;
|
|
630
|
+
events: EventsChannel;
|
|
631
|
+
scheduleOverrides: ScheduleOverridesChannel;
|
|
632
|
+
constructor(tokenManager: TokenManager, baseUrl: string);
|
|
633
|
+
/**
|
|
634
|
+
* Fetch realtime config from the order3000 backend.
|
|
635
|
+
* This keeps Supabase as an internal implementation detail.
|
|
636
|
+
*/
|
|
637
|
+
private fetchRealtimeConfig;
|
|
638
|
+
getSupabaseClient(): Promise<SupabaseClient>;
|
|
639
|
+
getVenueId(): string;
|
|
640
|
+
/**
|
|
641
|
+
* Get the realtime configuration (Supabase URL and anon key).
|
|
642
|
+
* Use this to pass the config to client-side code for establishing
|
|
643
|
+
* realtime connections from the browser.
|
|
644
|
+
*/
|
|
645
|
+
getConfig(): Promise<RealtimeConfig>;
|
|
646
|
+
disconnectAll(): Promise<void>;
|
|
647
|
+
}
|
|
648
|
+
|
|
445
649
|
interface Order3000ClientConfig {
|
|
446
650
|
clientId: string;
|
|
447
651
|
clientSecret: string;
|
|
@@ -459,11 +663,13 @@ interface Order3000ClientConfig {
|
|
|
459
663
|
}
|
|
460
664
|
declare class Order3000Client {
|
|
461
665
|
events: EventsResource;
|
|
666
|
+
menus: MenusResource;
|
|
462
667
|
orders: OrdersResource;
|
|
463
668
|
reservations: ReservationsResource;
|
|
669
|
+
realtime: RealtimeManager;
|
|
464
670
|
private readonly baseUrl;
|
|
465
671
|
private readonly tokenManager;
|
|
466
672
|
constructor(config: Order3000ClientConfig);
|
|
467
673
|
}
|
|
468
674
|
|
|
469
|
-
export { type Event, type EventPoster, type ListEventsOptions, type ListEventsResponse, Order3000Client, type Order3000ClientConfig, type Promotion, type Schedule, type ScheduleOverride, type TimeSlot };
|
|
675
|
+
export { type Event, type EventPoster, type EventRow, type GetMenuOptions, type ListEventsOptions, type ListEventsResponse, type Menu, type MenuAsset, type MenuBeverage, type MenuDish, type MenuItem, type MenuItemRow, type MenuItemType, Order3000Client, type Order3000ClientConfig, type Promotion, type RealtimeConfig, type RealtimeEvent, type RealtimeSubscription, type Schedule, type ScheduleOverride, type ScheduleOverrideRow, type ScheduleRow, type SubscriptionOptions, type TimeSlot };
|
package/dist/client.mjs
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
1
|
+
import { createClient } from '@supabase/supabase-js';
|
|
2
|
+
|
|
3
|
+
var __defProp$3 = Object.defineProperty;
|
|
4
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
|
+
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
6
|
class TokenManager {
|
|
5
7
|
constructor(clientId, clientSecret, tokenEndpoint, userVenueId) {
|
|
6
8
|
this.clientId = clientId;
|
|
7
9
|
this.clientSecret = clientSecret;
|
|
8
10
|
this.tokenEndpoint = tokenEndpoint;
|
|
9
11
|
this.userVenueId = userVenueId;
|
|
10
|
-
__publicField$
|
|
11
|
-
__publicField$
|
|
12
|
-
__publicField$
|
|
13
|
-
__publicField$
|
|
12
|
+
__publicField$3(this, "accessToken", null);
|
|
13
|
+
__publicField$3(this, "tokenExpiresAt", 0);
|
|
14
|
+
__publicField$3(this, "refreshPromise", null);
|
|
15
|
+
__publicField$3(this, "defaultVenueId");
|
|
14
16
|
}
|
|
15
17
|
async initialize() {
|
|
16
18
|
await this.refreshToken();
|
|
@@ -170,6 +172,36 @@ class EventsResource extends BaseResource {
|
|
|
170
172
|
}
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
class MenusResource extends BaseResource {
|
|
176
|
+
constructor(tokenManager, baseUrl) {
|
|
177
|
+
super(tokenManager, baseUrl, "/menus");
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get a menu by ID
|
|
181
|
+
* @param id - The menu UUID
|
|
182
|
+
* @param options - Optional expand options
|
|
183
|
+
* @returns The menu with optional items and assets
|
|
184
|
+
*/
|
|
185
|
+
async get(id, options) {
|
|
186
|
+
const queryString = this.buildQueryString(options);
|
|
187
|
+
return this.request("GET", `/${id}${queryString}`);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get a menu by accessor
|
|
191
|
+
* @param accessor - The menu accessor (slug)
|
|
192
|
+
* @param options - Optional expand options
|
|
193
|
+
* @returns The menu with optional items and assets
|
|
194
|
+
*/
|
|
195
|
+
async getByAccessor(accessor, options) {
|
|
196
|
+
const queryString = this.buildQueryString(options);
|
|
197
|
+
return this.request("GET", `/${accessor}${queryString}`);
|
|
198
|
+
}
|
|
199
|
+
buildQueryString(options) {
|
|
200
|
+
if (!options?.expand?.length) return "";
|
|
201
|
+
return `?expand=${options.expand.join(",")}`;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
173
205
|
class OrdersResource extends BaseResource {
|
|
174
206
|
constructor(tokenManager, baseUrl) {
|
|
175
207
|
super(tokenManager, baseUrl, "/orders");
|
|
@@ -204,14 +236,221 @@ class ReservationsResource extends BaseResource {
|
|
|
204
236
|
}
|
|
205
237
|
}
|
|
206
238
|
|
|
239
|
+
var __defProp$2 = Object.defineProperty;
|
|
240
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
241
|
+
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
242
|
+
class BaseRealtimeChannel {
|
|
243
|
+
constructor(manager, tableName) {
|
|
244
|
+
this.manager = manager;
|
|
245
|
+
this.tableName = tableName;
|
|
246
|
+
__publicField$2(this, "channels", /* @__PURE__ */ new Map());
|
|
247
|
+
__publicField$2(this, "supabaseClient", null);
|
|
248
|
+
}
|
|
249
|
+
async subscribe(options = {}) {
|
|
250
|
+
const client = await this.manager.getSupabaseClient();
|
|
251
|
+
this.supabaseClient = client;
|
|
252
|
+
const venueId = this.manager.getVenueId();
|
|
253
|
+
const channelName = `${this.tableName}:${venueId}:${Date.now()}`;
|
|
254
|
+
const filter = this.buildFilter(venueId, options.filter);
|
|
255
|
+
const requestedEvent = options.event || "*";
|
|
256
|
+
const channel = client.channel(channelName).on(
|
|
257
|
+
"postgres_changes",
|
|
258
|
+
{
|
|
259
|
+
event: "*",
|
|
260
|
+
schema: "public",
|
|
261
|
+
table: this.tableName,
|
|
262
|
+
filter: filter || void 0
|
|
263
|
+
},
|
|
264
|
+
(payload) => {
|
|
265
|
+
if (requestedEvent !== "*" && payload.eventType !== requestedEvent) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
this.handlePayload(payload, options);
|
|
269
|
+
}
|
|
270
|
+
).subscribe((status) => {
|
|
271
|
+
options.onStatusChange?.(status);
|
|
272
|
+
if (status === "CHANNEL_ERROR" || status === "TIMED_OUT") {
|
|
273
|
+
options.onError?.(new Error(`Channel ${status}: ${channelName}`));
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
this.channels.set(channelName, channel);
|
|
277
|
+
return {
|
|
278
|
+
channelName,
|
|
279
|
+
unsubscribe: async () => {
|
|
280
|
+
await client.removeChannel(channel);
|
|
281
|
+
this.channels.delete(channelName);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
buildFilter(venueId, additionalFilter) {
|
|
286
|
+
const baseFilter = `venue_id=eq.${venueId}`;
|
|
287
|
+
return additionalFilter ? `${baseFilter},${additionalFilter}` : baseFilter;
|
|
288
|
+
}
|
|
289
|
+
handlePayload(payload, options) {
|
|
290
|
+
options.onChange?.(payload);
|
|
291
|
+
switch (payload.eventType) {
|
|
292
|
+
case "INSERT":
|
|
293
|
+
options.onInsert?.(payload.new);
|
|
294
|
+
break;
|
|
295
|
+
case "UPDATE":
|
|
296
|
+
options.onUpdate?.(payload.new, payload.old);
|
|
297
|
+
break;
|
|
298
|
+
case "DELETE":
|
|
299
|
+
options.onDelete?.(payload.old);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async unsubscribeAll() {
|
|
304
|
+
if (!this.supabaseClient) return;
|
|
305
|
+
for (const [, channel] of this.channels) {
|
|
306
|
+
await this.supabaseClient.removeChannel(channel);
|
|
307
|
+
}
|
|
308
|
+
this.channels.clear();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
class MenuItemsChannel extends BaseRealtimeChannel {
|
|
313
|
+
constructor(manager) {
|
|
314
|
+
super(manager, "menu_items");
|
|
315
|
+
}
|
|
316
|
+
// menu_items doesn't have venue_id directly, so we filter by menu_id
|
|
317
|
+
// RLS policy handles security, client filters by menu_id
|
|
318
|
+
buildFilter(_venueId, additionalFilter) {
|
|
319
|
+
return additionalFilter || "";
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Subscribe to changes for a specific menu's items
|
|
323
|
+
*/
|
|
324
|
+
async subscribeToMenu(menuId, options = {}) {
|
|
325
|
+
return this.subscribe({
|
|
326
|
+
...options,
|
|
327
|
+
filter: `menu_id=eq.${menuId}`
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
class EventsChannel extends BaseRealtimeChannel {
|
|
333
|
+
constructor(manager) {
|
|
334
|
+
super(manager, "events");
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Subscribe to changes for a specific event
|
|
338
|
+
*/
|
|
339
|
+
async subscribeToEvent(eventId, options = {}) {
|
|
340
|
+
return this.subscribe({
|
|
341
|
+
...options,
|
|
342
|
+
filter: `id=eq.${eventId}`
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
class ScheduleOverridesChannel extends BaseRealtimeChannel {
|
|
348
|
+
constructor(manager) {
|
|
349
|
+
super(manager, "schedule_overrides");
|
|
350
|
+
}
|
|
351
|
+
// schedule_overrides doesn't have venue_id
|
|
352
|
+
buildFilter(_venueId, additionalFilter) {
|
|
353
|
+
return additionalFilter || "";
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Subscribe to changes for a specific schedule's overrides
|
|
357
|
+
*/
|
|
358
|
+
async subscribeToSchedule(scheduleId, options = {}) {
|
|
359
|
+
return this.subscribe({
|
|
360
|
+
...options,
|
|
361
|
+
filter: `schedule_id=eq.${scheduleId}`
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
var __defProp$1 = Object.defineProperty;
|
|
367
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
368
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
369
|
+
class RealtimeManager {
|
|
370
|
+
constructor(tokenManager, baseUrl) {
|
|
371
|
+
this.tokenManager = tokenManager;
|
|
372
|
+
this.baseUrl = baseUrl;
|
|
373
|
+
__publicField$1(this, "supabaseClient", null);
|
|
374
|
+
__publicField$1(this, "configPromise", null);
|
|
375
|
+
__publicField$1(this, "menuItems");
|
|
376
|
+
__publicField$1(this, "events");
|
|
377
|
+
__publicField$1(this, "scheduleOverrides");
|
|
378
|
+
this.menuItems = new MenuItemsChannel(this);
|
|
379
|
+
this.events = new EventsChannel(this);
|
|
380
|
+
this.scheduleOverrides = new ScheduleOverridesChannel(this);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Fetch realtime config from the order3000 backend.
|
|
384
|
+
* This keeps Supabase as an internal implementation detail.
|
|
385
|
+
*/
|
|
386
|
+
async fetchRealtimeConfig() {
|
|
387
|
+
if (!this.configPromise) {
|
|
388
|
+
this.configPromise = (async () => {
|
|
389
|
+
const token = await this.tokenManager.getToken();
|
|
390
|
+
const response = await fetch(
|
|
391
|
+
`${this.baseUrl}/m2m/realtime-config`,
|
|
392
|
+
{
|
|
393
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
394
|
+
}
|
|
395
|
+
);
|
|
396
|
+
if (!response.ok) {
|
|
397
|
+
throw new Error(`Failed to fetch realtime config: ${response.status}`);
|
|
398
|
+
}
|
|
399
|
+
return response.json();
|
|
400
|
+
})();
|
|
401
|
+
}
|
|
402
|
+
return this.configPromise;
|
|
403
|
+
}
|
|
404
|
+
async getSupabaseClient() {
|
|
405
|
+
if (!this.supabaseClient) {
|
|
406
|
+
const config = await this.fetchRealtimeConfig();
|
|
407
|
+
this.supabaseClient = createClient(config.url, config.anonKey, {
|
|
408
|
+
auth: {
|
|
409
|
+
persistSession: false,
|
|
410
|
+
autoRefreshToken: false
|
|
411
|
+
},
|
|
412
|
+
realtime: {
|
|
413
|
+
params: {
|
|
414
|
+
eventsPerSecond: 10
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
return this.supabaseClient;
|
|
420
|
+
}
|
|
421
|
+
getVenueId() {
|
|
422
|
+
return this.tokenManager.getVenueId();
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get the realtime configuration (Supabase URL and anon key).
|
|
426
|
+
* Use this to pass the config to client-side code for establishing
|
|
427
|
+
* realtime connections from the browser.
|
|
428
|
+
*/
|
|
429
|
+
async getConfig() {
|
|
430
|
+
return this.fetchRealtimeConfig();
|
|
431
|
+
}
|
|
432
|
+
async disconnectAll() {
|
|
433
|
+
await Promise.all([
|
|
434
|
+
this.menuItems.unsubscribeAll(),
|
|
435
|
+
this.events.unsubscribeAll(),
|
|
436
|
+
this.scheduleOverrides.unsubscribeAll()
|
|
437
|
+
]);
|
|
438
|
+
if (this.supabaseClient) {
|
|
439
|
+
await this.supabaseClient.removeAllChannels();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
207
444
|
var __defProp = Object.defineProperty;
|
|
208
445
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
209
446
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
210
447
|
class Order3000Client {
|
|
211
448
|
constructor(config) {
|
|
212
449
|
__publicField(this, "events");
|
|
450
|
+
__publicField(this, "menus");
|
|
213
451
|
__publicField(this, "orders");
|
|
214
452
|
__publicField(this, "reservations");
|
|
453
|
+
__publicField(this, "realtime");
|
|
215
454
|
__publicField(this, "baseUrl");
|
|
216
455
|
__publicField(this, "tokenManager");
|
|
217
456
|
this.baseUrl = config.baseUrl || "https://order3000.com/api";
|
|
@@ -223,11 +462,13 @@ class Order3000Client {
|
|
|
223
462
|
config.venueId
|
|
224
463
|
);
|
|
225
464
|
this.events = new EventsResource(this.tokenManager, this.baseUrl);
|
|
465
|
+
this.menus = new MenusResource(this.tokenManager, this.baseUrl);
|
|
226
466
|
this.orders = new OrdersResource(this.tokenManager, this.baseUrl);
|
|
227
467
|
this.reservations = new ReservationsResource(
|
|
228
468
|
this.tokenManager,
|
|
229
469
|
this.baseUrl
|
|
230
470
|
);
|
|
471
|
+
this.realtime = new RealtimeManager(this.tokenManager, this.baseUrl);
|
|
231
472
|
}
|
|
232
473
|
}
|
|
233
474
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "order3000",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "order3000 node.js sdk",
|
|
5
5
|
"main": "./dist/client.cjs",
|
|
6
6
|
"module": "./dist/client.mjs",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"tsx": "^4.19.2",
|
|
31
31
|
"typescript": "^5.9.3",
|
|
32
32
|
"@kolaveri/eslint-config": "1.0.9",
|
|
33
|
-
"@kolaveri/
|
|
34
|
-
"@kolaveri/
|
|
33
|
+
"@kolaveri/tsconfig": "1.0.1",
|
|
34
|
+
"@kolaveri/prettier-config": "1.0.6"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist"
|