@staticbot/base44-supabase-shim 0.3.0 → 0.4.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 CHANGED
@@ -26,9 +26,10 @@ In each consuming app, vendor the prebuilt tarball or add as a git submodule
26
26
  git submodule add https://github.com/staticbot/staticbot-base44-supabase-shim.git vendor/base44-supabase-shim
27
27
  cd vendor/base44-supabase-shim && npm i && npm run build
28
28
 
29
- # Option B: vendor the prebuilt tarball
30
- curl -L https://github.com/staticbot/staticbot-base44-supabase-shim/releases/latest/download/base44-supabase-shim.tgz \
31
- | tar -xz -C vendor/base44-supabase-shim
29
+ # Option B: vendor the prebuilt tarball straight from the npm registry
30
+ # (same URL shape staticbot-app's Base44ShimProperties.getDistTarballUrl builds)
31
+ curl -L https://registry.npmjs.org/@staticbot/base44-supabase-shim/-/base44-supabase-shim-0.4.0.tgz \
32
+ | tar -xz -C vendor/base44-supabase-shim --strip-components=1
32
33
  ```
33
34
 
34
35
  In `package.json`:
@@ -127,13 +128,48 @@ Operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`, `in`.
127
128
  - String form (Base44 convention): `'name'` ascending, `'-created_date'` descending.
128
129
  - Object form: `{ field: 'name', ascending: false }`.
129
130
 
131
+ ## Auth surface
132
+
133
+ Base44 alias names are supported alongside Supabase-idiomatic ones so migrated
134
+ apps compile unchanged:
135
+
136
+ - `auth.signIn({email, password})` / `auth.loginViaEmailPassword(email, password)`
137
+ - `auth.signUp({email, password, metadata?})` / `auth.register({email, password, metadata?})`
138
+ - `auth.loginWithProvider(provider, returnPath?)` — Google / Microsoft / Facebook / Apple; builds an absolute `redirectTo` from `window.location.origin`.
139
+ - `auth.verifyOtp({email, otpCode})` — email OTP after `register()`. Returns `{access_token, session, user}`; the Supabase session is installed as a side-effect so a follow-up `setToken()` is a no-op.
140
+ - `auth.resendOtp(email)`
141
+ - `auth.setToken(accessToken)` — kept as a warn-once no-op so migrated code doesn't `TypeError`; the session is already installed by `verifyOtp` / `signInWithPassword`.
142
+ - `auth.resetPasswordRequest(email, {redirectTo?})` — Supabase `resetPasswordForEmail`.
143
+ - `auth.resetPassword({resetToken, newPassword})` — `resetToken` is ignored (Supabase already exchanged the link fragment for a session before you got here).
144
+ - `auth.isAuthenticated(): Promise<boolean>`
145
+ - `auth.logout(returnUrl?)` — after signOut resolves, navigates to `returnUrl` if provided.
146
+ - `auth.me()` / `auth.updateMe(metadata)` / `auth.getUser()` / `auth.getSession()` / `auth.redirectToLogin(returnUrl?)` / `auth.onAuthStateChange(cb)`
147
+
148
+ ## Agents
149
+
150
+ Base44's hosted agents (WhatsApp / Telegram) have no self-host equivalent. The
151
+ `agents` namespace exposes a stub so `base44.agents.getWhatsAppConnectURL(name)`
152
+ doesn't crash — it returns `null` by default, which lets `<a href={...}>` render
153
+ as an inert link (feature visibly disabled). Wire your own bridge with:
154
+
155
+ ```js
156
+ createClient({
157
+ ...,
158
+ agents: {
159
+ whatsappUrls: { SubdomainReviewer: 'https://wa.example.com/connect' },
160
+ },
161
+ });
162
+ ```
163
+
130
164
  ## What's NOT covered
131
165
 
132
166
  - **Stripe / payments** — Base44 had managed integration; here, wire Stripe into
133
167
  edge functions yourself.
134
168
  - **Email** — air-gapped LAN can't send mail by default; supply an internal
135
- SMTP relay or capture via inbucket.
136
- - **AI / `InvokeLLM`** — out of scope (no Ollama in current target).
169
+ SMTP relay or capture via inbucket. Enable via `integrations.sendEmailFunction`.
170
+ - **AI / `InvokeLLM` / `GenerateImage`** — opt-in only. Wire your own endpoint
171
+ through an edge function and pass its name via `integrations.invokeLlmFunction`
172
+ / `integrations.generateImageFunction`.
137
173
  - **Schema discovery** — entity definitions must exist in Postgres first
