@quatrain/cloudwrapper-supabase 1.1.15 → 1.1.17
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/lib/SupabaseCloudWrapper.d.ts +11 -2
- package/lib/SupabaseCloudWrapper.js +30 -29
- package/package.json +12 -7
- package/src/SupabaseCloudWrapper.ts +215 -0
- package/src/index.ts +3 -0
|
@@ -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.
|
|
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,22 +101,27 @@ 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
|
-
cloudwrapper_1.CloudWrapper.info(`Triggering function on event ${trigger.name}`);
|
|
106
|
+
cloudwrapper_1.CloudWrapper.info(`Triggering storage function on event ${trigger.name}`);
|
|
104
107
|
try {
|
|
105
108
|
const payload = {
|
|
106
109
|
before: this._payload2File(before),
|
|
107
110
|
after: this._payload2File(after),
|
|
108
111
|
context,
|
|
109
112
|
};
|
|
113
|
+
cloudwrapper_1.CloudWrapper.debug(payload);
|
|
110
114
|
return yield trigger.script(payload);
|
|
111
115
|
}
|
|
112
116
|
catch (err) {
|
|
113
117
|
cloudwrapper_1.CloudWrapper.error(err.message);
|
|
114
|
-
console.log(err);
|
|
115
118
|
}
|
|
116
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
|
+
});
|
|
117
125
|
return params;
|
|
118
126
|
}
|
|
119
127
|
catch (err) {
|
|
@@ -121,38 +129,31 @@ class SupabaseCloudWrapper extends cloudwrapper_1.AbstractCloudWrapper {
|
|
|
121
129
|
throw new Error(`Realtime channel subscription failed`);
|
|
122
130
|
}
|
|
123
131
|
}
|
|
124
|
-
triggersEnable() {
|
|
125
|
-
if (!this._realtimeClient) {
|
|
126
|
-
throw new Error(`Realtime client is not enabled`);
|
|
127
|
-
}
|
|
128
|
-
this._realtimeClient.subscribe((res) => {
|
|
129
|
-
cloudwrapper_1.CloudWrapper.info(`Enabling Realtime triggers: ${res}`);
|
|
130
|
-
if (res !== 'SUBSCRIBED') {
|
|
131
|
-
throw new Error(`Failed to subscribe to Realtime channels`);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
triggersDisable() {
|
|
136
|
-
var _a;
|
|
137
|
-
if (!this._realtimeClient) {
|
|
138
|
-
throw new Error(`Realtime channel is not enabled`);
|
|
139
|
-
}
|
|
140
|
-
cloudwrapper_1.CloudWrapper.info(`Disabling Realtime triggers`);
|
|
141
|
-
(_a = this._supabaseClient) === null || _a === void 0 ? void 0 : _a.removeAllChannels();
|
|
142
|
-
}
|
|
143
132
|
_initialize() {
|
|
144
133
|
if (this._isInitialized === false) {
|
|
145
134
|
this._supabaseClient = (0, supabase_js_1.createClient)(this._params.url, this._params.key);
|
|
146
|
-
this._realtimeClient = this._supabaseClient.channel('table-db-changes');
|
|
147
135
|
this._isInitialized = true;
|
|
148
136
|
cloudwrapper_1.CloudWrapper.info(`Supabase App initialized`);
|
|
149
137
|
}
|
|
150
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
|
+
}
|
|
151
149
|
_payload2File(payload) {
|
|
150
|
+
if (!payload.metadata) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
152
153
|
if (Object.keys(payload).length === 0) {
|
|
153
154
|
return undefined;
|
|
154
155
|
}
|
|
155
|
-
const { size, mimetype } = payload.metadata;
|
|
156
|
+
const { size, mimetype } = (payload === null || payload === void 0 ? void 0 : payload.metadata) || {};
|
|
156
157
|
return {
|
|
157
158
|
host: 'supabase',
|
|
158
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.
|
|
3
|
+
"version": "1.1.17",
|
|
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.
|
|
20
|
+
"@quatrain/core": "^1.1.22"
|
|
15
21
|
},
|
|
16
22
|
"dependencies": {
|
|
17
|
-
"@quatrain/backend": "^1.1.
|
|
23
|
+
"@quatrain/backend": "^1.1.21",
|
|
18
24
|
"@quatrain/cloudwrapper": "^1.1.14",
|
|
19
|
-
"@
|
|
20
|
-
"@supabase/supabase-js": "^2.48.1"
|
|
25
|
+
"@supabase/supabase-js": "^2.57.2"
|
|
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": "^
|
|
31
|
+
"jest": "^30.1.2",
|
|
27
32
|
"jest-node-exports-resolver": "^1.1.6",
|
|
28
|
-
"jest-serial-runner": "^1.2.
|
|
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