@quatrain/cloudwrapper-supabase 1.1.16 → 1.1.18

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.
@@ -6,6 +6,15 @@ export type SupabaseParams = {
6
6
  url?: string;
7
7
  key?: string;
8
8
  };
9
+ export type Channel = {
10
+ name: string;
11
+ channel: RealtimeChannel | undefined;
12
+ state: any;
13
+ };
14
+ export type Channels = {
15
+ database: Channel[];
16
+ storage: Channel[];
17
+ };
9
18
  export declare const eventMap: {
10
19
  create: string;
11
20
  update: string;
@@ -15,6 +24,7 @@ export declare class SupabaseCloudWrapper extends AbstractCloudWrapper {
15
24
  protected _supabaseClient: SupabaseClient | undefined;
16
25
  protected _realtimeClient: RealtimeChannel | undefined;
17
26
  protected _isInitialized: boolean;
27
+ protected _channels: Channels;
18
28
  constructor(params: SupabaseParams);
19
29
  databaseTrigger(trigger: DatabaseTriggerType): void | {
20
30
  event: any;
@@ -26,9 +36,8 @@ export declare class SupabaseCloudWrapper extends AbstractCloudWrapper {
26
36
  schema: string;
27
37
  table: string;
28
38
  };
29
- triggersEnable(): void;
30
- triggersDisable(): void;
31
39
  protected _initialize(): void;
40
+ poll(): void;
32
41
  protected _payload2File(payload: {
33
42
  [x: string]: any;
34
43
  }): FileType | undefined;
@@ -33,12 +33,13 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
33
33
  constructor(params) {
34
34
  super(params);
35
35
  this._isInitialized = false;
36
+ this._channels = { database: [], storage: [] };
36
37
  this._initialize();
37
38
  }
38
39
  databaseTrigger(trigger) {
39
40
  var _a;
40
41
  this._initialize();
41
- if (!this._realtimeClient) {
42
+ if (!(this._supabaseClient instanceof supabase_js_1.SupabaseClient)) {
42
43
  throw new Error(`Realtime channel is not enabled`);
43
44
  }
44
45
  if (typeof trigger.script !== 'function') {
@@ -58,7 +59,7 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
58
59
  table: trigger.model,
59
60
  };
60
61
  cloudwrapper_1.CloudWrapper.info(`Set up DB trigger ${trigger.name} for ${trigger.event} event on table ${schema}.${params.table}`);
61
- (_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
62
+ const channel = (_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
62
63
  var { old: before, new: after } = _b, context = __rest(_b, ["old", "new"]);
63
64
  cloudwrapper_1.CloudWrapper.info(`Triggering function on event ${trigger.name}`);
64
65
  try {
@@ -69,6 +70,11 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
69
70
  console.log(err);
70
71
  }
71
72
  })).subscribe();
73
+ this._channels.database.push({
74
+ name: trigger.name,
75
+ channel,
76
+ state: channel.state,
77
+ });
72
78
  return params;
73
79
  }
74
80
  catch (err) {
@@ -79,9 +85,6 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
79
85
  storageTrigger(trigger) {
80
86
  var _a;
81
87
  this._initialize();
82
- if (!this._realtimeClient) {
83
- throw new Error(`Realtime channel is not enabled`);
84
- }
85
88
  if (typeof trigger.script !== 'function') {
86
89
  throw new Error(`Passed script value is not a function`);
87
90
  }
@@ -98,7 +101,7 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
98
101
  table: 'objects',
99
102
  };
100
103
  cloudwrapper_1.CloudWrapper.info(`Set up Storage trigger ${trigger.name} for ${trigger.event} event`);
101
- (_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
104
+ const channel = (_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel(trigger.name).on('postgres_changes', params, (_b) => __awaiter(this, void 0, void 0, function* () {
102
105
  var { old: before, new: after } = _b, context = __rest(_b, ["old", "new"]);
103
106
  cloudwrapper_1.CloudWrapper.info(`Triggering storage function on event ${trigger.name}`);
104
107
  try {
@@ -112,9 +115,13 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
112
115
  }
113
116
  catch (err) {
114
117
  cloudwrapper_1.CloudWrapper.error(err.message);
115
- console.log(err);
116
118
  }
117
119
  })).subscribe();
120
+ this._channels.storage.push({
121
+ name: trigger.name,
122
+ channel,
123
+ state: channel === null || channel === void 0 ? void 0 : channel.state,
124
+ });
118
125
  return params;
119
126
  }
120
127
  catch (err) {
@@ -122,42 +129,31 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
122
129
  throw new Error(`Realtime channel subscription failed`);
123
130
  }
124
131
  }
125
- triggersEnable() {
126
- if (!this._realtimeClient) {
127
- throw new Error(`Realtime client is not enabled`);
128
- }
129
- this._realtimeClient.subscribe((res) => {
130
- cloudwrapper_1.CloudWrapper.info(`Enabling Realtime triggers: ${res}`);
131
- if (res !== 'SUBSCRIBED') {
132
- throw new Error(`Failed to subscribe to Realtime channels`);
133
- }
134
- });
135
- }
136
- triggersDisable() {
137
- var _a;
138
- if (!this._realtimeClient) {
139
- throw new Error(`Realtime channel is not enabled`);
140
- }
141
- cloudwrapper_1.CloudWrapper.info(`Disabling Realtime triggers`);
142
- (_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.removeAllChannels();
143
- }
144
132
  _initialize() {
145
133
  if (this._isInitialized === false) {
146
134
  this._supabaseClient = (0, supabase_js_1.createClient)(this._params.url, this._params.key);
147
- this._realtimeClient = this._supabaseClient.channel('table-db-changes');
148
135
  this._isInitialized = true;
149
136
  cloudwrapper_1.CloudWrapper.info(`Supabase App initialized`);
150
137
  }
151
138
  }
139
+ poll() {
140
+ var _a;
141
+ cloudwrapper_1.CloudWrapper.info(`Supabase client status: ${(_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.channel}`);
142
+ this._channels.storage.forEach((channel) => {
143
+ var _a;
144
+ cloudwrapper_1.CloudWrapper.info(`${channel.name}: ${channel.state}`);
145
+ console.log((_a = channel.channel) === null || _a === void 0 ? void 0 : _a.params);
146
+ });
147
+ setTimeout(() => this.poll(), 10000);
148
+ }
152
149
  _payload2File(payload) {
153
- console.log('received payload', payload);
154
150
  if (!payload.metadata) {
155
151
  return undefined;
156
152
  }
157
153
  if (Object.keys(payload).length === 0) {
158
154
  return undefined;
159
155
  }
160
- const { size, mimetype } = payload === null || payload === void 0 ? void 0 : payload.metadata;
156
+ const { size, mimetype } = (payload === null || payload === void 0 ? void 0 : payload.metadata) || {};
161
157
  return {
162
158
  host: 'supabase',
163
159
  bucket: payload.bucket_id,
package/package.json CHANGED
@@ -1,31 +1,36 @@
1
1
  {
2
2
  "name": "@quatrain/cloudwrapper-supabase",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "license": "MIT",
5
5
  "description": "Cloud Wrapper adapter for Supabase",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/quatrain/Core.git",
9
+ "directory": "packages/cloudwrapper-supabase"
10
+ },
6
11
  "main": "lib/index.js",
7
12
  "types": "lib/index.d.ts",
8
13
  "files": [
14
+ "src/",
9
15
  "lib/",
10
16
  "README.md"
11
17
  ],
12
18
  "author": "Quatrain Développement SAS <developers@quatrain.com>",
13
19
  "peerDependencies": {
14
- "@quatrain/core": "^1.1.19"
20
+ "@quatrain/core": "^1.1.22"
15
21
  },
16
22
  "dependencies": {
17
- "@quatrain/backend": "^1.1.20",
23
+ "@quatrain/backend": "^1.1.21",
18
24
  "@quatrain/cloudwrapper": "^1.1.14",
19
- "@quatrain/core": "^1.1.19",
20
- "@supabase/supabase-js": "^2.49.4"
25
+ "@supabase/supabase-js": "^2.53.0"
21
26
  },
22
27
  "devDependencies": {
23
28
  "@tsconfig/recommended": "^1.0.1",
24
29
  "@types/jest": "^27.0.3",
25
30
  "@types/node": "^22.10.1",
26
- "jest": "^27.4.7",
31
+ "jest": "^30.1.2",
27
32
  "jest-node-exports-resolver": "^1.1.6",
28
- "jest-serial-runner": "^1.2.1",
33
+ "jest-serial-runner": "^1.2.2",
29
34
  "trace-unhandled": "^2.0.1",
30
35
  "ts-jest": "^27.1.2",
31
36
  "ts-node": "^10.9.1",
@@ -0,0 +1,215 @@
1
+ import {
2
+ CloudWrapper,
3
+ AbstractCloudWrapper,
4
+ DatabaseTriggerType,
5
+ StorageTriggerType,
6
+ StorageEventPayloadType,
7
+ } from '@quatrain/cloudwrapper'
8
+ import { BackendAction } from '@quatrain/backend'
9
+ import {
10
+ createClient,
11
+ SupabaseClient,
12
+ RealtimeChannel,
13
+ } from '@supabase/supabase-js'
14
+ import { FileType } from '@quatrain/storage'
15
+
16
+ export type SupabaseParams = {
17
+ useEmulator?: boolean
18
+ url?: string
19
+ key?: string
20
+ }
21
+
22
+ export type Channel = {
23
+ name: string
24
+ channel: RealtimeChannel | undefined
25
+ state: any
26
+ }
27
+
28
+ export type Channels = {
29
+ database: Channel[]
30
+ storage: Channel[]
31
+ }
32
+
33
+ export const eventMap = {
34
+ [BackendAction.CREATE]: 'INSERT',
35
+ [BackendAction.UPDATE]: 'UPDATE',
36
+ [BackendAction.DELETE]: 'DELETE',
37
+ }
38
+
39
+ export class SupabaseCloudWrapper extends AbstractCloudWrapper {
40
+ protected _supabaseClient: SupabaseClient | undefined
41
+ protected _realtimeClient: RealtimeChannel | undefined
42
+ protected _isInitialized = false
43
+ protected _channels: Channels = { database: [], storage: [] }
44
+
45
+ constructor(params: SupabaseParams) {
46
+ super(params)
47
+ this._initialize()
48
+ }
49
+
50
+ databaseTrigger(trigger: DatabaseTriggerType) {
51
+ this._initialize()
52
+
53
+ if (!(this._supabaseClient instanceof SupabaseClient)) {
54
+ throw new Error(`Realtime channel is not enabled`)
55
+ }
56
+
57
+ if (typeof trigger.script !== 'function') {
58
+ throw new Error(`Passed script value is not a function`)
59
+ }
60
+
61
+ if (Array.isArray(trigger.event)) {
62
+ const params = trigger.event.forEach((event) => {
63
+ return this.databaseTrigger({
64
+ ...trigger,
65
+ event,
66
+ name: `${trigger.name}-${event}`,
67
+ })
68
+ })
69
+ return params
70
+ }
71
+
72
+ try {
73
+ const schema = trigger.schema || 'public'
74
+ const params = {
75
+ event: Reflect.get(eventMap, trigger.event),
76
+ schema,
77
+ table: trigger.model,
78
+ }
79
+ CloudWrapper.info(
80
+ `Set up DB trigger ${trigger.name} for ${trigger.event} event on table ${schema}.${params.table}`
81
+ )
82
+ const channel = this._supabaseClient
83
+ ?.channel(trigger.name)
84
+ .on(
85
+ 'postgres_changes',
86
+ params,
87
+ async ({ old: before, new: after, ...context }) => {
88
+ CloudWrapper.info(
89
+ `Triggering function on event ${trigger.name}`
90
+ )
91
+ try {
92
+ return await trigger.script({ before, after, context })
93
+ } catch (err) {
94
+ CloudWrapper.error((err as Error).message)
95
+ console.log(err)
96
+ }
97
+ }
98
+ )
99
+ .subscribe()
100
+ this._channels.database.push({
101
+ name: trigger.name,
102
+ channel,
103
+ state: channel.state,
104
+ })
105
+
106
+ return params
107
+ } catch (err) {
108
+ CloudWrapper.error(err)
109
+ throw new Error(`Realtime channel subscription failed`)
110
+ }
111
+ }
112
+
113
+ storageTrigger(trigger: StorageTriggerType) {
114
+ this._initialize()
115
+
116
+ if (typeof trigger.script !== 'function') {
117
+ throw new Error(`Passed script value is not a function`)
118
+ }
119
+
120
+ if (Array.isArray(trigger.event)) {
121
+ const params = trigger.event.forEach((event) => {
122
+ return this.storageTrigger({
123
+ ...trigger,
124
+ event,
125
+ name: `${trigger.name}-${event}`,
126
+ })
127
+ })
128
+
129
+ return params
130
+ }
131
+
132
+ try {
133
+ const params = {
134
+ event: Reflect.get(eventMap, trigger.event),
135
+ schema: 'storage',
136
+ table: 'objects',
137
+ }
138
+ CloudWrapper.info(
139
+ `Set up Storage trigger ${trigger.name} for ${trigger.event} event`
140
+ )
141
+ const channel = this._supabaseClient
142
+ ?.channel(trigger.name)
143
+ .on(
144
+ 'postgres_changes',
145
+ params,
146
+ async ({ old: before, new: after, ...context }) => {
147
+ CloudWrapper.info(
148
+ `Triggering storage function on event ${trigger.name}`
149
+ )
150
+ try {
151
+ const payload: StorageEventPayloadType = {
152
+ before: this._payload2File(before),
153
+ after: this._payload2File(after),
154
+ context,
155
+ }
156
+ CloudWrapper.debug(payload)
157
+ return await trigger.script(payload)
158
+ } catch (err) {
159
+ CloudWrapper.error((err as Error).message)
160
+ }
161
+ }
162
+ )
163
+ .subscribe()
164
+ this._channels.storage.push({
165
+ name: trigger.name,
166
+ channel,
167
+ state: channel?.state,
168
+ })
169
+ return params
170
+ } catch (err) {
171
+ console.log(err)
172
+ throw new Error(`Realtime channel subscription failed`)
173
+ }
174
+ }
175
+
176
+ protected _initialize() {
177
+ if (this._isInitialized === false) {
178
+ this._supabaseClient = createClient(this._params.url, this._params.key)
179
+ this._isInitialized = true
180
+ CloudWrapper.info(`Supabase App initialized`)
181
+ }
182
+ }
183
+
184
+ public poll() {
185
+ CloudWrapper.info(
186
+ `Supabase client status: ${this._supabaseClient?.channel}`
187
+ )
188
+ this._channels.storage.forEach((channel) => {
189
+ CloudWrapper.info(`${channel.name}: ${channel.state}`)
190
+ console.log(channel.channel?.params)
191
+ })
192
+ setTimeout(() => this.poll(), 10000)
193
+ }
194
+
195
+ protected _payload2File(payload: {
196
+ [x: string]: any
197
+ }): FileType | undefined {
198
+ if (!payload.metadata) {
199
+ return undefined
200
+ }
201
+
202
+ if (Object.keys(payload).length === 0) {
203
+ return undefined
204
+ }
205
+
206
+ const { size, mimetype } = payload?.metadata || {}
207
+ return {
208
+ host: 'supabase',
209
+ bucket: payload.bucket_id,
210
+ ref: payload.name,
211
+ contentType: mimetype,
212
+ size,
213
+ }
214
+ }
215
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { SupabaseCloudWrapper } from './SupabaseCloudWrapper'
2
+
3
+ export { SupabaseCloudWrapper }