@staticbot/base44-supabase-shim 0.3.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/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/chunk-PUROQAXO.js +49 -0
- package/dist/chunk-PUROQAXO.js.map +1 -0
- package/dist/chunk-SWURHLDN.js +267 -0
- package/dist/chunk-SWURHLDN.js.map +1 -0
- package/dist/index.cjs +572 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +159 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.js +249 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations.cjs +73 -0
- package/dist/integrations.cjs.map +1 -0
- package/dist/integrations.d.cts +56 -0
- package/dist/integrations.d.ts +56 -0
- package/dist/integrations.js +7 -0
- package/dist/integrations.js.map +1 -0
- package/dist/server.cjs +321 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +24 -0
- package/dist/server.d.ts +24 -0
- package/dist/server.js +38 -0
- package/dist/server.js.map +1 -0
- package/dist/types-HIZDZaWa.d.cts +115 -0
- package/dist/types-HIZDZaWa.d.ts +115 -0
- package/package.json +49 -0
- package/src/auth.ts +101 -0
- package/src/entities.ts +267 -0
- package/src/functions.ts +28 -0
- package/src/index.ts +97 -0
- package/src/integrations.ts +94 -0
- package/src/misc.ts +43 -0
- package/src/server.ts +54 -0
- package/src/storage.ts +63 -0
- package/src/types.ts +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bitfiction s.r.o. (https://staticbot.dev)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# @staticbot/base44-supabase-shim
|
|
2
|
+
|
|
3
|
+
Drop-in shim that exposes the **same surface as `@base44/sdk`** but routes
|
|
4
|
+
every call to a **Supabase** backend (Postgres + GoTrue + Storage + Edge
|
|
5
|
+
Functions). Built so you swap one import in `src/api/base44Client.js` and
|
|
6
|
+
your hundreds of pages keep working.
|
|
7
|
+
|
|
8
|
+
This is the runtime shim Staticbot's Base44 → Supabase migration vendors into
|
|
9
|
+
your repo as the final step of the migration. You can also use it standalone
|
|
10
|
+
if you're doing the migration by hand.
|
|
11
|
+
|
|
12
|
+
## Install (automated path — done for you by Staticbot)
|
|
13
|
+
|
|
14
|
+
Staticbot's Base44 native migration vendors the prebuilt `dist/` directly into
|
|
15
|
+
your repo at `vendor/base44-supabase-shim/` and patches `package.json` to point
|
|
16
|
+
at it. You don't need to do anything manually — just merge the PR that lands
|
|
17
|
+
on the `staticbot/base44-switchover-<id>` branch.
|
|
18
|
+
|
|
19
|
+
## Install (manual)
|
|
20
|
+
|
|
21
|
+
In each consuming app, vendor the prebuilt tarball or add as a git submodule
|
|
22
|
+
(so builds don't depend on an npm registry):
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
# Option A: git submodule (you'll need to build it once locally)
|
|
26
|
+
git submodule add https://github.com/staticbot/staticbot-base44-supabase-shim.git vendor/base44-supabase-shim
|
|
27
|
+
cd vendor/base44-supabase-shim && npm i && npm run build
|
|
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
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
In `package.json`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@staticbot/base44-supabase-shim": "file:./vendor/base44-supabase-shim",
|
|
40
|
+
"@supabase/supabase-js": "^2.45.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Use (browser / Vite app)
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
// src/api/base44Client.js — replace @base44/sdk import
|
|
49
|
+
import { createClient } from '@staticbot/base44-supabase-shim';
|
|
50
|
+
|
|
51
|
+
export const base44 = createClient({
|
|
52
|
+
supabaseUrl: import.meta.env.VITE_SUPABASE_URL,
|
|
53
|
+
supabaseAnonKey: import.meta.env.VITE_SUPABASE_ANON_KEY,
|
|
54
|
+
schemaPrefix: 'propertyflow', // app-specific schema
|
|
55
|
+
sharedSchema: 'core', // shared entities live here
|
|
56
|
+
// sharedEntities default: ['Customer','Company','User','Role','Department','Notification','AuditLog']
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Then everything in your existing pages keeps working:
|
|
60
|
+
const customers = await base44.entities.Customer.list('-created_date', 50);
|
|
61
|
+
const c = await base44.entities.Customer.get('uuid');
|
|
62
|
+
await base44.entities.Customer.update('uuid', { phone: '081...' });
|
|
63
|
+
await base44.entities.Unit.create({ unit_no: 'A-101' });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Use (Supabase Edge Function — Deno)
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createClientFromRequest } from 'npm:@staticbot/base44-supabase-shim/server';
|
|
70
|
+
|
|
71
|
+
Deno.serve(async (req) => {
|
|
72
|
+
const base44 = createClientFromRequest(req, {
|
|
73
|
+
supabaseUrl: Deno.env.get('SUPABASE_URL')!,
|
|
74
|
+
supabaseAnonKey: Deno.env.get('SUPABASE_ANON_KEY')!,
|
|
75
|
+
supabaseServiceRoleKey: Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!,
|
|
76
|
+
schemaPrefix: 'propertyflow',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const customers = await base44.asServiceRole.entities.Customer.filter(
|
|
80
|
+
{},
|
|
81
|
+
'-created_date',
|
|
82
|
+
50,
|
|
83
|
+
);
|
|
84
|
+
return new Response(JSON.stringify({ data: customers }));
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Entity → table mapping
|
|
89
|
+
|
|
90
|
+
Default rule: PascalCase → snake_case + plural.
|
|
91
|
+
- `Customer` → `customers`
|
|
92
|
+
- `ChartOfAccount` → `chart_of_accounts`
|
|
93
|
+
- `MeetingMinute` → `meeting_minutes`
|
|
94
|
+
|
|
95
|
+
Override via `entityMap` if a real table doesn't follow the rule:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
createClient({
|
|
99
|
+
...,
|
|
100
|
+
entityMap: {
|
|
101
|
+
Customer: { schema: 'core', table: 'customers' }, // explicit
|
|
102
|
+
Job: { schema: 'construction', table: 'project_jobs' }, // non-default name
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Filter syntax
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
// Equality (default)
|
|
111
|
+
await base44.entities.Customer.filter({ status: 'active', vip: true });
|
|
112
|
+
|
|
113
|
+
// IN (pass array)
|
|
114
|
+
await base44.entities.Customer.filter({ id: ['a', 'b', 'c'] });
|
|
115
|
+
|
|
116
|
+
// Operators
|
|
117
|
+
await base44.entities.Invoice.filter({
|
|
118
|
+
amount: { op: 'gt', value: 1000 },
|
|
119
|
+
customer_name: { op: 'ilike', value: '%co.%' },
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Operators: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`, `in`.
|
|
124
|
+
|
|
125
|
+
## Order by
|
|
126
|
+
|
|
127
|
+
- String form (Base44 convention): `'name'` ascending, `'-created_date'` descending.
|
|
128
|
+
- Object form: `{ field: 'name', ascending: false }`.
|
|
129
|
+
|
|
130
|
+
## What's NOT covered
|
|
131
|
+
|
|
132
|
+
- **Stripe / payments** — Base44 had managed integration; here, wire Stripe into
|
|
133
|
+
edge functions yourself.
|
|
134
|
+
- **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).
|
|
137
|
+
- **Schema discovery** — entity definitions must exist in Postgres first
|
|
138
|
+
(separate migrations). The shim assumes tables exist with conventional names.
|
|
139
|
+
|
|
140
|
+
## Test
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
npm i
|
|
144
|
+
npm test # vitest, mocked SupabaseClient
|
|
145
|
+
npm run build # tsup → dist/
|
|
146
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// src/integrations.ts
|
|
2
|
+
function makeIntegrations(client, opts) {
|
|
3
|
+
const Core = {
|
|
4
|
+
async UploadFile({
|
|
5
|
+
file,
|
|
6
|
+
bucket,
|
|
7
|
+
path,
|
|
8
|
+
contentType
|
|
9
|
+
}) {
|
|
10
|
+
const b = bucket ?? opts.defaultBucket;
|
|
11
|
+
const p = path ?? `uploads/${Date.now()}-${Math.random().toString(36).slice(2, 8)}-${file?.name ?? "file"}`;
|
|
12
|
+
const { error } = await client.storage.from(b).upload(p, file, {
|
|
13
|
+
contentType: contentType ?? file?.type,
|
|
14
|
+
upsert: false
|
|
15
|
+
});
|
|
16
|
+
if (error) throw error;
|
|
17
|
+
const { data } = client.storage.from(b).getPublicUrl(p);
|
|
18
|
+
return { url: data.publicUrl, path: p };
|
|
19
|
+
},
|
|
20
|
+
async SendEmail(payload) {
|
|
21
|
+
if (!opts.sendEmailFunction) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"SendEmail not configured. Set integrations.sendEmailFunction in createClient() and deploy a corresponding Supabase Edge Function (e.g. send-email)."
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const { error } = await client.functions.invoke(opts.sendEmailFunction, { body: payload });
|
|
27
|
+
if (error) throw error;
|
|
28
|
+
return { ok: true };
|
|
29
|
+
},
|
|
30
|
+
async InvokeLLM(payload) {
|
|
31
|
+
if (!opts.invokeLlmFunction) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"InvokeLLM not configured. AI features are disabled in this self-host build. Set integrations.invokeLlmFunction in createClient() and deploy an edge function that proxies to your LLM endpoint (Ollama / internal gateway)."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const { data, error } = await client.functions.invoke(opts.invokeLlmFunction, {
|
|
37
|
+
body: payload
|
|
38
|
+
});
|
|
39
|
+
if (error) throw error;
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
return { Core };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export {
|
|
47
|
+
makeIntegrations
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=chunk-PUROQAXO.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\n/**\n * Stub of `base44.integrations.Core.*`. Three 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 *\n * Air-gapped LAN cannot call OpenAI directly; wire your own LLM endpoint\n * (Ollama, internal API gateway, etc.) inside an edge function and pass the\n * function name via createClient({ integrations: { invokeLlmFunction: 'llm-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\n return { Core };\n}\n"],"mappings":";AA4BO,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,EACF;AAEA,SAAO,EAAE,KAAK;AAChB;","names":[]}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
// src/entities.ts
|
|
2
|
+
var DEFAULT_SHARED_ENTITIES = [
|
|
3
|
+
// Auth/identity (option C: shared)
|
|
4
|
+
"User",
|
|
5
|
+
"Role",
|
|
6
|
+
"UserRole",
|
|
7
|
+
// 'finance.user_roles' was 404 — UserRole belongs in shared
|
|
8
|
+
"Department",
|
|
9
|
+
// Org (option C: shared)
|
|
10
|
+
"Company",
|
|
11
|
+
// Cross-app entities exposed by core schema
|
|
12
|
+
"AuditLog"
|
|
13
|
+
// Note: Customer is intentionally NOT shared — each app's Customer schema
|
|
14
|
+
// differs significantly (CRM vs Facility vs Construction).
|
|
15
|
+
];
|
|
16
|
+
function defaultEntityToTable(entityName) {
|
|
17
|
+
const snake = entityName.replace(
|
|
18
|
+
/([A-Z])/g,
|
|
19
|
+
(m, c, i) => i === 0 ? c.toLowerCase() : "_" + c.toLowerCase()
|
|
20
|
+
);
|
|
21
|
+
if (snake.endsWith("y") && !/[aeiou]y$/.test(snake)) return snake.slice(0, -1) + "ies";
|
|
22
|
+
if (/(s|x|z|ch|sh)$/.test(snake)) return snake + "es";
|
|
23
|
+
return snake + "s";
|
|
24
|
+
}
|
|
25
|
+
function resolveEntityMapping(entityName, opts) {
|
|
26
|
+
if (opts.entityMap?.[entityName]) return opts.entityMap[entityName];
|
|
27
|
+
const sharedEntities = opts.sharedEntities ?? DEFAULT_SHARED_ENTITIES;
|
|
28
|
+
const sharedSchema = opts.sharedSchema ?? "core";
|
|
29
|
+
const schema = sharedEntities.includes(entityName) ? sharedSchema : opts.schemaPrefix;
|
|
30
|
+
return { schema, table: defaultEntityToTable(entityName) };
|
|
31
|
+
}
|
|
32
|
+
function parseOrderBy(input) {
|
|
33
|
+
if (!input) return void 0;
|
|
34
|
+
if (typeof input === "object") return input;
|
|
35
|
+
if (input.startsWith("-")) return { field: input.slice(1), ascending: false };
|
|
36
|
+
return { field: input, ascending: true };
|
|
37
|
+
}
|
|
38
|
+
var MONGO_OPS = [
|
|
39
|
+
"$eq",
|
|
40
|
+
"$ne",
|
|
41
|
+
"$gt",
|
|
42
|
+
"$gte",
|
|
43
|
+
"$lt",
|
|
44
|
+
"$lte",
|
|
45
|
+
"$in",
|
|
46
|
+
"$nin",
|
|
47
|
+
"$like",
|
|
48
|
+
"$ilike"
|
|
49
|
+
];
|
|
50
|
+
function isMongoFilter(v) {
|
|
51
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
52
|
+
return Object.keys(v).some(
|
|
53
|
+
(k) => MONGO_OPS.includes(k)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
function applyMongoOps(query, field, ops) {
|
|
57
|
+
for (const [op, value] of Object.entries(ops)) {
|
|
58
|
+
switch (op) {
|
|
59
|
+
case "$eq":
|
|
60
|
+
query = query.eq(field, value);
|
|
61
|
+
break;
|
|
62
|
+
case "$ne":
|
|
63
|
+
query = query.neq(field, value);
|
|
64
|
+
break;
|
|
65
|
+
case "$gt":
|
|
66
|
+
query = query.gt(field, value);
|
|
67
|
+
break;
|
|
68
|
+
case "$gte":
|
|
69
|
+
query = query.gte(field, value);
|
|
70
|
+
break;
|
|
71
|
+
case "$lt":
|
|
72
|
+
query = query.lt(field, value);
|
|
73
|
+
break;
|
|
74
|
+
case "$lte":
|
|
75
|
+
query = query.lte(field, value);
|
|
76
|
+
break;
|
|
77
|
+
case "$in":
|
|
78
|
+
query = query.in(field, value);
|
|
79
|
+
break;
|
|
80
|
+
case "$nin":
|
|
81
|
+
query = query.not("in", `(${value.map((v) => JSON.stringify(v)).join(",")})`);
|
|
82
|
+
break;
|
|
83
|
+
case "$like":
|
|
84
|
+
query = query.like(field, value);
|
|
85
|
+
break;
|
|
86
|
+
case "$ilike":
|
|
87
|
+
query = query.ilike(field, value);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return query;
|
|
92
|
+
}
|
|
93
|
+
function applyFilter(query, where) {
|
|
94
|
+
for (const [field, raw] of Object.entries(where)) {
|
|
95
|
+
const v = raw;
|
|
96
|
+
if (v === null) {
|
|
97
|
+
query = query.is(field, null);
|
|
98
|
+
} else if (isMongoFilter(v)) {
|
|
99
|
+
query = applyMongoOps(query, field, v);
|
|
100
|
+
} else if (typeof v === "object" && !Array.isArray(v) && "op" in v) {
|
|
101
|
+
const { op, value } = v;
|
|
102
|
+
query = applyMongoOps(query, field, { [`$${op}`]: value });
|
|
103
|
+
} else if (Array.isArray(v)) {
|
|
104
|
+
query = query.in(field, v);
|
|
105
|
+
} else {
|
|
106
|
+
query = query.eq(field, v);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return query;
|
|
110
|
+
}
|
|
111
|
+
function applyRange(query, limit, skip) {
|
|
112
|
+
if (typeof skip === "number" && typeof limit === "number") {
|
|
113
|
+
return query.range(skip, skip + limit - 1);
|
|
114
|
+
}
|
|
115
|
+
if (typeof limit === "number") return query.limit(limit);
|
|
116
|
+
return query;
|
|
117
|
+
}
|
|
118
|
+
function makeEntityApi(client, mapping) {
|
|
119
|
+
const from = () => client.schema(mapping.schema).from(mapping.table);
|
|
120
|
+
const isMissingTableError = (e) => {
|
|
121
|
+
if (!e || typeof e !== "object") return false;
|
|
122
|
+
const code = e.code;
|
|
123
|
+
const status = e.status;
|
|
124
|
+
return code === "PGRST205" || code === "42P01" || status === 404;
|
|
125
|
+
};
|
|
126
|
+
const warnedTables = /* @__PURE__ */ new Set();
|
|
127
|
+
const warnMissing = () => {
|
|
128
|
+
const key = `${mapping.schema}.${mapping.table}`;
|
|
129
|
+
if (warnedTables.has(key)) return;
|
|
130
|
+
warnedTables.add(key);
|
|
131
|
+
if (typeof console !== "undefined") console.warn(`[base44-shim] table ${key} not found \u2014 returning empty result`);
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
async list(orderBy, limit, skip) {
|
|
135
|
+
let q = from().select("*");
|
|
136
|
+
const ob = parseOrderBy(orderBy);
|
|
137
|
+
if (ob) q = q.order(ob.field, { ascending: ob.ascending ?? true });
|
|
138
|
+
q = applyRange(q, limit, skip);
|
|
139
|
+
const { data, error } = await q;
|
|
140
|
+
if (error) {
|
|
141
|
+
if (isMissingTableError(error)) {
|
|
142
|
+
warnMissing();
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
return data ?? [];
|
|
148
|
+
},
|
|
149
|
+
async filter(where, orderBy, limit, skip) {
|
|
150
|
+
let q = from().select("*");
|
|
151
|
+
q = applyFilter(q, where);
|
|
152
|
+
const ob = parseOrderBy(orderBy);
|
|
153
|
+
if (ob) q = q.order(ob.field, { ascending: ob.ascending ?? true });
|
|
154
|
+
q = applyRange(q, limit, skip);
|
|
155
|
+
const { data, error } = await q;
|
|
156
|
+
if (error) {
|
|
157
|
+
if (isMissingTableError(error)) {
|
|
158
|
+
warnMissing();
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
return data ?? [];
|
|
164
|
+
},
|
|
165
|
+
async get(id) {
|
|
166
|
+
const { data, error } = await from().select("*").eq("id", id).maybeSingle();
|
|
167
|
+
if (error) {
|
|
168
|
+
if (isMissingTableError(error)) {
|
|
169
|
+
warnMissing();
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
return data ?? null;
|
|
175
|
+
},
|
|
176
|
+
async read(id) {
|
|
177
|
+
const { data, error } = await from().select("*").eq("id", id).maybeSingle();
|
|
178
|
+
if (error) {
|
|
179
|
+
if (isMissingTableError(error)) {
|
|
180
|
+
warnMissing();
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
return data ?? null;
|
|
186
|
+
},
|
|
187
|
+
async create(body) {
|
|
188
|
+
const { data, error } = await from().insert(body).select().single();
|
|
189
|
+
if (error) throw error;
|
|
190
|
+
return data;
|
|
191
|
+
},
|
|
192
|
+
async bulkCreate(rows) {
|
|
193
|
+
if (!rows.length) return [];
|
|
194
|
+
const { data, error } = await from().insert(rows).select();
|
|
195
|
+
if (error) throw error;
|
|
196
|
+
return data ?? [];
|
|
197
|
+
},
|
|
198
|
+
async update(id, body) {
|
|
199
|
+
const { data, error } = await from().update(body).eq("id", id).select().single();
|
|
200
|
+
if (error) throw error;
|
|
201
|
+
return data;
|
|
202
|
+
},
|
|
203
|
+
async bulkUpdate(rows) {
|
|
204
|
+
if (!rows.length) return [];
|
|
205
|
+
const results = await Promise.all(
|
|
206
|
+
rows.map(
|
|
207
|
+
({ id, ...patch }) => from().update(patch).eq("id", id).select().single()
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
const errors = results.map((r) => r.error).filter(Boolean);
|
|
211
|
+
if (errors.length) throw errors[0];
|
|
212
|
+
return results.map((r) => r.data);
|
|
213
|
+
},
|
|
214
|
+
async updateMany(where, update) {
|
|
215
|
+
const patch = update?.$set ?? update;
|
|
216
|
+
let q = from().update(patch);
|
|
217
|
+
q = applyFilter(q, where);
|
|
218
|
+
const { data, error } = await q.select();
|
|
219
|
+
if (error) throw error;
|
|
220
|
+
return data ?? [];
|
|
221
|
+
},
|
|
222
|
+
async delete(id) {
|
|
223
|
+
const { error } = await from().delete().eq("id", id);
|
|
224
|
+
if (error) throw error;
|
|
225
|
+
},
|
|
226
|
+
async deleteMany(where) {
|
|
227
|
+
let q = from().delete();
|
|
228
|
+
q = applyFilter(q, where);
|
|
229
|
+
const { error } = await q;
|
|
230
|
+
if (error) throw error;
|
|
231
|
+
},
|
|
232
|
+
subscribe(callback) {
|
|
233
|
+
const channel = client.channel(`${mapping.schema}.${mapping.table}.${Math.random().toString(36).slice(2, 8)}`).on(
|
|
234
|
+
"postgres_changes",
|
|
235
|
+
{ event: "*", schema: mapping.schema, table: mapping.table },
|
|
236
|
+
(payload) => {
|
|
237
|
+
const type = payload.eventType.toLowerCase();
|
|
238
|
+
callback({ type, new: payload.new, old: payload.old });
|
|
239
|
+
}
|
|
240
|
+
).subscribe();
|
|
241
|
+
return () => {
|
|
242
|
+
void client.removeChannel(channel);
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function makeEntitiesProxy(client, opts) {
|
|
248
|
+
const cache = {};
|
|
249
|
+
return new Proxy({}, {
|
|
250
|
+
get(_target, prop) {
|
|
251
|
+
if (typeof prop !== "string") return void 0;
|
|
252
|
+
if (!cache[prop]) {
|
|
253
|
+
const mapping = resolveEntityMapping(prop, opts);
|
|
254
|
+
cache[prop] = makeEntityApi(client, mapping);
|
|
255
|
+
}
|
|
256
|
+
return cache[prop];
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export {
|
|
262
|
+
defaultEntityToTable,
|
|
263
|
+
resolveEntityMapping,
|
|
264
|
+
parseOrderBy,
|
|
265
|
+
makeEntitiesProxy
|
|
266
|
+
};
|
|
267
|
+
//# sourceMappingURL=chunk-SWURHLDN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/entities.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"],"mappings":"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","names":[]}
|