138
174
  (separate migrations). The shim assumes tables exist with conventional names.
139
175
 
@@ -144,3 +180,83 @@ npm i
144
180
  npm test # vitest, mocked SupabaseClient
145
181
  npm run build # tsup → dist/
146
182
  ```
183
+
184
+ ## Releasing
185
+
186
+ Manual recipe. The shim ships to the public npm registry — that's where the
187
+ tarball is fetched at Base44 switchover time (staticbot-app's
188
+ `Base44ShimProperties.cdnBaseUrl` defaults to
189
+ `https://registry.npmjs.org/@staticbot/base44-supabase-shim/-`, so
190
+ `getDistTarballUrl()` produces the standard unpkg-style URL). npm's CDN is
191
+ public + immutable per version — no bucket, no ACL, no S3 CLI.
192
+
193
+ ### 1. Prep
194
+
195
+ ```sh
196
+ # From a clean working tree:
197
+ npm run typecheck && npm test && npm run build
198
+ ```
199
+
200
+ Bump `version` in `package.json` to the new semver. Additive changes → minor;
201
+ bugfix-only → patch. Breaking changes need a coordinated rollout (see step 4).
202
+
203
+ ### 2. Publish to npm
204
+
205
+ ```sh
206
+ npm publish --access public
207
+ # `--access public` is required for scoped packages (@staticbot/*).
208
+ # `npm whoami` first if you're not sure you're logged in as the maintainer.
209
+ ```
210
+
211
+ Verify the tarball resolves at the URL the worker will hit:
212
+
213
+ ```sh
214
+ curl -I https://registry.npmjs.org/@staticbot/base44-supabase-shim/-/base44-supabase-shim-<version>.tgz
215
+ # expect: HTTP/2 200, content-type: application/octet-stream
216
+ ```
217
+
218
+ **Never overwrite / re-publish a version.** npm forbids it by default (you'd
219
+ have to `npm unpublish` first, which npm strongly discourages and blocks after
220
+ 72h). Even if you could, the worker caches by URL hash
221
+ (`SHIM_CACHE_ROOT/<sha256(url)[:16]>/`) — an in-place replacement is invisible
222
+ to any worker that already cached the version.
223
+
224
+ ### 3. Tag + push
225
+
226
+ ```sh
227
+ git add -A
228
+ git commit -m "v<version>: <short changelog>"
229
+ git tag v<version>
230
+ git push origin main --tags
231
+ ```
232
+
233
+ ### 4. Roll out the version pin in staticbot-app
234
+
235
+ Two places reference the pinned version — bump both, otherwise integration tests
236
+ will fail against the newly-published tarball URL:
237
+
238
+ - `staticbot-app`'s `Base44ShimProperties.version` default (and matching docs).
239
+ - `staticbot-control-center`'s `test_phase7_base44_switchover.py` fixtures — the
240
+ module-level `_make_shim_tarball(version=...)` default, the `shim_version` /
241
+ `shim_dist_url` keys returned by the `_input_data(...)` helper, and the
242
+ `package.json` version fixture in the shim-tarball setup used by the
243
+ `TestCommitFilesToBranch`-style tests.
244
+
245
+ Then either **redeploy staticbot-app** to bake in the new default, or **hot-flip
246
+ in prod** by setting `BASE44_SHIM_VERSION=<new>` in the Spring env — the value
247
+ in `application.yml` under `application.base44-shim.version` reads
248
+ `${BASE44_SHIM_VERSION:<default>}`, so the env override takes effect immediately
249
+ for any new BASE44_SWITCHOVER job with no restart.
250
+
251
+ If you ever need to mirror the tarball off npm (self-host CDN, air-gapped
252
+ customer, etc.), the second knob is `BASE44_SHIM_CDN_BASE_URL` — point it at any
253
+ directory that serves `base44-supabase-shim-<version>.tgz`. Same URL shape npm's
254
+ `/-/` path uses; no code change required.
255
+
256
+ ### 5. Rollback
257
+
258
+ If a customer migration reports a bad shim release, flip `BASE44_SHIM_VERSION`
259
+ back to the last-known-good version in prod env — no code deploy needed.
260
+ Already-running switchover jobs continue with whatever tarball they cached; only
261
+ new jobs pick up the flip. That's why step 2's "never re-publish" rule is
262
+ load-bearing.
@@ -38,6 +38,18 @@ function makeIntegrations(client, opts) {
38
38
  });
39
39
  if (error) throw error;
40
40
  return data;
