@aurabase/functions 0.4.1 → 0.5.1

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
@@ -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('http://localhost:8080', 'my-project', 'aura_anon_xxx')
26
+ const aura = createClient('https://mon-projet.aurabase.cloud', 'aura_anon_xxx')
27
27
  ```
28
28
 
29
29
  ### Invocation
30
30
 
31
- `invoke<T>()` enveloppe la requête (`{ method, path, headers, body }`) et dé-wrap la réponse JSON du backend — tout est bufferisé, pas de streaming (voir Limitations).
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: fn } = await aura.functions.get(fns![0]!.id)
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 JSON avant de résoudre (pas de lecture incrémentale, pas de réponse SSE/chunked exploitable).
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 tel quel à 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`, `Content-Type`).
95
- - Pas de `getCronJob()` ni `updateCronJob()` : seuls `listCronJobs`, `createCronJob` et `deleteCronJob` sont exposés.
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/FunctionsService.ts
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 `/v1/functions/${this.client.projectId}`;
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` (rétrocompat `isAuraError`/champ `error`). `timeout`/
56
- * `signal` continuent de produire le code `timeout`.
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 = { method: options?.method, path: options?.path, headers: options?.headers, body: options?.body };
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: { code: "network_error", message: err instanceof Error ? err.message : "Function relay 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
- async get(id) {
141
- return this.request("GET", `${this.prefix}/${id}`);
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, AuraError } from '@aurabase/core';
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` (rétrocompat `isAuraError`/champ `error`). `timeout`/
23
- * `signal` continuent de produire le code `timeout`.
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
- get(id: string): Promise<AuraResponse<FunctionDef>>;
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, AuraError } from '@aurabase/core';
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` (rétrocompat `isAuraError`/champ `error`). `timeout`/
23
- * `signal` continuent de produire le code `timeout`.
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
- get(id: string): Promise<AuraResponse<FunctionDef>>;
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/FunctionsService.ts
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 `/v1/functions/${this.client.projectId}`;
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` (rétrocompat `isAuraError`/champ `error`). `timeout`/
54
- * `signal` continuent de produire le code `timeout`.
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 = { method: options?.method, path: options?.path, headers: options?.headers, body: options?.body };
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: { code: "network_error", message: err instanceof Error ? err.message : "Function relay 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
- async get(id) {
139
- return this.request("GET", `${this.prefix}/${id}`);
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.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Service edge functions du SDK Aurabase (invoke, jobs asynchrones, cron)",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -34,7 +34,7 @@
34
34
  "url": "git+https://github.com/daylami555/aurabase.git",
35
35
  "directory": "aurabase-js/packages/functions"
36
36
  },
37
- "homepage": "https://github.com/daylami555/aurabase/tree/main/aurabase-js/packages/functions#readme",
37
+ "homepage": "https://aurabase.cloud",
38
38
  "bugs": {
39
39
  "url": "https://github.com/daylami555/aurabase/issues"
40
40
  },
@@ -44,19 +44,20 @@
44
44
  "sdk",
45
45
  "functions"
46
46
  ],
47
- "dependencies": {
48
- "@aurabase/core": "0.4.1"
49
- },
50
- "devDependencies": {
51
- "@types/node": "20.14.15",
52
- "tsup": "^8.0.0",
53
- "typescript": "^5.5.0",
54
- "vitest": "^4.1.6"
55
- },
56
47
  "scripts": {
57
48
  "build": "tsup",
58
49
  "test": "vitest run --passWithNoTests",
59
50
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
51
+ "prepack": "pnpm build",
60
52
  "typecheck:tests": "tsc --noEmit -p tsconfig.test.json"
53
+ },
54
+ "dependencies": {
55
+ "@aurabase/core": "workspace:*"
56
+ },
57
+ "devDependencies": {
58
+ "@types/node": "catalog:",
59
+ "tsup": "catalog:",
60
+ "typescript": "catalog:",
61
+ "vitest": "catalog:"
61
62
  }
62
- }
63
+ }