@aurabase/functions 0.4.1 → 0.5.0
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 +82 -8
- package/dist/index.cjs +43 -10
- package/dist/index.d.cts +64 -37
- package/dist/index.d.ts +64 -37
- package/dist/index.js +43 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -23,12 +23,12 @@ Dans l'usage courant, `FunctionsService` n'est jamais instancié directement : i
|
|
|
23
23
|
```ts
|
|
24
24
|
import { createClient } from '@aurabase/aurabase-js'
|
|
25
25
|
|
|
26
|
-
const aura = createClient('
|
|
26
|
+
const aura = createClient('https://mon-projet.aurabase.cloud', 'aura_anon_xxx')
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
### Invocation
|
|
30
30
|
|
|
31
|
-
`invoke<T>()`
|
|
31
|
+
`invoke<T>()` décrit la requête que votre fonction doit recevoir (`{ method, path, headers, body }`) et dé-wrap la réponse JSON du backend — tout est bufferisé, pas de streaming (voir Limitations).
|
|
32
32
|
|
|
33
33
|
```ts
|
|
34
34
|
const { data, error } = await aura.functions.invoke<{ ok: boolean }>('hello-world', {
|
|
@@ -42,7 +42,7 @@ if (!data) throw new Error('réponse vide')
|
|
|
42
42
|
console.log(data.ok)
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Options complètes : `method`, `path` (sous-route à l'intérieur de la fonction), `headers` (transmis à la fonction elle-même, pas des headers de transport), `signal`, `timeout`.
|
|
45
|
+
Options complètes : `body`, `method`, `path` (sous-route à l'intérieur de la fonction), `headers` (transmis à la fonction elle-même, pas des headers de transport), `signal`, `timeout`, `responseType`.
|
|
46
46
|
|
|
47
47
|
```ts
|
|
48
48
|
const controller = new AbortController()
|
|
@@ -61,11 +61,84 @@ if (error?.code === 'timeout') {
|
|
|
61
61
|
}
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
#### Ce que la fonction reçoit
|
|
65
|
+
|
|
66
|
+
`method`, `path`, `headers` et `body` décrivent la requête **vue par votre fonction** — pas le transport HTTP vers Aurabase :
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
await aura.functions.invoke('items', {
|
|
70
|
+
method: 'PUT',
|
|
71
|
+
path: '/items/1',
|
|
72
|
+
headers: { 'x-request-source': 'sdk' },
|
|
73
|
+
body: { qty: 2 },
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// items.ts (runtime Deno)
|
|
79
|
+
Deno.serve(async (req) => {
|
|
80
|
+
req.method // 'PUT'
|
|
81
|
+
new URL(req.url).pathname // '/items/1'
|
|
82
|
+
req.headers.get('x-request-source') // 'sdk'
|
|
83
|
+
await req.json() // { qty: 2 } ← le corps, PAS une enveloppe
|
|
84
|
+
return new Response('ok')
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Défauts si vous ne précisez rien : `POST` sur `/`. `GET` et `HEAD` sont envoyés sans corps (la spec Fetch l'interdit) ; un `body` non-chaîne est sérialisé en JSON et `content-type: application/json` est posé si vous n'en fixez pas un.
|
|
89
|
+
|
|
90
|
+
Sans `body`, la fonction reçoit une requête **sans corps** (parité `@supabase/functions-js`) : un `await req.json()` inconditionnel y échoue. Écrivez `await req.json().catch(() => ({}))` si l'appel peut être fait sans charge utile.
|
|
91
|
+
|
|
92
|
+
Les en-têtes de cadrage/transport (`content-length`, `transfer-encoding`, `connection`, `keep-alive`, `upgrade`, `host`…) passés dans `headers` sont ignorés : ils décrivent le message, pas la requête applicative, et un `content-length` forgé désynchroniserait la lecture du corps.
|
|
93
|
+
|
|
94
|
+
> **Changement de comportement (correctif C20).** Jusqu'à ce correctif, une fonction Deno recevait invariablement un `POST /invoke/{project_id}/{name}` dont le corps était l'enveloppe `{ method, path, headers, body }`. Une fonction écrite contre cet ancien comportement (`const { body } = await req.json()`) doit être ajustée : lisez désormais `await req.json()` directement. Les modèles de fonctions du Studio et les exemples de ce README étaient déjà écrits selon le contrat correct — ils ne fonctionnaient donc pas avant ce correctif.
|
|
95
|
+
|
|
96
|
+
#### Type de réponse (`responseType`)
|
|
97
|
+
|
|
98
|
+
Défaut `'json'` : le corps est parsé et dé-wrappé (`{data}` → contenu). Les autres valeurs renvoient le corps brut de la fonction, sans dé-wrapping, et `error.status` porte alors le **vrai** statut HTTP de la fonction :
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const { data: csv } = await aura.functions.invoke<string>('export', { responseType: 'text' })
|
|
102
|
+
const { data: png } = await aura.functions.invoke<Blob>('render', { responseType: 'blob' })
|
|
103
|
+
const { data: buf } = await aura.functions.invoke<ArrayBuffer>('pdf', { responseType: 'arrayBuffer' })
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Erreurs typées
|
|
107
|
+
|
|
108
|
+
Parité `@supabase/functions-js`. Les deux classes sous-classent `AuraError` (donc `isAuraError()`, `status`, `code` restent valides) :
|
|
109
|
+
|
|
110
|
+
| Classe | Quand | Statuts |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `FunctionsHttpError` | **votre fonction** a répondu en erreur | tout non-2xx hors relais (400, 404, 500…) |
|
|
113
|
+
| `FunctionsRelayError` | la réponse de votre fonction n'est jamais arrivée | 0 (réseau), 502, 503, 504 |
|
|
114
|
+
|
|
115
|
+
Deux statuts de relais à ne pas confondre — ils ne se rejouent pas de la même façon :
|
|
116
|
+
|
|
117
|
+
- **503 `SERVICE_UNAVAILABLE`** : l'edge-runtime est injoignable, votre fonction **n'a pas** été exécutée → réessayer est sûr ;
|
|
118
|
+
- **504 `TIMEOUT`** : votre fonction a dépassé son budget (`permissions.timeout_secs` + marge). Elle **a été lancée** et a pu produire ses effets de bord ; seule la réponse manque → ne rejouez pas une opération non idempotente à l'aveugle.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { FunctionsHttpError, FunctionsRelayError, isRelayStatus } from '@aurabase/functions'
|
|
122
|
+
|
|
123
|
+
const { error } = await aura.functions.invoke('checkout', { responseType: 'text' })
|
|
124
|
+
if (error instanceof FunctionsRelayError) {
|
|
125
|
+
// rien n'a été exécuté côté fonction → réessayer est sûr
|
|
126
|
+
} else if (error instanceof FunctionsHttpError) {
|
|
127
|
+
// la fonction a bien tourné et a répondu en erreur → déboguer VOTRE code
|
|
128
|
+
}
|
|
129
|
+
isRelayStatus(error?.status ?? 0) // même décision, sans instanceof
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Une annulation (`timeout`/`signal`) reste une `AuraError` de code `timeout`, jamais une de ces deux classes.
|
|
133
|
+
|
|
64
134
|
### Fonctions (métadonnées)
|
|
65
135
|
|
|
136
|
+
`get()` accepte le **nom** ou l'**identifiant** (B10) — en cas d'homonymie, le nom l'emporte :
|
|
137
|
+
|
|
66
138
|
```ts
|
|
67
139
|
const { data: fns } = await aura.functions.list()
|
|
68
|
-
const { data:
|
|
140
|
+
const { data: parId } = await aura.functions.get(fns![0]!.id)
|
|
141
|
+
const { data: parNom } = await aura.functions.get('hello-world')
|
|
69
142
|
```
|
|
70
143
|
|
|
71
144
|
### Jobs asynchrones (extra vs `@supabase/functions-js`)
|
|
@@ -88,11 +161,12 @@ await aura.functions.deleteCronJob('nightly-cleanup')
|
|
|
88
161
|
|
|
89
162
|
## Limitations
|
|
90
163
|
|
|
91
|
-
- Pas de streaming de réponse : `invoke()` bufferise toujours l'intégralité du corps
|
|
92
|
-
- Pas de `responseType` (`json`/`text`/`blob`) : la réponse est toujours parsée comme JSON puis dé-wrappée (`{data}` → contenu).
|
|
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.
|
|
93
165
|
- Pas de sélection de région d'exécution.
|
|
94
|
-
- `headers` est transmis
|
|
95
|
-
-
|
|
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.
|
|
96
170
|
|
|
97
171
|
## Documentation
|
|
98
172
|
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var core = require('@aurabase/core');
|
|
4
4
|
|
|
5
|
-
// src/
|
|
5
|
+
// src/errors.ts
|
|
6
6
|
var RELAY_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
7
7
|
function isRelayStatus(status) {
|
|
8
8
|
return status === 0 || RELAY_STATUSES.has(status);
|
|
@@ -29,19 +29,36 @@ function toFunctionsError(err, context) {
|
|
|
29
29
|
const body = err.details != null ? err.details : { error: { code: err.code, message: err.message } };
|
|
30
30
|
return isRelayStatus(err.status) ? new FunctionsRelayError(err.status, body, err.details) : new FunctionsHttpError(err.status, body, err.details);
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
// src/FunctionsService.ts
|
|
34
32
|
var FunctionsService = class extends core.BaseService {
|
|
35
33
|
constructor(client) {
|
|
36
34
|
super(client);
|
|
37
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Préfixe des routes de ce service. Aucun identifiant de projet : il est injecté par
|
|
38
|
+
* la passerelle depuis le sous-domaine du projet (`inject_project_id`,
|
|
39
|
+
* `services/aura-gateway/src/middleware/project_subdomain.rs`).
|
|
40
|
+
*/
|
|
38
41
|
get prefix() {
|
|
39
|
-
return
|
|
42
|
+
return "/v1/functions";
|
|
40
43
|
}
|
|
41
44
|
// -- Function invocation ----------------------------------------------------
|
|
42
45
|
/**
|
|
43
46
|
* Invoque une edge function.
|
|
44
47
|
*
|
|
48
|
+
* Ce que la fonction REÇOIT (C20) : la requête vue par votre code est celle que vous
|
|
49
|
+
* décrivez ici — `method` (défaut `POST`), `path` (défaut `/`), `headers` et `body`
|
|
50
|
+
* APPLICATIF. Sur le runtime Deno :
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* // invoke('x', { method: 'PUT', path: '/items/1', headers: { 'x-src': 'sdk' }, body: { a: 1 } })
|
|
54
|
+
* Deno.serve(async (req) => {
|
|
55
|
+
* req.method // 'PUT'
|
|
56
|
+
* new URL(req.url).pathname // '/items/1'
|
|
57
|
+
* req.headers.get('x-src') // 'sdk'
|
|
58
|
+
* await req.json() // { a: 1 } ← le corps, PAS l'enveloppe
|
|
59
|
+
* })
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
45
62
|
* `responseType` (parité `@supabase/functions-js`, défaut `'json'`) :
|
|
46
63
|
* - `'json'` : chemin PIPELINE inchangé (refresh proactif centralisé, injection
|
|
47
64
|
* apikey/Authorization, dé-wrapping `{data,meta}`, retryConfig). Non-régression.
|
|
@@ -52,8 +69,9 @@ var FunctionsService = class extends core.BaseService {
|
|
|
52
69
|
*
|
|
53
70
|
* Erreurs typées (parité) : une réponse HTTP non-2xx → `FunctionsHttpError`, une
|
|
54
71
|
* panne de relais/infra (réseau, 502/503/504) → `FunctionsRelayError` ; les deux
|
|
55
|
-
* sous-classent `AuraError
|
|
56
|
-
* `
|
|
72
|
+
* sous-classent `AuraError`, ce qui est l'invariant du typage d'erreur du SDK :
|
|
73
|
+
* `isAuraError` reste vrai et le champ `error` reste lisible sur les deux. `timeout`/
|
|
74
|
+
* `signal` produisent le code `timeout`.
|
|
57
75
|
*/
|
|
58
76
|
async invoke(slug, options) {
|
|
59
77
|
const controller = new AbortController();
|
|
@@ -69,7 +87,12 @@ var FunctionsService = class extends core.BaseService {
|
|
|
69
87
|
if (timeoutId) clearTimeout(timeoutId);
|
|
70
88
|
if (userSignal) userSignal.removeEventListener("abort", onAbort);
|
|
71
89
|
};
|
|
72
|
-
const envelope = {
|
|
90
|
+
const envelope = {
|
|
91
|
+
method: options?.method,
|
|
92
|
+
path: options?.path,
|
|
93
|
+
headers: options?.headers,
|
|
94
|
+
body: options?.body
|
|
95
|
+
};
|
|
73
96
|
const responseType = options?.responseType ?? "json";
|
|
74
97
|
const timeoutError = () => ({
|
|
75
98
|
data: null,
|
|
@@ -126,7 +149,10 @@ var FunctionsService = class extends core.BaseService {
|
|
|
126
149
|
return {
|
|
127
150
|
data: null,
|
|
128
151
|
error: new FunctionsRelayError(0, {
|
|
129
|
-
error: {
|
|
152
|
+
error: {
|
|
153
|
+
code: "network_error",
|
|
154
|
+
message: err instanceof Error ? err.message : "Function relay error"
|
|
155
|
+
}
|
|
130
156
|
})
|
|
131
157
|
};
|
|
132
158
|
} finally {
|
|
@@ -137,8 +163,15 @@ var FunctionsService = class extends core.BaseService {
|
|
|
137
163
|
async list() {
|
|
138
164
|
return this.request("GET", `${this.prefix}`);
|
|
139
165
|
}
|
|
140
|
-
|
|
141
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Lit une fonction par son **nom** ou par son **identifiant** (B10).
|
|
168
|
+
*
|
|
169
|
+
* Les deux clés sont acceptées : `list()` renvoie `{ id, name, … }` et les deux sont des
|
|
170
|
+
* points d'entrée naturels. En cas d'homonymie (une fonction PEUT s'appeler comme un UUID),
|
|
171
|
+
* le NOM l'emporte. Une valeur qui n'est ni l'un ni l'autre produit un 404 qui le dit.
|
|
172
|
+
*/
|
|
173
|
+
async get(nameOrId) {
|
|
174
|
+
return this.request("GET", `${this.prefix}/${nameOrId}`);
|
|
142
175
|
}
|
|
143
176
|
// -- Jobs -------------------------------------------------------------------
|
|
144
177
|
async listJobs(options) {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,66 @@
|
|
|
1
|
-
import { BaseService, AuraClientLike, AuraResponse, FunctionDef, Job, CronJob
|
|
1
|
+
import { AuraError, BaseService, AuraClientLike, AuraResponse, FunctionDef, Job, CronJob } from '@aurabase/core';
|
|
2
2
|
export { AuraResponse, CronJob, FunctionDef, Job } from '@aurabase/core';
|
|
3
3
|
|
|
4
|
+
declare function isRelayStatus(status: number): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Erreur HTTP APPLICATIVE renvoyée par l'invocation d'une edge function
|
|
7
|
+
* (réponse HTTP non-2xx). Parité `FunctionsHttpError` de `@supabase/functions-js`.
|
|
8
|
+
* Sous-classe `AuraError` → `isAuraError()` reste vrai, `status` porte le code HTTP.
|
|
9
|
+
* `context` conserve le corps déjà lu (JSON/texte) pour inspection.
|
|
10
|
+
*/
|
|
11
|
+
declare class FunctionsHttpError extends AuraError {
|
|
12
|
+
readonly context: unknown;
|
|
13
|
+
constructor(status: number, body?: {
|
|
14
|
+
error?: {
|
|
15
|
+
code?: string;
|
|
16
|
+
message?: string;
|
|
17
|
+
};
|
|
18
|
+
message?: string;
|
|
19
|
+
} | null, context?: unknown);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Erreur de RELAIS/infra : fonction injoignable (échec réseau), 502/503/504 de la
|
|
23
|
+
* passerelle, cold start échoué, corps non exploitable. Parité `FunctionsRelayError`
|
|
24
|
+
* de `@supabase/functions-js`. Sous-classe `AuraError`.
|
|
25
|
+
*/
|
|
26
|
+
declare class FunctionsRelayError extends AuraError {
|
|
27
|
+
readonly context: unknown;
|
|
28
|
+
constructor(status: number, body?: {
|
|
29
|
+
error?: {
|
|
30
|
+
code?: string;
|
|
31
|
+
message?: string;
|
|
32
|
+
};
|
|
33
|
+
message?: string;
|
|
34
|
+
} | null, context?: unknown);
|
|
35
|
+
}
|
|
36
|
+
|
|
4
37
|
/** Type de réponse attendu de l'invocation (parité `@supabase/functions-js`). */
|
|
5
38
|
type FunctionsResponseType = 'json' | 'text' | 'blob' | 'arrayBuffer';
|
|
6
39
|
declare class FunctionsService extends BaseService {
|
|
7
40
|
constructor(client: AuraClientLike);
|
|
41
|
+
/**
|
|
42
|
+
* Préfixe des routes de ce service. Aucun identifiant de projet : il est injecté par
|
|
43
|
+
* la passerelle depuis le sous-domaine du projet (`inject_project_id`,
|
|
44
|
+
* `services/aura-gateway/src/middleware/project_subdomain.rs`).
|
|
45
|
+
*/
|
|
8
46
|
private get prefix();
|
|
9
47
|
/**
|
|
10
48
|
* Invoque une edge function.
|
|
11
49
|
*
|
|
50
|
+
* Ce que la fonction REÇOIT (C20) : la requête vue par votre code est celle que vous
|
|
51
|
+
* décrivez ici — `method` (défaut `POST`), `path` (défaut `/`), `headers` et `body`
|
|
52
|
+
* APPLICATIF. Sur le runtime Deno :
|
|
53
|
+
*
|
|
54
|
+
* ```ts
|
|
55
|
+
* // invoke('x', { method: 'PUT', path: '/items/1', headers: { 'x-src': 'sdk' }, body: { a: 1 } })
|
|
56
|
+
* Deno.serve(async (req) => {
|
|
57
|
+
* req.method // 'PUT'
|
|
58
|
+
* new URL(req.url).pathname // '/items/1'
|
|
59
|
+
* req.headers.get('x-src') // 'sdk'
|
|
60
|
+
* await req.json() // { a: 1 } ← le corps, PAS l'enveloppe
|
|
61
|
+
* })
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
12
64
|
* `responseType` (parité `@supabase/functions-js`, défaut `'json'`) :
|
|
13
65
|
* - `'json'` : chemin PIPELINE inchangé (refresh proactif centralisé, injection
|
|
14
66
|
* apikey/Authorization, dé-wrapping `{data,meta}`, retryConfig). Non-régression.
|
|
@@ -19,8 +71,9 @@ declare class FunctionsService extends BaseService {
|
|
|
19
71
|
*
|
|
20
72
|
* Erreurs typées (parité) : une réponse HTTP non-2xx → `FunctionsHttpError`, une
|
|
21
73
|
* panne de relais/infra (réseau, 502/503/504) → `FunctionsRelayError` ; les deux
|
|
22
|
-
* sous-classent `AuraError
|
|
23
|
-
* `
|
|
74
|
+
* sous-classent `AuraError`, ce qui est l'invariant du typage d'erreur du SDK :
|
|
75
|
+
* `isAuraError` reste vrai et le champ `error` reste lisible sur les deux. `timeout`/
|
|
76
|
+
* `signal` produisent le code `timeout`.
|
|
24
77
|
*/
|
|
25
78
|
invoke<T = unknown>(slug: string, options?: {
|
|
26
79
|
body?: unknown;
|
|
@@ -32,7 +85,14 @@ declare class FunctionsService extends BaseService {
|
|
|
32
85
|
responseType?: FunctionsResponseType;
|
|
33
86
|
}): Promise<AuraResponse<T>>;
|
|
34
87
|
list(): Promise<AuraResponse<FunctionDef[]>>;
|
|
35
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Lit une fonction par son **nom** ou par son **identifiant** (B10).
|
|
90
|
+
*
|
|
91
|
+
* Les deux clés sont acceptées : `list()` renvoie `{ id, name, … }` et les deux sont des
|
|
92
|
+
* points d'entrée naturels. En cas d'homonymie (une fonction PEUT s'appeler comme un UUID),
|
|
93
|
+
* le NOM l'emporte. Une valeur qui n'est ni l'un ni l'autre produit un 404 qui le dit.
|
|
94
|
+
*/
|
|
95
|
+
get(nameOrId: string): Promise<AuraResponse<FunctionDef>>;
|
|
36
96
|
listJobs(options?: {
|
|
37
97
|
status?: string;
|
|
38
98
|
limit?: number;
|
|
@@ -45,37 +105,4 @@ declare class FunctionsService extends BaseService {
|
|
|
45
105
|
deleteCronJob(name: string): Promise<AuraResponse<null>>;
|
|
46
106
|
}
|
|
47
107
|
|
|
48
|
-
declare function isRelayStatus(status: number): boolean;
|
|
49
|
-
/**
|
|
50
|
-
* Erreur HTTP APPLICATIVE renvoyée par l'invocation d'une edge function
|
|
51
|
-
* (réponse HTTP non-2xx). Parité `FunctionsHttpError` de `@supabase/functions-js`.
|
|
52
|
-
* Sous-classe `AuraError` → `isAuraError()` reste vrai, `status` porte le code HTTP.
|
|
53
|
-
* `context` conserve le corps déjà lu (JSON/texte) pour inspection.
|
|
54
|
-
*/
|
|
55
|
-
declare class FunctionsHttpError extends AuraError {
|
|
56
|
-
readonly context: unknown;
|
|
57
|
-
constructor(status: number, body?: {
|
|
58
|
-
error?: {
|
|
59
|
-
code?: string;
|
|
60
|
-
message?: string;
|
|
61
|
-
};
|
|
62
|
-
message?: string;
|
|
63
|
-
} | null, context?: unknown);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Erreur de RELAIS/infra : fonction injoignable (échec réseau), 502/503/504 de la
|
|
67
|
-
* passerelle, cold start échoué, corps non exploitable. Parité `FunctionsRelayError`
|
|
68
|
-
* de `@supabase/functions-js`. Sous-classe `AuraError`.
|
|
69
|
-
*/
|
|
70
|
-
declare class FunctionsRelayError extends AuraError {
|
|
71
|
-
readonly context: unknown;
|
|
72
|
-
constructor(status: number, body?: {
|
|
73
|
-
error?: {
|
|
74
|
-
code?: string;
|
|
75
|
-
message?: string;
|
|
76
|
-
};
|
|
77
|
-
message?: string;
|
|
78
|
-
} | null, context?: unknown);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
108
|
export { FunctionsHttpError, FunctionsRelayError, type FunctionsResponseType, FunctionsService, isRelayStatus };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,66 @@
|
|
|
1
|
-
import { BaseService, AuraClientLike, AuraResponse, FunctionDef, Job, CronJob
|
|
1
|
+
import { AuraError, BaseService, AuraClientLike, AuraResponse, FunctionDef, Job, CronJob } from '@aurabase/core';
|
|
2
2
|
export { AuraResponse, CronJob, FunctionDef, Job } from '@aurabase/core';
|
|
3
3
|
|
|
4
|
+
declare function isRelayStatus(status: number): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Erreur HTTP APPLICATIVE renvoyée par l'invocation d'une edge function
|
|
7
|
+
* (réponse HTTP non-2xx). Parité `FunctionsHttpError` de `@supabase/functions-js`.
|
|
8
|
+
* Sous-classe `AuraError` → `isAuraError()` reste vrai, `status` porte le code HTTP.
|
|
9
|
+
* `context` conserve le corps déjà lu (JSON/texte) pour inspection.
|
|
10
|
+
*/
|
|
11
|
+
declare class FunctionsHttpError extends AuraError {
|
|
12
|
+
readonly context: unknown;
|
|
13
|
+
constructor(status: number, body?: {
|
|
14
|
+
error?: {
|
|
15
|
+
code?: string;
|
|
16
|
+
message?: string;
|
|
17
|
+
};
|
|
18
|
+
message?: string;
|
|
19
|
+
} | null, context?: unknown);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Erreur de RELAIS/infra : fonction injoignable (échec réseau), 502/503/504 de la
|
|
23
|
+
* passerelle, cold start échoué, corps non exploitable. Parité `FunctionsRelayError`
|
|
24
|
+
* de `@supabase/functions-js`. Sous-classe `AuraError`.
|
|
25
|
+
*/
|
|
26
|
+
declare class FunctionsRelayError extends AuraError {
|
|
27
|
+
readonly context: unknown;
|
|
28
|
+
constructor(status: number, body?: {
|
|
29
|
+
error?: {
|
|
30
|
+
code?: string;
|
|
31
|
+
message?: string;
|
|
32
|
+
};
|
|
33
|
+
message?: string;
|
|
34
|
+
} | null, context?: unknown);
|
|
35
|
+
}
|
|
36
|
+
|
|
4
37
|
/** Type de réponse attendu de l'invocation (parité `@supabase/functions-js`). */
|
|
5
38
|
type FunctionsResponseType = 'json' | 'text' | 'blob' | 'arrayBuffer';
|
|
6
39
|
declare class FunctionsService extends BaseService {
|
|
7
40
|
constructor(client: AuraClientLike);
|
|
41
|
+
/**
|
|
42
|
+
* Préfixe des routes de ce service. Aucun identifiant de projet : il est injecté par
|
|
43
|
+
* la passerelle depuis le sous-domaine du projet (`inject_project_id`,
|
|
44
|
+
* `services/aura-gateway/src/middleware/project_subdomain.rs`).
|
|
45
|
+
*/
|
|
8
46
|
private get prefix();
|
|
9
47
|
/**
|
|
10
48
|
* Invoque une edge function.
|
|
11
49
|
*
|
|
50
|
+
* Ce que la fonction REÇOIT (C20) : la requête vue par votre code est celle que vous
|
|
51
|
+
* décrivez ici — `method` (défaut `POST`), `path` (défaut `/`), `headers` et `body`
|
|
52
|
+
* APPLICATIF. Sur le runtime Deno :
|
|
53
|
+
*
|
|
54
|
+
* ```ts
|
|
55
|
+
* // invoke('x', { method: 'PUT', path: '/items/1', headers: { 'x-src': 'sdk' }, body: { a: 1 } })
|
|
56
|
+
* Deno.serve(async (req) => {
|
|
57
|
+
* req.method // 'PUT'
|
|
58
|
+
* new URL(req.url).pathname // '/items/1'
|
|
59
|
+
* req.headers.get('x-src') // 'sdk'
|
|
60
|
+
* await req.json() // { a: 1 } ← le corps, PAS l'enveloppe
|
|
61
|
+
* })
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
12
64
|
* `responseType` (parité `@supabase/functions-js`, défaut `'json'`) :
|
|
13
65
|
* - `'json'` : chemin PIPELINE inchangé (refresh proactif centralisé, injection
|
|
14
66
|
* apikey/Authorization, dé-wrapping `{data,meta}`, retryConfig). Non-régression.
|
|
@@ -19,8 +71,9 @@ declare class FunctionsService extends BaseService {
|
|
|
19
71
|
*
|
|
20
72
|
* Erreurs typées (parité) : une réponse HTTP non-2xx → `FunctionsHttpError`, une
|
|
21
73
|
* panne de relais/infra (réseau, 502/503/504) → `FunctionsRelayError` ; les deux
|
|
22
|
-
* sous-classent `AuraError
|
|
23
|
-
* `
|
|
74
|
+
* sous-classent `AuraError`, ce qui est l'invariant du typage d'erreur du SDK :
|
|
75
|
+
* `isAuraError` reste vrai et le champ `error` reste lisible sur les deux. `timeout`/
|
|
76
|
+
* `signal` produisent le code `timeout`.
|
|
24
77
|
*/
|
|
25
78
|
invoke<T = unknown>(slug: string, options?: {
|
|
26
79
|
body?: unknown;
|
|
@@ -32,7 +85,14 @@ declare class FunctionsService extends BaseService {
|
|
|
32
85
|
responseType?: FunctionsResponseType;
|
|
33
86
|
}): Promise<AuraResponse<T>>;
|
|
34
87
|
list(): Promise<AuraResponse<FunctionDef[]>>;
|
|
35
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Lit une fonction par son **nom** ou par son **identifiant** (B10).
|
|
90
|
+
*
|
|
91
|
+
* Les deux clés sont acceptées : `list()` renvoie `{ id, name, … }` et les deux sont des
|
|
92
|
+
* points d'entrée naturels. En cas d'homonymie (une fonction PEUT s'appeler comme un UUID),
|
|
93
|
+
* le NOM l'emporte. Une valeur qui n'est ni l'un ni l'autre produit un 404 qui le dit.
|
|
94
|
+
*/
|
|
95
|
+
get(nameOrId: string): Promise<AuraResponse<FunctionDef>>;
|
|
36
96
|
listJobs(options?: {
|
|
37
97
|
status?: string;
|
|
38
98
|
limit?: number;
|
|
@@ -45,37 +105,4 @@ declare class FunctionsService extends BaseService {
|
|
|
45
105
|
deleteCronJob(name: string): Promise<AuraResponse<null>>;
|
|
46
106
|
}
|
|
47
107
|
|
|
48
|
-
declare function isRelayStatus(status: number): boolean;
|
|
49
|
-
/**
|
|
50
|
-
* Erreur HTTP APPLICATIVE renvoyée par l'invocation d'une edge function
|
|
51
|
-
* (réponse HTTP non-2xx). Parité `FunctionsHttpError` de `@supabase/functions-js`.
|
|
52
|
-
* Sous-classe `AuraError` → `isAuraError()` reste vrai, `status` porte le code HTTP.
|
|
53
|
-
* `context` conserve le corps déjà lu (JSON/texte) pour inspection.
|
|
54
|
-
*/
|
|
55
|
-
declare class FunctionsHttpError extends AuraError {
|
|
56
|
-
readonly context: unknown;
|
|
57
|
-
constructor(status: number, body?: {
|
|
58
|
-
error?: {
|
|
59
|
-
code?: string;
|
|
60
|
-
message?: string;
|
|
61
|
-
};
|
|
62
|
-
message?: string;
|
|
63
|
-
} | null, context?: unknown);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Erreur de RELAIS/infra : fonction injoignable (échec réseau), 502/503/504 de la
|
|
67
|
-
* passerelle, cold start échoué, corps non exploitable. Parité `FunctionsRelayError`
|
|
68
|
-
* de `@supabase/functions-js`. Sous-classe `AuraError`.
|
|
69
|
-
*/
|
|
70
|
-
declare class FunctionsRelayError extends AuraError {
|
|
71
|
-
readonly context: unknown;
|
|
72
|
-
constructor(status: number, body?: {
|
|
73
|
-
error?: {
|
|
74
|
-
code?: string;
|
|
75
|
-
message?: string;
|
|
76
|
-
};
|
|
77
|
-
message?: string;
|
|
78
|
-
} | null, context?: unknown);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
108
|
export { FunctionsHttpError, FunctionsRelayError, type FunctionsResponseType, FunctionsService, isRelayStatus };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AuraError, BaseService, isTokenExpired, refreshOnce, isAbortError } from '@aurabase/core';
|
|
2
2
|
|
|
3
|
-
// src/
|
|
3
|
+
// src/errors.ts
|
|
4
4
|
var RELAY_STATUSES = /* @__PURE__ */ new Set([502, 503, 504]);
|
|
5
5
|
function isRelayStatus(status) {
|
|
6
6
|
return status === 0 || RELAY_STATUSES.has(status);
|
|
@@ -27,19 +27,36 @@ function toFunctionsError(err, context) {
|
|
|
27
27
|
const body = err.details != null ? err.details : { error: { code: err.code, message: err.message } };
|
|
28
28
|
return isRelayStatus(err.status) ? new FunctionsRelayError(err.status, body, err.details) : new FunctionsHttpError(err.status, body, err.details);
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
// src/FunctionsService.ts
|
|
32
30
|
var FunctionsService = class extends BaseService {
|
|
33
31
|
constructor(client) {
|
|
34
32
|
super(client);
|
|
35
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Préfixe des routes de ce service. Aucun identifiant de projet : il est injecté par
|
|
36
|
+
* la passerelle depuis le sous-domaine du projet (`inject_project_id`,
|
|
37
|
+
* `services/aura-gateway/src/middleware/project_subdomain.rs`).
|
|
38
|
+
*/
|
|
36
39
|
get prefix() {
|
|
37
|
-
return
|
|
40
|
+
return "/v1/functions";
|
|
38
41
|
}
|
|
39
42
|
// -- Function invocation ----------------------------------------------------
|
|
40
43
|
/**
|
|
41
44
|
* Invoque une edge function.
|
|
42
45
|
*
|
|
46
|
+
* Ce que la fonction REÇOIT (C20) : la requête vue par votre code est celle que vous
|
|
47
|
+
* décrivez ici — `method` (défaut `POST`), `path` (défaut `/`), `headers` et `body`
|
|
48
|
+
* APPLICATIF. Sur le runtime Deno :
|
|
49
|
+
*
|
|
50
|
+
* ```ts
|
|
51
|
+
* // invoke('x', { method: 'PUT', path: '/items/1', headers: { 'x-src': 'sdk' }, body: { a: 1 } })
|
|
52
|
+
* Deno.serve(async (req) => {
|
|
53
|
+
* req.method // 'PUT'
|
|
54
|
+
* new URL(req.url).pathname // '/items/1'
|
|
55
|
+
* req.headers.get('x-src') // 'sdk'
|
|
56
|
+
* await req.json() // { a: 1 } ← le corps, PAS l'enveloppe
|
|
57
|
+
* })
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
43
60
|
* `responseType` (parité `@supabase/functions-js`, défaut `'json'`) :
|
|
44
61
|
* - `'json'` : chemin PIPELINE inchangé (refresh proactif centralisé, injection
|
|
45
62
|
* apikey/Authorization, dé-wrapping `{data,meta}`, retryConfig). Non-régression.
|
|
@@ -50,8 +67,9 @@ var FunctionsService = class extends BaseService {
|
|
|
50
67
|
*
|
|
51
68
|
* Erreurs typées (parité) : une réponse HTTP non-2xx → `FunctionsHttpError`, une
|
|
52
69
|
* panne de relais/infra (réseau, 502/503/504) → `FunctionsRelayError` ; les deux
|
|
53
|
-
* sous-classent `AuraError
|
|
54
|
-
* `
|
|
70
|
+
* sous-classent `AuraError`, ce qui est l'invariant du typage d'erreur du SDK :
|
|
71
|
+
* `isAuraError` reste vrai et le champ `error` reste lisible sur les deux. `timeout`/
|
|
72
|
+
* `signal` produisent le code `timeout`.
|
|
55
73
|
*/
|
|
56
74
|
async invoke(slug, options) {
|
|
57
75
|
const controller = new AbortController();
|
|
@@ -67,7 +85,12 @@ var FunctionsService = class extends BaseService {
|
|
|
67
85
|
if (timeoutId) clearTimeout(timeoutId);
|
|
68
86
|
if (userSignal) userSignal.removeEventListener("abort", onAbort);
|
|
69
87
|
};
|
|
70
|
-
const envelope = {
|
|
88
|
+
const envelope = {
|
|
89
|
+
method: options?.method,
|
|
90
|
+
path: options?.path,
|
|
91
|
+
headers: options?.headers,
|
|
92
|
+
body: options?.body
|
|
93
|
+
};
|
|
71
94
|
const responseType = options?.responseType ?? "json";
|
|
72
95
|
const timeoutError = () => ({
|
|
73
96
|
data: null,
|
|
@@ -124,7 +147,10 @@ var FunctionsService = class extends BaseService {
|
|
|
124
147
|
return {
|
|
125
148
|
data: null,
|
|
126
149
|
error: new FunctionsRelayError(0, {
|
|
127
|
-
error: {
|
|
150
|
+
error: {
|
|
151
|
+
code: "network_error",
|
|
152
|
+
message: err instanceof Error ? err.message : "Function relay error"
|
|
153
|
+
}
|
|
128
154
|
})
|
|
129
155
|
};
|
|
130
156
|
} finally {
|
|
@@ -135,8 +161,15 @@ var FunctionsService = class extends BaseService {
|
|
|
135
161
|
async list() {
|
|
136
162
|
return this.request("GET", `${this.prefix}`);
|
|
137
163
|
}
|
|
138
|
-
|
|
139
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Lit une fonction par son **nom** ou par son **identifiant** (B10).
|
|
166
|
+
*
|
|
167
|
+
* Les deux clés sont acceptées : `list()` renvoie `{ id, name, … }` et les deux sont des
|
|
168
|
+
* points d'entrée naturels. En cas d'homonymie (une fonction PEUT s'appeler comme un UUID),
|
|
169
|
+
* le NOM l'emporte. Une valeur qui n'est ni l'un ni l'autre produit un 404 qui le dit.
|
|
170
|
+
*/
|
|
171
|
+
async get(nameOrId) {
|
|
172
|
+
return this.request("GET", `${this.prefix}/${nameOrId}`);
|
|
140
173
|
}
|
|
141
174
|
// -- Jobs -------------------------------------------------------------------
|
|
142
175
|
async listJobs(options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurabase/functions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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.
|
|
48
|
+
"@aurabase/core": "0.5.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "20.14.15",
|