41
+ },
42
+ async GenerateImage(payload) {
43
+ if (!opts.generateImageFunction) {
44
+ throw new Error(
45
+ "GenerateImage not configured. Image-gen features are disabled in this self-host build. Set integrations.generateImageFunction in createClient() and deploy an edge function that proxies to your image endpoint (SDXL / DALL\xB7E gateway)."
46
+ );
47
+ }
48
+ const { data, error } = await client.functions.invoke(opts.generateImageFunction, {
49
+ body: payload
50
+ });
51
+ if (error) throw error;
52
+ return data;
41
53
  }
42
54
  };
43
55
  return { Core };
@@ -46,4 +58,4 @@ function makeIntegrations(client, opts) {
46
58
  export {
47
59
  makeIntegrations
48
60
  };
49
- //# sourceMappingURL=chunk-PUROQAXO.js.map
61
+ //# sourceMappingURL=chunk-E2KG6ZL4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/integrations.ts"],"sourcesContent":["import type { SupabaseClient } from '@supabase/supabase-js';\n\nexport interface IntegrationsOptions {\n /** Default storage bucket for UploadFile. Falls back to schemaPrefix from createClient. */\n defaultBucket: string;\n /**\n * Edge function name to invoke for SendEmail. If unset, calls fail loudly.\n * Implement this function in supabase/volumes/functions/send-email/.\n */\n sendEmailFunction?: string;\n /**\n * Edge function name to invoke for InvokeLLM. If unset, calls fail loudly\n * (AI features are out of scope for the air-gapped self-host stack).\n */\n invokeLlmFunction?: string;\n /**\n * Edge function name to invoke for GenerateImage. If unset, calls fail loudly.\n * Base44's default backing was DALL·E — for self-host, proxy your own image\n * endpoint through an edge function.\n */\n generateImageFunction?: string;\n}\n\n/**\n * Stub of `base44.integrations.Core.*`. Four methods are implemented:\n *\n * UploadFile → delegates to Supabase Storage upload + returns {url, path}.\n * SendEmail → invokes a configured edge function (or throws if none).\n * InvokeLLM → invokes a configured edge function (or throws if none).\n * GenerateImage → invokes a configured edge function (or throws if none).\n *\n * Air-gapped LAN cannot call OpenAI directly; wire your own LLM/image endpoint\n * (Ollama, internal API gateway, etc.) inside an edge function and pass the\n * function name via createClient({ integrations: { invokeLlmFunction: 'llm-relay',\n * generateImageFunction: 'image-relay' } }).\n */\nexport function makeIntegrations(client: SupabaseClient, opts: IntegrationsOptions) {\n const Core = {\n async UploadFile({\n file,\n bucket,\n path,\n contentType,\n }: {\n file: Blob | File | ArrayBuffer | Uint8Array;\n bucket?: string;\n path?: string;\n contentType?: string;\n }): Promise<{ url: string; path: string }> {\n const b = bucket ?? opts.defaultBucket;\n const p =\n path ??\n `uploads/${Date.now()}-${Math.random().toString(36).slice(2, 8)}-${(file as File)?.name ?? 'file'}`;\n const { error } = await client.storage.from(b).upload(p, file, {\n contentType: contentType ?? (file as File)?.type,\n upsert: false,\n });\n if (error) throw error;\n const { data } = client.storage.from(b).getPublicUrl(p);\n return { url: data.publicUrl, path: p };\n },\n\n async SendEmail(payload: {\n to: string | string[];\n subject: string;\n body?: string;\n html?: string;\n from?: string;\n }): Promise<{ ok: boolean }> {\n if (!opts.sendEmailFunction) {\n throw new Error(\n 'SendEmail not configured. Set integrations.sendEmailFunction in createClient() ' +\n 'and deploy a corresponding Supabase Edge Function (e.g. send-email).',\n );\n }\n const { error } = await client.functions.invoke(opts.sendEmailFunction, { body: payload });\n if (error) throw error;\n return { ok: true };\n },\n\n async InvokeLLM(payload: {\n prompt: string;\n model?: string;\n [key: string]: unknown;\n }): Promise<unknown> {\n if (!opts.invokeLlmFunction) {\n throw new Error(\n 'InvokeLLM not configured. AI features are disabled in this self-host build. ' +\n 'Set integrations.invokeLlmFunction in createClient() and deploy an edge function ' +\n 'that proxies to your LLM endpoint (Ollama / internal gateway).',\n );\n }\n const { data, error } = await client.functions.invoke(opts.invokeLlmFunction, {\n body: payload,\n });\n if (error) throw error;\n return data;\n },\n\n async GenerateImage(payload: {\n prompt: string;\n [key: string]: unknown;\n }): Promise<unknown> {\n if (!opts.generateImageFunction) {\n throw new Error(\n 'GenerateImage not configured. Image-gen features are disabled in this self-host ' +\n 'build. Set integrations.generateImageFunction in createClient() and deploy an ' +\n 'edge function that proxies to your image endpoint (SDXL / DALL·E gateway).',\n );\n }\n const { data, error } = await client.functions.invoke(opts.generateImageFunction, {\n body: payload,\n });\n if (error) throw error;\n return data;\n },\n };\n\n return { Core };\n}\n"],"mappings":";AAoCO,SAAS,iBAAiB,QAAwB,MAA2B;AAClF,QAAM,OAAO;AAAA,IACX,MAAM,WAAW;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,GAK2C;AACzC,YAAM,IAAI,UAAU,KAAK;AACzB,YAAM,IACJ,QACA,WAAW,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,IAAK,MAAe,QAAQ,MAAM;AACnG,YAAM,EAAE,MAAM,IAAI,MAAM,OAAO,QAAQ,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM;AAAA,QAC7D,aAAa,eAAgB,MAAe;AAAA,QAC5C,QAAQ;AAAA,MACV,CAAC;AACD,UAAI,MAAO,OAAM;AACjB,YAAM,EAAE,KAAK,IAAI,OAAO,QAAQ,KAAK,CAAC,EAAE,aAAa,CAAC;AACtD,aAAO,EAAE,KAAK,KAAK,WAAW,MAAM,EAAE;AAAA,IACxC;AAAA,IAEA,MAAM,UAAU,SAMa;AAC3B,UAAI,CAAC,KAAK,mBAAmB;AAC3B,cAAM,IAAI;AAAA,UACR;AAAA,QAEF;AAAA,MACF;AACA,YAAM,EAAE,MAAM,IAAI,MAAM,OAAO,UAAU,OAAO,KAAK,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AACzF,UAAI,MAAO,OAAM;AACjB,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,IAEA,MAAM,UAAU,SAIK;AACnB,UAAI,CAAC,KAAK,mBAAmB;AAC3B,cAAM,IAAI;AAAA,UACR;AAAA,QAGF;AAAA,MACF;AACA,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,UAAU,OAAO,KAAK,mBAAmB;AAAA,QAC5E,MAAM;AAAA,MACR,CAAC;AACD,UAAI,MAAO,OAAM;AACjB,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,cAAc,SAGC;AACnB,UAAI,CAAC,KAAK,uBAAuB;AAC/B,cAAM,IAAI;AAAA,UACR;AAAA,QAGF;AAAA,MACF;AACA,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,UAAU,OAAO,KAAK,uBAAuB;AAAA,QAChF,MAAM;AAAA,MACR,CAAC;AACD,UAAI,MAAO,OAAM;AACjB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,EAAE,KAAK;AAChB;","names":[]}
@@ -258,10 +258,35 @@ function makeEntitiesProxy(client, opts) {
258
258
  });
