@resonatehq/supabase 0.1.10 → 0.1.11

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/index.d.ts CHANGED
@@ -1,10 +1,18 @@
1
1
  import { Context, type Encryptor, type Func } from "@resonatehq/sdk";
2
+ type OnTerminateCallback = (result: {
3
+ status: "completed";
4
+ result?: any;
5
+ } | {
6
+ status: "suspended";
7
+ waitingOn: string[];
8
+ }) => Promise<void>;
2
9
  export { Context };
3
10
  export declare class Resonate {
4
11
  private registry;
5
12
  private dependencies;
6
13
  private verbose;
7
14
  private encryptor;
15
+ private encoder;
8
16
  constructor({ verbose, encryptor, }?: {
9
17
  verbose?: boolean;
10
18
  encryptor?: Encryptor;
@@ -16,6 +24,7 @@ export declare class Resonate {
16
24
  version?: number;
17
25
  }): void;
18
26
  setDependency(name: string, obj: any): void;
19
- handler(req: Request): Promise<Response>;
20
- httpHandler(): Deno.HttpServer;
27
+ handler(req: Request, onTerminate?: OnTerminateCallback): Promise<Response>;
28
+ private decrypt;
29
+ httpHandler(onTerminate?: OnTerminateCallback): Deno.HttpServer;
21
30
  }
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export class Resonate {
5
5
  this.dependencies = new Map();
6
6
  this.verbose = verbose;
7
7
  this.encryptor = encryptor ?? new NoopEncryptor();
8
+ this.encoder = new JsonEncoder();
8
9
  }
9
10
  register(nameOrFunc, funcOrOptions, maybeOptions = {}) {
10
11
  const { version = 1 } = (typeof funcOrOptions === "object" ? funcOrOptions : maybeOptions) ?? {};
@@ -15,7 +16,7 @@ export class Resonate {
15
16
  setDependency(name, obj) {
16
17
  this.dependencies.set(name, obj);
17
18
  }
18
- async handler(req) {
19
+ async handler(req, onTerminate) {
19
20
  try {
20
21
  if (req.method !== "POST") {
21
22
  return new Response(JSON.stringify({ error: "Method not allowed. Use POST." }), {
@@ -40,7 +41,6 @@ export class Resonate {
40
41
  status: 400,
41
42
  });
42
43
  }
43
- const encoder = new JsonEncoder();
44
44
  const clock = new WallClock();
45
45
  const tracer = new NoopTracer();
46
46
  const network = new HttpNetwork({
@@ -56,7 +56,7 @@ export class Resonate {
56
56
  ttl: 30 * 1000,
57
57
  clock,
58
58
  network,
59
- handler: new Handler(network, encoder, this.encryptor),
59
+ handler: new Handler(network, this.encoder, this.encryptor),
60
60
  registry: this.registry,
61
61
  heartbeat: new NoopHeartbeat(),
62
62
  dependencies: this.dependencies,
@@ -68,39 +68,39 @@ export class Resonate {
68
68
  tracer,
69
69
  });
70
70
  const task = { kind: "unclaimed", task: body.task };
71
- const completion = new Promise((resolve) => {
71
+ return new Promise((resolve, reject) => {
72
72
  resonateInner.process(tracer.startSpan(task.task.rootPromiseId, clock.now()), task, (error, status) => {
73
73
  if (error || !status) {
74
- resolve(new Response(JSON.stringify({
74
+ return reject({
75
75
  error: "Task processing failed",
76
76
  details: { error, status },
77
- }), {
78
- status: 500,
79
- }));
80
- return;
81
- }
82
- if (status.kind === "completed") {
83
- resolve(new Response(JSON.stringify({
84
- status: "completed",
85
- result: status.promise.value,
86
- requestUrl: url,
87
- }), {
88
- status: 200,
89
- }));
90
- return;
91
- }
92
- else if (status.kind === "suspended") {
93
- resolve(new Response(JSON.stringify({
94
- status: "suspended",
95
- requestUrl: url,
96
- }), {
97
- status: 200,
98
- }));
99
- return;
77
+ });
100
78
  }
79
+ return resolve({
80
+ ...(status.kind === "completed"
81
+ ? { status: status.kind, result: status.promise.value }
82
+ : {
83
+ status: status.kind,
84
+ waitingOn: status.callbacks.map((cb) => cb.promiseId),
85
+ }),
86
+ });
101
87
  });
88
+ })
89
+ .then(async (res) => {
90
+ try {
91
+ await onTerminate?.(this.decrypt(res));
92
+ }
93
+ catch (e) {
94
+ console.error("onTerminate failed", e);
95
+ }
96
+ return new Response(JSON.stringify({
97
+ ...res,
98
+ requestUrl: url,
99
+ }), { status: 200 });
100
+ })
101
+ .catch((err) => {
102
+ return new Response(JSON.stringify(err), { status: 500 });
102
103
  });
103
- return completion;
104
104
  }
105
105
  catch (error) {
106
106
  return new Response(JSON.stringify({
@@ -108,9 +108,17 @@ export class Resonate {
108
108
  }), { status: 500 });
109
109
  }
110
110
  }
111
- httpHandler() {
111
+ decrypt(res) {
112
+ if (res.status !== "completed")
113
+ return res;
114
+ return {
115
+ ...res,
116
+ result: this.encoder.decode(this.encryptor.decrypt(res.result)),
117
+ };
118
+ }
119
+ httpHandler(onTerminate) {
112
120
  return Deno.serve(async (req) => {
113
- return await this.handler(req);
121
+ return await this.handler(req, onTerminate);
114
122
  });
115
123
  }
116
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resonatehq/supabase",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Resonate FaaS handler for Supabase Edge Functions (TypeScript)",
5
5
  "repository": {
6
6
  "url": "https://github.com/resonatehq/resonate-faas-supabase-ts"
package/src/index.ts CHANGED
@@ -15,6 +15,12 @@ import {
15
15
  WallClock,
16
16
  } from "@resonatehq/sdk";
17
17
 
18
+ type OnTerminateCallback = (
19
+ result:
20
+ | { status: "completed"; result?: any }
21
+ | { status: "suspended"; waitingOn: string[] },
22
+ ) => Promise<void>;
23
+
18
24
  export { Context };
19
25
 
20
26
  export class Resonate {
@@ -22,6 +28,7 @@ export class Resonate {
22
28
  private dependencies = new Map<string, any>();
23
29
  private verbose: boolean;
24
30
  private encryptor: Encryptor;
31
+ private encoder: JsonEncoder;
25
32
 
26
33
  constructor({
27
34
  verbose = false,
@@ -29,6 +36,7 @@ export class Resonate {
29
36
  }: { verbose?: boolean; encryptor?: Encryptor } = {}) {
30
37
  this.verbose = verbose;
31
38
  this.encryptor = encryptor ?? new NoopEncryptor();
39
+ this.encoder = new JsonEncoder();
32
40
  }
33
41
 
34
42
  public register<F extends Func>(
@@ -68,7 +76,10 @@ export class Resonate {
68
76
  this.dependencies.set(name, obj);
69
77
  }
70
78
 
71
- public async handler(req: Request): Promise<Response> {
79
+ public async handler(
80
+ req: Request,
81
+ onTerminate?: OnTerminateCallback,
82
+ ): Promise<Response> {
72
83
  try {
73
84
  if (req.method !== "POST") {
74
85
  return new Response(
@@ -109,7 +120,6 @@ export class Resonate {
109
120
  );
110
121
  }
111
122
 
112
- const encoder = new JsonEncoder();
113
123
  const clock = new WallClock();
114
124
  const tracer = new NoopTracer();
115
125
  const network = new HttpNetwork({
@@ -126,7 +136,7 @@ export class Resonate {
126
136
  ttl: 30 * 1000,
127
137
  clock,
128
138
  network,
129
- handler: new Handler(network, encoder, this.encryptor),
139
+ handler: new Handler(network, this.encoder, this.encryptor),
130
140
  registry: this.registry,
131
141
  heartbeat: new NoopHeartbeat(),
132
142
  dependencies: this.dependencies,
@@ -140,58 +150,49 @@ export class Resonate {
140
150
 
141
151
  const task: Task = { kind: "unclaimed", task: body.task };
142
152
 
143
- const completion: Promise<Response> = new Promise((resolve) => {
153
+ return new Promise<
154
+ | { status: "completed"; result?: any }
155
+ | { status: "suspended"; waitingOn: string[] }
156
+ >((resolve, reject) => {
144
157
  resonateInner.process(
145
158
  tracer.startSpan(task.task.rootPromiseId, clock.now()),
146
159
  task,
147
160
  (error, status) => {
148
161
  if (error || !status) {
149
- resolve(
150
- new Response(
151
- JSON.stringify({
152
- error: "Task processing failed",
153
- details: { error, status },
154
- }),
155
- {
156
- status: 500,
157
- },
158
- ),
159
- );
160
- return;
162
+ return reject({
163
+ error: "Task processing failed",
164
+ details: { error, status },
165
+ });
161
166
  }
162
167
 
163
- if (status.kind === "completed") {
164
- resolve(
165
- new Response(
166
- JSON.stringify({
167
- status: "completed",
168
- result: status.promise.value,
169
- requestUrl: url,
168
+ return resolve({
169
+ ...(status.kind === "completed"
170
+ ? { status: status.kind, result: status.promise.value }
171
+ : {
172
+ status: status.kind,
173
+ waitingOn: status.callbacks.map((cb) => cb.promiseId),
170
174
  }),
171
- {
172
- status: 200,
173
- },
174
- ),
175
- );
176
- return;
177
- } else if (status.kind === "suspended") {
178
- resolve(
179
- new Response(
180
- JSON.stringify({
181
- status: "suspended",
182
- requestUrl: url,
183
- }),
184
- {
185
- status: 200,
186
- },
187
- ),
188
- );
189
- return;
190
- }
175
+ });
191
176
  },
192
177
  );
193
- });
194
- return completion;
178
+ })
179
+ .then(async (res) => {
180
+ try {
181
+ await onTerminate?.(this.decrypt(res));
182
+ } catch (e) {
183
+ console.error("onTerminate failed", e);
184
+ }
185
+ return new Response(
186
+ JSON.stringify({
187
+ ...res,
188
+ requestUrl: url,
189
+ }),
190
+ { status: 200 },
191
+ );
192
+ })
193
+ .catch((err) => {
194
+ return new Response(JSON.stringify(err), { status: 500 });
195
+ });
195
196
  } catch (error) {
196
197
  return new Response(
197
198
  JSON.stringify({
@@ -202,9 +203,24 @@ export class Resonate {
202
203
  }
203
204
  }
204
205
 
205
- public httpHandler(): Deno.HttpServer {
206
+ private decrypt(
207
+ res:
208
+ | { status: "completed"; result?: any }
209
+ | { status: "suspended"; waitingOn: string[] },
210
+ ):
211
+ | { status: "completed"; result?: any }
212
+ | { status: "suspended"; waitingOn: string[] } {
213
+ if (res.status !== "completed") return res;
214
+
215
+ return {
216
+ ...res,
217
+ result: this.encoder.decode(this.encryptor.decrypt(res.result)),
218
+ };
219
+ }
220
+
221
+ public httpHandler(onTerminate?: OnTerminateCallback): Deno.HttpServer {
206
222
  return Deno.serve(async (req: Request) => {
207
- return await this.handler(req);
223
+ return await this.handler(req, onTerminate);
208
224
  });
209
225
  }
210
226
  }