@aurabase/functions 0.5.3 → 0.5.4

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/README.md CHANGED
@@ -144,6 +144,14 @@ const { data: parNom } = await aura.functions.get('hello-world')
144
144
  ### Jobs asynchrones (extra vs `@supabase/functions-js`)
145
145
 
146
146
  ```ts
147
+ // Enfiler un nouveau job d'exécution en arrière-plan
148
+ const { data: jobInfo, error } = await aura.functions.enqueueJob('generate-report', {
149
+ payload: { month: '2026-10', sendEmail: true },
150
+ maxAttempts: 3,
151
+ delaySecs: 60, // exécuter dans 60 secondes
152
+ })
153
+
154
+ // Consulter les jobs
147
155
  const { data: jobs } = await aura.functions.listJobs({ status: 'pending', limit: 20 })
148
156
  const { data: job } = await aura.functions.getJob(jobs![0]!.id)
149
157
  ```
@@ -159,14 +167,19 @@ const { data: crons } = await aura.functions.listCronJobs()
159
167
  await aura.functions.deleteCronJob('nightly-cleanup')
160
168
  ```
161
169
 
170
+ ## Contrat d'invocation et comportement des erreurs
171
+
172
+ - **Déballage transparent :** En mode `responseType: 'json'` (défaut), `data` contient directement le type attendu `T` renvoyé par votre fonction. Pour préserver la compatibilité avec le code existant lisant l'enveloppe, les propriétés `status` et `body` restent accessibles sur l'objet retourné.
173
+ - **Propagation des erreurs HTTP applicatives :** Si votre fonction répond avec un statut HTTP $\ge 400$, le SDK lève désormais une `FunctionsHttpError` avec le statut réel de votre fonction (`error.status`) et son message d'erreur, même lorsque le transport réseau sous-jacent utilise une enveloppe.
174
+ - **Relais et disponibilité :** En cas d'indisponibilité du runtime ou de coupure réseau, une `FunctionsRelayError` est renvoyée.
175
+ - **Propagation d'identité sécurisée :** Les en-têtes d'autorisation (`Authorization: Bearer <token>`, `apikey`) sont validés par la passerelle Aurabase et propagés de façon étanche vers le worker de la fonction (`x-aura-user-id`, `x-aura-role`), protégeant votre backend contre toute falsification client.
176
+
162
177
  ## Limitations
163
178
 
164
- - Pas de streaming de réponse : `invoke()` bufferise toujours l'intégralité du corps avant de résoudre (pas de lecture incrémentale, pas de réponse SSE/chunked exploitable). `responseType` change le DÉCODAGE du corps bufferisé, pas ce point.
165
- - Pas de sélection de région d'exécution.
166
- - `headers` est transmis à la fonction invoquée — ce ne sont pas des headers de transport HTTP (pas de contrôle sur ceux injectés par le pipeline : `apikey`, `Authorization`). La fonction ne voit QUE les en-têtes que vous passez (plus le `content-type` par défaut) : les en-têtes internes du transport ne lui sont pas exposés.
167
- - Le statut réel de la fonction n'est visible qu'avec un `responseType` autre que `'json'` : en mode `'json'` la réponse est enveloppée et le transport rend toujours HTTP 200 (le statut de la fonction est alors dans `data.status`).
168
- - Pas de `getCronJob()` ni `updateCronJob()` : seuls `listCronJobs`, `createCronJob` et `deleteCronJob` sont exposés. Pas de `deploy()` / `delete()` de fonction depuis le SDK : le déploiement passe par l'API HTTP, la CLI `aura functions deploy` ou le Studio.
169
- - `deleteCronJob(name)` et les routes `PATCH`/`DELETE`/`invoke` d'une fonction attendent un **nom** ; seul `get()` accepte aussi l'identifiant.
179
+ - Pas de streaming de réponse : `invoke()` bufferise l'intégralité du corps avant de résoudre. `responseType` configure le décodage du corps (`json`, `text`, `blob`, `arrayBuffer`).
180
+ - Pas de sélection de région d'exécution (exécution sur le cluster Edge actif).
181
+ - Les en-têtes de cadrage de transport (`content-length`, `connection`, `host`, etc.) ne sont pas transmissibles à la fonction.
182
+ - Pas de `getCronJob()` ni `updateCronJob()` : seuls `listCronJobs`, `createCronJob` et `deleteCronJob` sont exposés. Le déploiement s'effectue via le CLI `aura functions deploy` ou Aurabase Studio.
170
183
 
171
184
  ## Documentation
172
185
 
package/dist/index.cjs CHANGED
@@ -27,7 +27,7 @@ var FunctionsRelayError = class _FunctionsRelayError extends core.AuraError {
27
27
  };
28
28
  function toFunctionsError(err, context) {
29
29
  const body = err.details != null ? err.details : { error: { code: err.code, message: err.message } };
30
- return isRelayStatus(err.status) ? new FunctionsRelayError(err.status, body, err.details) : new FunctionsHttpError(err.status, body, err.details);
30
+ return isRelayStatus(err.status) ? new FunctionsRelayError(err.status, body, context ?? err.details) : new FunctionsHttpError(err.status, body, context ?? err.details);
31
31
  }
32
32
  var FunctionsService = class extends core.BaseService {
33
33
  constructor(client) {
@@ -108,6 +108,47 @@ var FunctionsService = class extends core.BaseService {
108
108
  });
109
109
  if (res2.error && controller.signal.aborted) return timeoutError();
110
110
  if (res2.error) return { data: null, error: toFunctionsError(res2.error) };
111
+ const rawData = res2.data;
112
+ if (rawData && typeof rawData === "object" && "body" in rawData && "status" in rawData) {
113
+ const invokeStatus = rawData.status;
114
+ const invokeBody = rawData.body;
115
+ if (typeof invokeStatus === "number" && invokeStatus >= 400) {
116
+ const errPayload = typeof invokeBody === "object" && invokeBody !== null ? invokeBody : { message: String(invokeBody) };
117
+ const errorMsg = typeof errPayload.message === "string" ? errPayload.message : typeof errPayload.error === "string" ? errPayload.error : typeof errPayload.error?.message === "string" ? String(errPayload.error.message) : `Function returned HTTP ${invokeStatus}`;
118
+ const normalizedBody = {
119
+ ...errPayload,
120
+ message: errorMsg,
121
+ error: {
122
+ code: typeof errPayload.code === "string" ? errPayload.code : `http_${invokeStatus}`,
123
+ message: errorMsg
124
+ }
125
+ };
126
+ return {
127
+ data: null,
128
+ error: toFunctionsError(new core.AuraError(invokeStatus, normalizedBody), invokeBody)
129
+ };
130
+ }
131
+ let unwrappedData = invokeBody;
132
+ if (unwrappedData !== null && typeof unwrappedData === "object") {
133
+ if (!("body" in unwrappedData)) {
134
+ Object.defineProperty(unwrappedData, "body", {
135
+ value: unwrappedData,
136
+ enumerable: false,
137
+ writable: true,
138
+ configurable: true
139
+ });
140
+ }
141
+ if (!("status" in unwrappedData)) {
142
+ Object.defineProperty(unwrappedData, "status", {
143
+ value: invokeStatus,
144
+ enumerable: false,
145
+ writable: true,
146
+ configurable: true
147
+ });
148
+ }
149
+ }
150
+ return { data: unwrappedData, error: null, meta: res2.meta };
151
+ }
111
152
  return res2;
112
153
  }
113
154
  const client = this.client;
@@ -184,6 +225,29 @@ var FunctionsService = class extends core.BaseService {
184
225
  async getJob(jobId) {
185
226
  return this.request("GET", `${this.prefix}/jobs/${jobId}`);
186
227
  }
228
+ /**
229
+ * Enfile un job d'exécution asynchrone en arrière-plan pour une fonction.
230
+ *
231
+ * @param functionName Nom de la fonction à exécuter.
232
+ * @param options Paramètres optionnels : charge utile (`payload`), délai avant exécution (`delaySecs`)
233
+ * et nombre maximum de tentatives (`maxAttempts`).
234
+ */
235
+ async enqueueJob(functionName, options) {
236
+ if (!functionName || typeof functionName !== "string" || functionName.trim() === "") {
237
+ return {
238
+ data: null,
239
+ error: new core.AuraError(400, {
240
+ error: { code: "bad_request", message: "Function name is required to enqueue a job" }
241
+ })
242
+ };
243
+ }
244
+ return this.request("POST", `${this.prefix}/jobs`, {
245
+ function_name: functionName.trim(),
246
+ payload: options?.payload,
247
+ delay_secs: options?.delaySecs,
248
+ max_attempts: options?.maxAttempts
249
+ });
250
+ }
187
251
  // -- Cron -------------------------------------------------------------------
188
252
  async listCronJobs() {
189
253
  return this.request("GET", `${this.prefix}/cron`);
package/dist/index.d.cts CHANGED
@@ -98,6 +98,18 @@ declare class FunctionsService extends BaseService {
98
98
  limit?: number;
99
99
  }): Promise<AuraResponse<Job[]>>;
100
100
  getJob(jobId: string): Promise<AuraResponse<Job>>;
101
+ /**
102
+ * Enfile un job d'exécution asynchrone en arrière-plan pour une fonction.
103
+ *
104
+ * @param functionName Nom de la fonction à exécuter.
105
+ * @param options Paramètres optionnels : charge utile (`payload`), délai avant exécution (`delaySecs`)
106
+ * et nombre maximum de tentatives (`maxAttempts`).
107
+ */
108
+ enqueueJob(functionName: string, options?: {
109
+ payload?: unknown;
110
+ delaySecs?: number;
111
+ maxAttempts?: number;
112
+ }): Promise<AuraResponse<Job>>;
101
113
  listCronJobs(): Promise<AuraResponse<CronJob[]>>;
102
114
  createCronJob(name: string, schedule: string, functionName: string, options?: {
103
115
  payload?: unknown;
package/dist/index.d.ts CHANGED
@@ -98,6 +98,18 @@ declare class FunctionsService extends BaseService {
98
98
  limit?: number;
99
99
  }): Promise<AuraResponse<Job[]>>;
100
100
  getJob(jobId: string): Promise<AuraResponse<Job>>;
101
+ /**
102
+ * Enfile un job d'exécution asynchrone en arrière-plan pour une fonction.
103
+ *
104
+ * @param functionName Nom de la fonction à exécuter.
105
+ * @param options Paramètres optionnels : charge utile (`payload`), délai avant exécution (`delaySecs`)
106
+ * et nombre maximum de tentatives (`maxAttempts`).
107
+ */
108
+ enqueueJob(functionName: string, options?: {
109
+ payload?: unknown;
110
+ delaySecs?: number;
111
+ maxAttempts?: number;
112
+ }): Promise<AuraResponse<Job>>;
101
113
  listCronJobs(): Promise<AuraResponse<CronJob[]>>;
102
114
  createCronJob(name: string, schedule: string, functionName: string, options?: {
103
115
  payload?: unknown;
package/dist/index.js CHANGED
@@ -25,7 +25,7 @@ var FunctionsRelayError = class _FunctionsRelayError extends AuraError {
25
25
  };
26
26
  function toFunctionsError(err, context) {
27
27
  const body = err.details != null ? err.details : { error: { code: err.code, message: err.message } };
28
- return isRelayStatus(err.status) ? new FunctionsRelayError(err.status, body, err.details) : new FunctionsHttpError(err.status, body, err.details);
28
+ return isRelayStatus(err.status) ? new FunctionsRelayError(err.status, body, context ?? err.details) : new FunctionsHttpError(err.status, body, context ?? err.details);
29
29
  }
30
30
  var FunctionsService = class extends BaseService {
31
31
  constructor(client) {
@@ -106,6 +106,47 @@ var FunctionsService = class extends BaseService {
106
106
  });
107
107
  if (res2.error && controller.signal.aborted) return timeoutError();
108
108
  if (res2.error) return { data: null, error: toFunctionsError(res2.error) };
109
+ const rawData = res2.data;
110
+ if (rawData && typeof rawData === "object" && "body" in rawData && "status" in rawData) {
111
+ const invokeStatus = rawData.status;
112
+ const invokeBody = rawData.body;
113
+ if (typeof invokeStatus === "number" && invokeStatus >= 400) {
114
+ const errPayload = typeof invokeBody === "object" && invokeBody !== null ? invokeBody : { message: String(invokeBody) };
115
+ const errorMsg = typeof errPayload.message === "string" ? errPayload.message : typeof errPayload.error === "string" ? errPayload.error : typeof errPayload.error?.message === "string" ? String(errPayload.error.message) : `Function returned HTTP ${invokeStatus}`;
116
+ const normalizedBody = {
117
+ ...errPayload,
118
+ message: errorMsg,
119
+ error: {
120
+ code: typeof errPayload.code === "string" ? errPayload.code : `http_${invokeStatus}`,
121
+ message: errorMsg
122
+ }
123
+ };
124
+ return {
125
+ data: null,
126
+ error: toFunctionsError(new AuraError(invokeStatus, normalizedBody), invokeBody)
127
+ };
128
+ }
129
+ let unwrappedData = invokeBody;
130
+ if (unwrappedData !== null && typeof unwrappedData === "object") {
131
+ if (!("body" in unwrappedData)) {
132
+ Object.defineProperty(unwrappedData, "body", {
133
+ value: unwrappedData,
134
+ enumerable: false,
135
+ writable: true,
136
+ configurable: true
137
+ });
138
+ }
139
+ if (!("status" in unwrappedData)) {
140
+ Object.defineProperty(unwrappedData, "status", {
141
+ value: invokeStatus,
142
+ enumerable: false,
143
+ writable: true,
144
+ configurable: true
145
+ });
146
+ }
147
+ }
148
+ return { data: unwrappedData, error: null, meta: res2.meta };
149
+ }
109
150
  return res2;
110
151
  }
111
152
  const client = this.client;
@@ -182,6 +223,29 @@ var FunctionsService = class extends BaseService {
182
223
  async getJob(jobId) {
183
224
  return this.request("GET", `${this.prefix}/jobs/${jobId}`);
184
225
  }
226
+ /**
227
+ * Enfile un job d'exécution asynchrone en arrière-plan pour une fonction.
228
+ *
229
+ * @param functionName Nom de la fonction à exécuter.
230
+ * @param options Paramètres optionnels : charge utile (`payload`), délai avant exécution (`delaySecs`)
231
+ * et nombre maximum de tentatives (`maxAttempts`).
232
+ */
233
+ async enqueueJob(functionName, options) {
234
+ if (!functionName || typeof functionName !== "string" || functionName.trim() === "") {
235
+ return {
236
+ data: null,
237
+ error: new AuraError(400, {
238
+ error: { code: "bad_request", message: "Function name is required to enqueue a job" }
239
+ })
240
+ };
241
+ }
242
+ return this.request("POST", `${this.prefix}/jobs`, {
243
+ function_name: functionName.trim(),
244
+ payload: options?.payload,
245
+ delay_secs: options?.delaySecs,
246
+ max_attempts: options?.maxAttempts
247
+ });
248
+ }
185
249
  // -- Cron -------------------------------------------------------------------
186
250
  async listCronJobs() {
187
251
  return this.request("GET", `${this.prefix}/cron`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurabase/functions",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Service edge functions du SDK Aurabase (invoke, jobs asynchrones, cron)",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -45,7 +45,7 @@
45
45
  "functions"
46
46
  ],
47
47
  "dependencies": {
48
- "@aurabase/core": "0.5.3"
48
+ "@aurabase/core": "0.5.4"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@types/node": "20.14.15",