259
259
  }
260
260
 
261
+ // src/functions.ts
262
+ function makeFunctions(client) {
263
+ return {
264
+ /** Invoke an edge function by name, passing JSON body. Returns parsed JSON. */
265
+ async invoke(name, payload) {
266
+ const { data, error } = await client.functions.invoke(name, {
267
+ body: payload
268
+ });
269
+ if (error) throw error;
270
+ return data;
271
+ },
272
+ /** Lower-level: invoke and return Response so caller can handle non-JSON. */
273
+ async fetch(name, init) {
274
+ const url = `${client.supabaseUrl}/functions/v1/${name}`;
275
+ const headers = new Headers(init?.headers);
276
+ const session = await client.auth.getSession();
277
+ const token = session.data.session?.access_token;
278
+ if (token) headers.set("Authorization", `Bearer ${token}`);
279
+ headers.set("apikey", client.supabaseKey);
280
+ return fetch(url, { ...init, headers });
281
+ }
282
+ };
283
+ }
284
+
261
285
  export {
262
286
  defaultEntityToTable,
263
287
  resolveEntityMapping,
264
288
  parseOrderBy,
265
- makeEntitiesProxy
289
+ makeEntitiesProxy,
290
+ makeFunctions
266
291
  };
267
- //# sourceMappingURL=chunk-SWURHLDN.js.map
292
+ //# sourceMappingURL=chunk-YQ3BDCIS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/entities.ts","../src/functions.ts"],"sourcesContent":["import type { SupabaseClient } from '@supabase/supabase-js';\nimport type {\n ClientOptions,\n EntitiesProxy,\n EntityApi,\n EntityMapping,\n FilterObject,\n FilterValue,\n OrderBy,\n} from './types.js';\n\nconst DEFAULT_SHARED_ENTITIES = [\n // Auth/identity (option C: shared)\n 'User',\n 'Role',\n 'UserRole', // 'finance.user_roles' was 404 — UserRole belongs in shared\n 'Department',\n // Org (option C: shared)\n 'Company',\n // Cross-app entities exposed by core schema\n 'AuditLog',\n // Note: Customer is intentionally NOT shared — each app's Customer schema\n // differs significantly (CRM vs Facility vs Construction).\n];\n\n/** PascalCase → snake_case + naive pluralization (s, ies, es). */\nexport function defaultEntityToTable(entityName: string): string {\n const snake = entityName.replace(/([A-Z])/g, (m, c, i) =>\n i === 0 ? c.toLowerCase() : '_' + c.toLowerCase(),\n );\n if (snake.endsWith('y') && !/[aeiou]y$/.test(snake)) return snake.slice(0, -1) + 'ies';\n if (/(s|x|z|ch|sh)$/.test(snake)) return snake + 'es';\n return snake + 's';\n}\n\nexport function resolveEntityMapping(\n entityName: string,\n opts: Required<Pick<ClientOptions, 'schemaPrefix'>> &\n Pick<ClientOptions, 'sharedSchema' | 'sharedEntities' | 'entityMap'>,\n): EntityMapping {\n if (opts.entityMap?.[entityName]) return opts.entityMap[entityName];\n const sharedEntities = opts.sharedEntities ?? DEFAULT_SHARED_ENTITIES;\n const sharedSchema = opts.sharedSchema ?? 'core';\n const schema = sharedEntities.includes(entityName) ? sharedSchema : opts.schemaPrefix;\n return { schema, table: defaultEntityToTable(entityName) };\n}\n\n/**\n * Parse Base44-style orderBy:\n * - string '-created_date' → { field: 'created_date', ascending: false }\n * - string 'name' → { field: 'name', ascending: true }\n * - object passes through.\n */\nexport function parseOrderBy(input: string | OrderBy | undefined): OrderBy | undefined {\n if (!input) return undefined;\n if (typeof input === 'object') return input;\n if (input.startsWith('-')) return { field: input.slice(1), ascending: false };\n return { field: input, ascending: true };\n}\n\nconst MONGO_OPS = [\n '$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in', '$nin', '$like', '$ilike',\n] as const;\n\nfunction isMongoFilter(v: unknown): v is Record<string, unknown> {\n if (!v || typeof v !== 'object' || Array.isArray(v)) return false;\n return Object.keys(v as object).some((k) =>\n (MONGO_OPS as readonly string[]).includes(k),\n );\n}\n\nfunction applyMongoOps(query: any, field: string, ops: Record<string, unknown>): any {\n for (const [op, value] of Object.entries(ops)) {\n switch (op) {\n case '$eq': query = query.eq(field, value); break;\n case '$ne': query = query.neq(field, value); break;\n case '$gt': query = query.gt(field, value); break;\n case '$gte': query = query.gte(field, value); break;\n case '$lt': query = query.lt(field, value); break;\n case '$lte': query = query.lte(field, value); break;\n case '$in': query = query.in(field, value as unknown[]); break;\n case '$nin': query = query.not('in', `(${(value as unknown[]).map(v => JSON.stringify(v)).join(',')})`); break;\n case '$like': query = query.like(field, value as string); break;\n case '$ilike': query = query.ilike(field, value as string); break;\n // Unknown ops are ignored rather than throwing — keeps shim forward-\n // compatible if Base44 ships new operators we haven't seen yet.\n }\n }\n return query;\n}\n\nfunction applyFilter(query: any, where: FilterObject): any {\n for (const [field, raw] of Object.entries(where)) {\n const v = raw as FilterValue;\n if (v === null) {\n query = query.is(field, null);\n } else if (isMongoFilter(v)) {\n query = applyMongoOps(query, field, v as Record<string, unknown>);\n } else if (typeof v === 'object' && !Array.isArray(v) && 'op' in v) {\n // legacy {op, value} form — translate to the Mongo path so behaviour stays in sync\n const { op, value } = v as { op: string; value: unknown };\n query = applyMongoOps(query, field, { [`$${op}`]: value });\n } else if (Array.isArray(v)) {\n query = query.in(field, v);\n } else {\n query = query.eq(field, v);\n }\n }\n return query;\n}\n\nfunction applyRange(query: any, limit: number | undefined, skip: number | undefined): any {\n if (typeof skip === 'number' && typeof limit === 'number') {\n return query.range(skip, skip + limit - 1);\n }\n if (typeof limit === 'number') return query.limit(limit);\n return query;\n}\n\nexport function makeEntityApi(client: SupabaseClient, mapping: EntityMapping): EntityApi {\n const from = () => client.schema(mapping.schema as never).from(mapping.table);\n\n // Treat \"table not in schema cache\" (PGRST205) and 404 as empty result, not error.\n // Apps reference many entities that may not exist in self-host schema yet —\n // surfacing as empty lets the UI render skeletons instead of crashing.\n const isMissingTableError = (e: unknown): boolean => {\n if (!e || typeof e !== 'object') return false;\n const code = (e as { code?: string }).code;\n const status = (e as { status?: number }).status;\n return code === 'PGRST205' || code === '42P01' || status === 404;\n };\n const warnedTables = new Set<string>();\n const warnMissing = () => {\n const key = `${mapping.schema}.${mapping.table}`;\n if (warnedTables.has(key)) return;\n warnedTables.add(key);\n if (typeof console !== 'undefined') console.warn(`[base44-shim] table ${key} not found — returning empty result`);\n };\n\n return {\n async list(orderBy, limit, skip) {\n let q = from().select('*');\n const ob = parseOrderBy(orderBy);\n if (ob) q = q.order(ob.field, { ascending: ob.ascending ?? true });\n q = applyRange(q, limit, skip);\n const { data, error } = await q;\n if (error) {\n if (isMissingTableError(error)) { warnMissing(); return []; }\n throw error;\n }\n return data ?? [];\n },\n async filter(where, orderBy, limit, skip) {\n let q = from().select('*');\n q = applyFilter(q, where);\n const ob = parseOrderBy(orderBy);\n if (ob) q = q.order(ob.field, { ascending: ob.ascending ?? true });\n q = applyRange(q, limit, skip);\n const { data, error } = await q;\n if (error) {\n if (isMissingTableError(error)) { warnMissing(); return []; }\n throw error;\n }\n return data ?? [];\n },\n async get(id) {\n const { data, error } = await from().select('*').eq('id', id).maybeSingle();\n if (error) {\n if (isMissingTableError(error)) { warnMissing(); return null; }\n throw error;\n }\n return data ?? null;\n },\n async read(id) {\n const { data, error } = await from().select('*').eq('id', id).maybeSingle();\n if (error) {\n if (isMissingTableError(error)) { warnMissing(); return null; }\n throw error;\n }\n return data ?? null;\n },\n async create(body) {\n const { data, error } = await from().insert(body).select().single();\n if (error) throw error;\n return data;\n },\n async bulkCreate(rows) {\n if (!rows.length) return [];\n const { data, error } = await from().insert(rows).select();\n if (error) throw error;\n return data ?? [];\n },\n async update(id, body) {\n const { data, error } = await from().update(body).eq('id', id).select().single();\n if (error) throw error;\n return data;\n },\n async bulkUpdate(rows) {\n if (!rows.length) return [];\n // PostgREST has no native multi-patch with per-row payloads; do one\n // round-trip per row. Fine for hundreds — re-evaluate if a caller\n // starts pushing thousands and we need a CTE-based RPC.\n const results = await Promise.all(\n rows.map(({ id, ...patch }) =>\n from().update(patch).eq('id', id).select().single(),\n ),\n );\n const errors = results.map((r) => r.error).filter(Boolean);\n if (errors.length) throw errors[0];\n return results.map((r) => r.data) as any[];\n },\n async updateMany(where, update) {\n // Base44 SDK / Mongo style: `{ $set: { field: value } }` wraps the patch.\n // Unwrap so the same call works whether the caller used $set or a flat object.\n const patch = (update as { $set?: unknown })?.$set ?? update;\n let q = from().update(patch as Record<string, unknown>);\n q = applyFilter(q, where);\n const { data, error } = await q.select();\n if (error) throw error;\n return data ?? [];\n },\n async delete(id) {\n const { error } = await from().delete().eq('id', id);\n if (error) throw error;\n },\n async deleteMany(where) {\n let q = from().delete();\n q = applyFilter(q, where);\n const { error } = await q;\n if (error) throw error;\n },\n subscribe(callback) {\n const channel = client\n .channel(`${mapping.schema}.${mapping.table}.${Math.random().toString(36).slice(2, 8)}`)\n .on(\n 'postgres_changes' as never,\n { event: '*', schema: mapping.schema, table: mapping.table },\n (payload: { eventType: string; new?: unknown; old?: unknown }) => {\n const type = payload.eventType.toLowerCase() as 'insert' | 'update' | 'delete';\n callback({ type, new: payload.new as never, old: payload.old as never });\n },\n )\n .subscribe();\n return () => {\n void client.removeChannel(channel);\n };\n },\n };\n}\n\nexport function makeEntitiesProxy(\n client: SupabaseClient,\n opts: Required<Pick<ClientOptions, 'schemaPrefix'>> &\n Pick<ClientOptions, 'sharedSchema' | 'sharedEntities' | 'entityMap'>,\n): EntitiesProxy {\n const cache: Record<string, EntityApi> = {};\n return new Proxy({} as EntitiesProxy, {\n get(_target, prop: string) {\n if (typeof prop !== 'string') return undefined;\n if (!cache[prop]) {\n const mapping = resolveEntityMapping(prop, opts);\n cache[prop] = makeEntityApi(client, mapping);\n }\n return cache[prop];\n },\n });\n}\n","import type { SupabaseClient } from '@supabase/supabase-js';\n\n/** Mirror of base44.functions — Supabase Edge Functions invoke wrapper.\n *\n * Returns the raw body. On HTTP error, throws an Error with the response body attached.\n */\nexport function makeFunctions(client: SupabaseClient) {\n return {\n /** Invoke an edge function by name, passing JSON body. Returns parsed JSON. */\n async invoke<T = unknown>(name: string, payload?: Record<string, unknown>): Promise<T> {\n const { data, error } = await client.functions.invoke<T>(name, {\n body: payload,\n });\n if (error) throw error;\n return data as T;\n },\n /** Lower-level: invoke and return Response so caller can handle non-JSON. */\n async fetch(name: string, init?: RequestInit): Promise<Response> {\n const url = `${(client as unknown as { supabaseUrl: string }).supabaseUrl}/functions/v1/${name}`;\n const headers = new Headers(init?.headers);\n const session = await client.auth.getSession();\n const token = session.data.session?.access_token;\n if (token) headers.set('Authorization', `Bearer ${token}`);\n headers.set('apikey', (client as unknown as { supabaseKey: string }).supabaseKey);\n return fetch(url, { ...init, headers });\n },\n };\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -28,7 +28,23 @@ __export(index_exports, {
28
28
  module.exports = __toCommonJS(index_exports);
