@svadmin/supabase 0.13.3 → 0.13.5

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.
Files changed (2) hide show
  1. package/package.json +3 -3
  2. package/src/supacloud.ts +200 -12
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/supabase",
3
- "version": "0.13.3",
3
+ "version": "0.13.5",
4
4
  "description": "Supabase adapters — DataProvider, AuthProvider, LiveProvider, AuditHandler",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "peerDependencies": {
27
- "@svadmin/core": "^0.47.1",
27
+ "@svadmin/core": "^0.48.0",
28
28
  "@supabase/supabase-js": "^2.112.4",
29
29
  "@refinedev/supabase": "^6.0.2",
30
30
  "@supacloud/js": "^0.23.1"
@@ -55,7 +55,7 @@
55
55
  }
56
56
  },
57
57
  "dependencies": {
58
- "@svadmin/refine-adapter": "^0.10.11"
58
+ "@svadmin/refine-adapter": "^0.10.12"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@supabase/supabase-js": "^2.112.4"
package/src/supacloud.ts CHANGED
@@ -13,7 +13,23 @@ export interface SupaCloudTaskRecord extends TaskRecord {
13
13
  [key: string]: unknown;
14
14
  }
15
15
 
16
- export interface SupaCloudTaskClient<TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord> {
16
+ export interface SupaCloudTaskReceipt<TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord> {
17
+ id?: string;
18
+ taskId?: string;
19
+ wait(options?: unknown): Promise<TTask>;
20
+ cancel?(): Promise<unknown>;
21
+ retry?(): Promise<unknown>;
22
+ subscribe?(
23
+ options: {
24
+ onUpdate: (task: TTask) => void;
25
+ onStateChange?: (state: unknown, details?: { error?: unknown }) => void;
26
+ onError?: (error: unknown) => void;
27
+ stopOnTerminal?: boolean;
28
+ }
29
+ ): TaskSubscription | (() => void) | undefined;
30
+ }
31
+
32
+ export interface SupaCloudTaskLegacyClient<TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord> {
17
33
  submit(taskName: string, options?: SubmitTaskOptions): Promise<TaskHandle<TTask>>;
18
34
  get(taskId: string): Promise<TTask>;
19
35
  list?(params?: Record<string, unknown>): Promise<TTask[] | { data?: TTask[] }>;
@@ -26,6 +42,30 @@ export interface SupaCloudTaskClient<TTask extends SupaCloudTaskRecord = SupaClo
26
42
  ): TaskSubscription | (() => void) | undefined;
27
43
  }
28
44
 
45
+ export interface SupaCloudTaskSdkClient<TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord> {
46
+ tasks: {
47
+ submit(taskName: string, options?: Record<string, unknown>): Promise<SupaCloudTaskReceipt<TTask>>;
48
+ get(taskId: string): Promise<TTask>;
49
+ list?(params?: Record<string, unknown>): Promise<TTask[]>;
50
+ listDlq?(limit?: number): Promise<TTask[]>;
51
+ cancel?(taskId: string): Promise<unknown>;
52
+ retry?(taskId: string): Promise<unknown>;
53
+ subscribe?(
54
+ taskId: string,
55
+ options: {
56
+ onUpdate: (task: TTask) => void;
57
+ onStateChange?: (state: unknown, details?: { error?: unknown }) => void;
58
+ onError?: (error: unknown) => void;
59
+ stopOnTerminal?: boolean;
60
+ }
61
+ ): TaskSubscription | (() => void) | undefined;
62
+ };
63
+ }
64
+
65
+ export type SupaCloudTaskClient<TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord> =
66
+ | SupaCloudTaskLegacyClient<TTask>
67
+ | SupaCloudTaskSdkClient<TTask>;
68
+
29
69
  export interface CreateSupaCloudTaskProviderOptions<
30
70
  TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord,
31
71
  > {
@@ -35,7 +75,9 @@ export interface CreateSupaCloudTaskProviderOptions<
35
75
  export interface CreateSupaCloudTaskLiveProviderOptions<
36
76
  TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord,
37
77
  > {
38
- supacloud: Pick<SupaCloudTaskClient<TTask>, 'subscribe'>;
78
+ supacloud:
79
+ | Pick<SupaCloudTaskLegacyClient<TTask>, 'subscribe'>
80
+ | SupaCloudTaskSdkClient<TTask>;
39
81
  resource?: string;
40
82
  mapTaskToEvent?: (task: TTask, resource: string) => LiveEvent;
41
83
  }
@@ -51,14 +93,159 @@ function normalizeTaskSubscription(
51
93
  return undefined;
52
94
  }
53
95
 
96
+ function normalizeTaskHandle<TTask extends SupaCloudTaskRecord>(
97
+ taskHandle: SupaCloudTaskReceipt<TTask>,
98
+ ): TaskHandle<TTask> {
99
+ if (!taskHandle || typeof taskHandle.wait !== 'function') {
100
+ throw new Error('[svadmin/supabase] SupaCloud client returned an invalid task receipt');
101
+ }
102
+
103
+ const normalized: TaskHandle<TTask> = {
104
+ wait: () => taskHandle.wait(),
105
+ };
106
+
107
+ const id = 'taskId' in taskHandle && typeof taskHandle.taskId === 'string'
108
+ ? taskHandle.taskId
109
+ : typeof taskHandle.id === 'string'
110
+ ? taskHandle.id
111
+ : undefined;
112
+
113
+ if (id) {
114
+ normalized.id = id;
115
+ }
116
+
117
+ const cancel = taskHandle.cancel;
118
+ if (typeof cancel === 'function') {
119
+ normalized.cancel = () => cancel.call(taskHandle);
120
+ }
121
+
122
+ const retry = taskHandle.retry;
123
+ if (typeof retry === 'function') {
124
+ normalized.retry = () => retry.call(taskHandle);
125
+ }
126
+
127
+ const subscribe = taskHandle.subscribe;
128
+ if (typeof subscribe === 'function') {
129
+ normalized.subscribe = (callback) =>
130
+ normalizeTaskSubscription(
131
+ subscribe.call(taskHandle, {
132
+ onUpdate: (task) => callback(task),
133
+ }),
134
+ );
135
+ }
136
+
137
+ return normalized;
138
+ }
139
+
140
+ function mapSubmitOptionsToSdk(
141
+ submitOptions?: SubmitTaskOptions,
142
+ ): Record<string, unknown> | undefined {
143
+ if (!submitOptions) return undefined;
144
+
145
+ const sdkOptions: Record<string, unknown> = {};
146
+
147
+ if (submitOptions.body !== undefined) sdkOptions.body = submitOptions.body;
148
+ if (submitOptions.headers !== undefined) sdkOptions.headers = submitOptions.headers;
149
+ if (submitOptions.idempotencyKey !== undefined) sdkOptions.idempotencyKey = submitOptions.idempotencyKey;
150
+ if (submitOptions.meta !== undefined) sdkOptions.metadata = submitOptions.meta;
151
+
152
+ return sdkOptions;
153
+ }
154
+
54
155
  function normalizeTaskList<TTask extends SupaCloudTaskRecord>(
55
156
  value: TTask[] | { data?: TTask[] },
157
+ context: string,
56
158
  ): TaskListResult<TTask> {
57
- const data = Array.isArray(value) ? value : (value.data ?? []);
58
- return {
59
- data,
60
- total: data.length,
61
- };
159
+ if (Array.isArray(value)) {
160
+ return {
161
+ data: value,
162
+ total: value.length,
163
+ };
164
+ }
165
+
166
+ if (value && typeof value === 'object' && Array.isArray(value.data)) {
167
+ return {
168
+ data: value.data,
169
+ total: value.data.length,
170
+ };
171
+ }
172
+
173
+ throw new Error(`[svadmin/supabase] ${context} returned an invalid task list response`);
174
+ }
175
+
176
+ function getTaskBridge<TTask extends SupaCloudTaskRecord>(
177
+ supacloud: SupaCloudTaskClient<TTask>,
178
+ ): SupaCloudTaskLegacyClient<TTask> {
179
+ if ('tasks' in supacloud && supacloud.tasks) {
180
+ const sdk = supacloud.tasks;
181
+
182
+ return {
183
+ async submit(taskName, submitOptions) {
184
+ const receipt = await sdk.submit(taskName, mapSubmitOptionsToSdk(submitOptions));
185
+ return normalizeTaskHandle(receipt);
186
+ },
187
+ get(taskId) {
188
+ return sdk.get(taskId);
189
+ },
190
+ async list(params) {
191
+ if (!sdk.list) {
192
+ throw new Error('[svadmin/supabase] SupaCloud SDK client does not implement tasks.list');
193
+ }
194
+ return normalizeTaskList(await sdk.list(params), 'tasks.list');
195
+ },
196
+ async listDlq(params) {
197
+ if (!sdk.listDlq) {
198
+ throw new Error('[svadmin/supabase] SupaCloud SDK client does not implement tasks.listDlq');
199
+ }
200
+ const limit = typeof params?.limit === 'number' ? params.limit : undefined;
201
+ return normalizeTaskList(await sdk.listDlq(limit), 'tasks.listDlq');
202
+ },
203
+ cancel(taskId) {
204
+ if (!sdk.cancel) {
205
+ throw new Error('[svadmin/supabase] SupaCloud SDK client does not implement tasks.cancel');
206
+ }
207
+ return sdk.cancel(taskId);
208
+ },
209
+ retry(taskId) {
210
+ if (!sdk.retry) {
211
+ throw new Error('[svadmin/supabase] SupaCloud SDK client does not implement tasks.retry');
212
+ }
213
+ return sdk.retry(taskId);
214
+ },
215
+ subscribe(taskId, callback) {
216
+ if (!sdk.subscribe) {
217
+ throw new Error('[svadmin/supabase] SupaCloud SDK client does not implement tasks.subscribe');
218
+ }
219
+ return sdk.subscribe(taskId, {
220
+ onUpdate: callback,
221
+ });
222
+ },
223
+ };
224
+ }
225
+
226
+ return supacloud as SupaCloudTaskLegacyClient<TTask>;
227
+ }
228
+
229
+ function getTaskSubscribeBridge<TTask extends SupaCloudTaskRecord>(
230
+ supacloud:
231
+ | Pick<SupaCloudTaskLegacyClient<TTask>, 'subscribe'>
232
+ | SupaCloudTaskSdkClient<TTask>,
233
+ ): Pick<SupaCloudTaskLegacyClient<TTask>, 'subscribe'> {
234
+ if ('tasks' in supacloud && supacloud.tasks) {
235
+ return {
236
+ subscribe(taskId, callback) {
237
+ if (!supacloud.tasks.subscribe) {
238
+ throw new Error('[svadmin/supabase] SupaCloud SDK client does not implement tasks.subscribe');
239
+ }
240
+
241
+ return supacloud.tasks.subscribe(taskId, {
242
+ onUpdate: callback,
243
+ });
244
+ },
245
+ };
246
+ }
247
+
248
+ return supacloud as Pick<SupaCloudTaskLegacyClient<TTask>, 'subscribe'>;
62
249
  }
63
250
 
64
251
  function defaultMapTaskToEvent<TTask extends SupaCloudTaskRecord>(
@@ -75,7 +262,7 @@ function defaultMapTaskToEvent<TTask extends SupaCloudTaskRecord>(
75
262
  export function createSupaCloudTaskProvider<
76
263
  TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord,
77
264
  >(options: CreateSupaCloudTaskProviderOptions<TTask>): TaskProvider<TTask> {
78
- const { supacloud } = options;
265
+ const supacloud = getTaskBridge(options.supacloud);
79
266
 
80
267
  return {
81
268
  submit(taskName, submitOptions) {
@@ -88,13 +275,13 @@ export function createSupaCloudTaskProvider<
88
275
  if (!supacloud.list) {
89
276
  throw new Error('[svadmin/supabase] SupaCloud client does not implement tasks.list');
90
277
  }
91
- return normalizeTaskList(await supacloud.list(params));
278
+ return normalizeTaskList(await supacloud.list(params), 'tasks.list');
92
279
  },
93
280
  async listDlq(params) {
94
281
  if (!supacloud.listDlq) {
95
282
  throw new Error('[svadmin/supabase] SupaCloud client does not implement tasks.listDlq');
96
283
  }
97
- return normalizeTaskList(await supacloud.listDlq(params));
284
+ return normalizeTaskList(await supacloud.listDlq(params), 'tasks.listDlq');
98
285
  },
99
286
  cancel(taskId) {
100
287
  if (!supacloud.cancel) {
@@ -120,12 +307,13 @@ export function createSupaCloudTaskProvider<
120
307
  export function createSupaCloudTaskLiveProvider<
121
308
  TTask extends SupaCloudTaskRecord = SupaCloudTaskRecord,
122
309
  >(options: CreateSupaCloudTaskLiveProviderOptions<TTask>): LiveProvider {
310
+ const supacloud = getTaskSubscribeBridge(options.supacloud);
123
311
  const resourceName = options.resource ?? 'tasks';
124
312
  const mapTaskToEvent = options.mapTaskToEvent ?? defaultMapTaskToEvent<TTask>;
125
313
 
126
314
  return {
127
315
  subscribe({ resource, liveParams, callback }) {
128
- if (!options.supacloud.subscribe) {
316
+ if (!supacloud.subscribe) {
129
317
  throw new Error('[svadmin/supabase] SupaCloud client does not implement tasks.subscribe');
130
318
  }
131
319
 
@@ -134,7 +322,7 @@ export function createSupaCloudTaskLiveProvider<
134
322
  throw new Error('[svadmin/supabase] createSupaCloudTaskLiveProvider requires liveParams.taskId');
135
323
  }
136
324
 
137
- const unsubscribe = options.supacloud.subscribe(taskId, (task) => {
325
+ const unsubscribe = supacloud.subscribe(taskId, (task) => {
138
326
  callback(mapTaskToEvent(task, resource || resourceName));
139
327
  });
140
328