29
29
  var import_supabase_js = require("@supabase/supabase-js");
30
30
 
31
+ // src/agents.ts
32
+ function makeAgents(opts = {}) {
33
+ return {
34
+ /**
35
+ * Returns a WhatsApp connect URL for the given agent, or null when not
36
+ * configured. Meant to be dropped straight into `<a href={...}>` — React
37
+ * omits the attribute when null so the anchor becomes inert instead of
38
+ * navigating to "null".
39
+ */
40
+ getWhatsAppConnectURL(agentName) {
41
+ return opts.whatsappUrls?.[agentName] ?? null;
42
+ }
43
+ };
44
+ }
45
+
31
46
  // src/auth.ts
47
+ var setTokenWarned = false;
32
48
  function makeAuth(client, opts = {}) {
33
49
  const loginPath = opts.loginPath ?? "/login";
34
50
  return {
@@ -37,6 +53,12 @@ function makeAuth(client, opts = {}) {
37
53
  if (error) throw error;
38
54
  return data;
39
55
  },
56
+ /** Base44 alias for signIn. */
57
+ async loginViaEmailPassword(email, password) {
58
+ const { data, error } = await client.auth.signInWithPassword({ email, password });
59
+ if (error) throw error;
60
+ return data;
61
+ },
40
62
  async signUp({ email, password, metadata }) {
41
63
  const { data, error } = await client.auth.signUp({
42
64
  email,
@@ -46,14 +68,99 @@ function makeAuth(client, opts = {}) {
46
68
  if (error) throw error;
47
69
  return data;
48
70
  },
71
+ /** Base44 alias for signUp. */
72
+ async register({ email, password, metadata }) {
73
+ const { data, error } = await client.auth.signUp({
74
+ email,
75
+ password,
76
+ options: { data: metadata ?? {} }
77
+ });
78
+ if (error) throw error;
79
+ return data;
80
+ },
81
+ /**
82
+ * Base44 OAuth flow: redirects the browser to the provider's consent screen
83
+ * and back to `returnPath` (relative to window.location.origin) on success.
84
+ * Supabase equivalent: signInWithOAuth with an absolute redirectTo.
85
+ */
86
+ async loginWithProvider(provider, returnPath) {
87
+ const redirectTo = typeof window !== "undefined" ? window.location.origin + (returnPath ?? "/") : returnPath;
88
+ const { data, error } = await client.auth.signInWithOAuth({
89
+ provider,
90
+ options: redirectTo ? { redirectTo } : void 0
91
+ });
92
+ if (error) throw error;
93
+ return data;
94
+ },
95
+ /**
96
+ * Base44 email OTP verification after register(). Supabase's verifyOtp
97
+ * already sets the session on success, so setToken() is redundant afterward
98
+ * (kept as a soft no-op for source-compat).
99
+ */
100
+ async verifyOtp({ email, otpCode }) {
101
+ const { data, error } = await client.auth.verifyOtp({
102
+ email,
103
+ token: otpCode,
104
+ type: "signup"
105
+ });
106
+ if (error) throw error;
107
+ return {
108
+ access_token: data.session?.access_token,
109
+ session: data.session,
110
+ user: data.user
111
+ };
112
+ },
113
+ async resendOtp(email) {
114
+ const { error } = await client.auth.resend({ email, type: "signup" });
115
+ if (error) throw error;
116
+ },
117
+ /**
118
+ * No-op alias kept so migrated code doesn't TypeError. Supabase's
119
+ * verifyOtp() already installed the session — pushing the access_token in
120
+ * again would do nothing productive (Supabase requires access + refresh to
121
+ * setSession, and Base44 returns only access). Warns once per process.
122
+ */
123
+ setToken(_accessToken) {
124
+ if (!setTokenWarned) {
125
+ setTokenWarned = true;
126
+ if (typeof console !== "undefined") {
127
+ console.warn(
128
+ "[base44-shim] auth.setToken() is a no-op \u2014 verifyOtp()/signInWithPassword() already installed the Supabase session. Safe to remove the call."
129
+ );
130
+ }
131
+ }
132
+ },
133
+ async resetPasswordRequest(email, options) {
134
+ const redirectTo = options?.redirectTo ?? opts.resetPasswordRedirect;
135
+ const { error } = await client.auth.resetPasswordForEmail(
136
+ email,
137
+ redirectTo ? { redirectTo } : void 0
138
+ );
139
+ if (error) throw error;
140
+ },
141
+ async resetPassword({ newPassword }) {
142
+ const { data, error } = await client.auth.updateUser({ password: newPassword });
143
+ if (error) throw error;
144
+ return data;
145
+ },
49
146
  async signOut() {
50
147
  const { error } = await client.auth.signOut();
51
148
  if (error) throw error;
52
149
  },
53
- /** Base44 alias for signOut. */
54
- async logout() {
150
+ /**
151
+ * Base44 alias for signOut. If `returnUrl` is provided, navigates there
152
+ * after signOut resolves so SPAs can drop users on a public route.
153
+ */
154
+ async logout(returnUrl) {
55
155
  const { error } = await client.auth.signOut();
56
156
  if (error) throw error;
157
+ if (returnUrl && typeof window !== "undefined") {
158
+ window.location.assign(returnUrl);
159
+ }
160
+ },
161
+ async isAuthenticated() {
162
+ const { data } = await client.auth.getSession();
163
+ return !!data.session;
57
164
  },
58
165
  async getUser() {
59
166
  const { data, error } = await client.auth.getUser();
@@ -427,6 +534,18 @@ function makeIntegrations(client, opts) {
427
534
  });
428
535
  if (error) throw error;
429
536
  return data;
537
+ },
538
+ async GenerateImage(payload) {
539
+ if (!opts.generateImageFunction) {
540
+ throw new Error(
541
+ "GenerateImage not configured. Image-gen features are disabled in this self-host build. Set integrations.generateImageFunction in createClient() and deploy an edge function that proxies to your image endpoint (SDXL / DALL\xB7E gateway)."
542
+ );
543
+ }
544
+ const { data, error } = await client.functions.invoke(opts.generateImageFunction, {
545
+ body: payload
546
+ });
547
+ if (error) throw error;
548
+ return data;
430
549
  }
431
550
  };
432
551
  return { Core };
@@ -559,6 +678,7 @@ function createClient(options) {
559
678
  appLogs: makeAppLogs(supabase, options.schemaPrefix),
560
679
  users: makeUsers(),
561
680
  app,
681
+ agents: makeAgents(options.agents),
562
682
  asServiceRole
563
683
  };
564
684
  }