@palbase/backend 2.0.2 → 4.0.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/dist/chunk-EG7TTYHY.js +235 -0
- package/dist/chunk-EG7TTYHY.js.map +1 -0
- package/dist/chunk-WUQO76NW.js +101 -0
- package/dist/chunk-WUQO76NW.js.map +1 -0
- package/dist/db/env.cjs +19 -0
- package/dist/db/env.cjs.map +1 -0
- package/dist/db/env.d.cts +45 -0
- package/dist/db/env.d.ts +45 -0
- package/dist/db/env.js +1 -0
- package/dist/db/env.js.map +1 -0
- package/dist/db/index.cjs +143 -231
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +4 -20
- package/dist/db/index.d.ts +4 -20
- package/dist/db/index.js +13 -233
- package/dist/db/index.js.map +1 -1
- package/dist/{endpoint-Djk5L6G2.d.ts → endpoint-2d_DpASt.d.cts} +94 -96
- package/dist/{endpoint-BlcY2xNA.d.cts → endpoint-2d_DpASt.d.ts} +94 -96
- package/dist/index-DZW9CjiY.d.ts +463 -0
- package/dist/index-DzRFS3Tl.d.cts +463 -0
- package/dist/index.cjs +557 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +278 -161
- package/dist/index.d.ts +278 -161
- package/dist/index.js +343 -12
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +57 -2
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +1 -2
- package/dist/test/index.d.ts +1 -2
- package/dist/test/index.js +10 -2
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +33 -12
- package/docs/background.md +19 -13
- package/docs/database.md +70 -17
- package/docs/endpoints.md +103 -79
- package/docs/errors.md +37 -31
- package/docs/events.md +25 -17
- package/docs/getting-started.md +38 -18
- package/docs/llms-full.txt +758 -267
- package/docs/llms.txt +3 -1
- package/docs/migrations.md +98 -0
- package/docs/resources.md +94 -0
- package/docs/routing.md +54 -27
- package/docs/schema.md +163 -42
- package/docs/services.md +17 -14
- package/package.json +12 -2
- package/dist/chunk-4J3F32SH.js +0 -96
- package/dist/chunk-4J3F32SH.js.map +0 -1
- package/dist/chunk-L36JLUPO.js +0 -97
- package/dist/chunk-L36JLUPO.js.map +0 -1
- package/dist/schema-BqfEhIC0.d.cts +0 -133
- package/dist/schema-BqfEhIC0.d.ts +0 -133
package/dist/db/index.js
CHANGED
|
@@ -1,251 +1,31 @@
|
|
|
1
1
|
import {
|
|
2
|
+
EXTENSION_DEPENDENCIES,
|
|
3
|
+
PALBASE_EXTENSIONS,
|
|
4
|
+
PolicyBuilder,
|
|
2
5
|
boolean,
|
|
3
6
|
defineSchema,
|
|
4
7
|
enumType,
|
|
5
8
|
integer,
|
|
9
|
+
isPalbaseExtension,
|
|
6
10
|
jsonb,
|
|
7
|
-
|
|
11
|
+
makeTypedDB,
|
|
12
|
+
policy,
|
|
8
13
|
text,
|
|
9
14
|
timestamp,
|
|
10
15
|
uuid
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
|
|
13
|
-
// src/db/generator.ts
|
|
14
|
-
function columnTypeToSQL(col) {
|
|
15
|
-
switch (col.type) {
|
|
16
|
-
case "uuid":
|
|
17
|
-
return "UUID";
|
|
18
|
-
case "text":
|
|
19
|
-
return "TEXT";
|
|
20
|
-
case "integer":
|
|
21
|
-
return "INTEGER";
|
|
22
|
-
case "boolean":
|
|
23
|
-
return "BOOLEAN";
|
|
24
|
-
case "timestamp":
|
|
25
|
-
return "TIMESTAMPTZ";
|
|
26
|
-
case "jsonb":
|
|
27
|
-
return "JSONB";
|
|
28
|
-
case "enum":
|
|
29
|
-
return col.enumName ?? "TEXT";
|
|
30
|
-
default:
|
|
31
|
-
return "TEXT";
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function defaultClause(col) {
|
|
35
|
-
if (col.defaultRandom) {
|
|
36
|
-
return " DEFAULT gen_random_uuid()";
|
|
37
|
-
}
|
|
38
|
-
if (col.defaultNow) {
|
|
39
|
-
return " DEFAULT now()";
|
|
40
|
-
}
|
|
41
|
-
if (col.defaultValue !== void 0) {
|
|
42
|
-
if (typeof col.defaultValue === "string") {
|
|
43
|
-
const escaped = col.defaultValue.replace(/'/g, "''");
|
|
44
|
-
return ` DEFAULT '${escaped}'`;
|
|
45
|
-
}
|
|
46
|
-
if (typeof col.defaultValue === "boolean") {
|
|
47
|
-
return ` DEFAULT ${col.defaultValue ? "true" : "false"}`;
|
|
48
|
-
}
|
|
49
|
-
if (typeof col.defaultValue === "object" && col.defaultValue !== null) {
|
|
50
|
-
const escaped = JSON.stringify(col.defaultValue).replace(/'/g, "''");
|
|
51
|
-
return ` DEFAULT '${escaped}'::jsonb`;
|
|
52
|
-
}
|
|
53
|
-
return ` DEFAULT ${col.defaultValue}`;
|
|
54
|
-
}
|
|
55
|
-
return "";
|
|
56
|
-
}
|
|
57
|
-
function onDeleteSQL(action) {
|
|
58
|
-
switch (action) {
|
|
59
|
-
case "cascade":
|
|
60
|
-
return "CASCADE";
|
|
61
|
-
case "set null":
|
|
62
|
-
return "SET NULL";
|
|
63
|
-
case "restrict":
|
|
64
|
-
return "RESTRICT";
|
|
65
|
-
case "no action":
|
|
66
|
-
return "NO ACTION";
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
function generateEnumTypes(tables) {
|
|
70
|
-
const enums = /* @__PURE__ */ new Map();
|
|
71
|
-
for (const tableDef of Object.values(tables)) {
|
|
72
|
-
for (const col of Object.values(tableDef.columns)) {
|
|
73
|
-
if (col._def.type === "enum" && col._def.enumName && col._def.enumValues) {
|
|
74
|
-
enums.set(col._def.enumName, col._def.enumValues);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return Array.from(enums.entries()).map(([name, values]) => {
|
|
79
|
-
const vals = values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
80
|
-
return `CREATE TYPE ${name} AS ENUM (${vals});`;
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
function generateCreateTable(tableDef) {
|
|
84
|
-
const lines = [];
|
|
85
|
-
for (const [colName, colBuilder] of Object.entries(tableDef.columns)) {
|
|
86
|
-
const col = colBuilder._def;
|
|
87
|
-
let line = ` ${colName} ${columnTypeToSQL(col)}`;
|
|
88
|
-
if (col.primaryKey) {
|
|
89
|
-
line += " PRIMARY KEY";
|
|
90
|
-
}
|
|
91
|
-
if (!col.nullable && !col.primaryKey) {
|
|
92
|
-
line += " NOT NULL";
|
|
93
|
-
}
|
|
94
|
-
line += defaultClause(col);
|
|
95
|
-
if (col.references) {
|
|
96
|
-
line += ` REFERENCES ${col.references.table}(${col.references.column})`;
|
|
97
|
-
if (col.onDeleteAction) {
|
|
98
|
-
line += ` ON DELETE ${onDeleteSQL(col.onDeleteAction)}`;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
lines.push(line);
|
|
102
|
-
}
|
|
103
|
-
return `CREATE TABLE ${tableDef.name} (
|
|
104
|
-
${lines.join(",\n")}
|
|
105
|
-
);`;
|
|
106
|
-
}
|
|
107
|
-
function generateDropTable(tableDef) {
|
|
108
|
-
return `DROP TABLE IF EXISTS ${tableDef.name};`;
|
|
109
|
-
}
|
|
110
|
-
function generateDropEnumTypes(tables) {
|
|
111
|
-
const enums = /* @__PURE__ */ new Set();
|
|
112
|
-
for (const tableDef of Object.values(tables)) {
|
|
113
|
-
for (const col of Object.values(tableDef.columns)) {
|
|
114
|
-
if (col._def.type === "enum" && col._def.enumName) {
|
|
115
|
-
enums.add(col._def.enumName);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return Array.from(enums).map((name) => `DROP TYPE IF EXISTS ${name};`);
|
|
120
|
-
}
|
|
121
|
-
function generateMigration(schema) {
|
|
122
|
-
const upParts = [];
|
|
123
|
-
const downParts = [];
|
|
124
|
-
const enumTypes = generateEnumTypes(schema.tables);
|
|
125
|
-
upParts.push(...enumTypes);
|
|
126
|
-
for (const tableDef of Object.values(schema.tables)) {
|
|
127
|
-
upParts.push(generateCreateTable(tableDef));
|
|
128
|
-
}
|
|
129
|
-
const tableNames = Object.values(schema.tables);
|
|
130
|
-
for (let i = tableNames.length - 1; i >= 0; i--) {
|
|
131
|
-
downParts.push(generateDropTable(tableNames[i]));
|
|
132
|
-
}
|
|
133
|
-
downParts.push(...generateDropEnumTypes(schema.tables));
|
|
134
|
-
return {
|
|
135
|
-
up: upParts.join("\n\n"),
|
|
136
|
-
down: downParts.join("\n\n")
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function diffSchemas(oldSchema, newSchema) {
|
|
140
|
-
const addedTables = [];
|
|
141
|
-
const droppedTables = [];
|
|
142
|
-
const columnDiffs = [];
|
|
143
|
-
const oldTableNames = new Set(Object.keys(oldSchema.tables));
|
|
144
|
-
const newTableNames = new Set(Object.keys(newSchema.tables));
|
|
145
|
-
for (const name of newTableNames) {
|
|
146
|
-
if (!oldTableNames.has(name)) {
|
|
147
|
-
addedTables.push(newSchema.tables[name]);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
for (const name of oldTableNames) {
|
|
151
|
-
if (!newTableNames.has(name)) {
|
|
152
|
-
droppedTables.push(oldSchema.tables[name]);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
for (const name of newTableNames) {
|
|
156
|
-
if (!oldTableNames.has(name)) continue;
|
|
157
|
-
const oldTable = oldSchema.tables[name];
|
|
158
|
-
const newTable = newSchema.tables[name];
|
|
159
|
-
const oldCols = new Set(Object.keys(oldTable.columns));
|
|
160
|
-
const newCols = new Set(Object.keys(newTable.columns));
|
|
161
|
-
for (const col of newCols) {
|
|
162
|
-
if (!oldCols.has(col)) {
|
|
163
|
-
columnDiffs.push({
|
|
164
|
-
type: "add",
|
|
165
|
-
table: newTable.name,
|
|
166
|
-
column: col,
|
|
167
|
-
def: newTable.columns[col]._def
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
for (const col of oldCols) {
|
|
172
|
-
if (!newCols.has(col)) {
|
|
173
|
-
columnDiffs.push({
|
|
174
|
-
type: "drop",
|
|
175
|
-
table: oldTable.name,
|
|
176
|
-
column: col,
|
|
177
|
-
oldDef: oldTable.columns[col]._def
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return { addedTables, droppedTables, columnDiffs };
|
|
183
|
-
}
|
|
184
|
-
function generateAddColumn(table2, column, col) {
|
|
185
|
-
let sql = `ALTER TABLE ${table2} ADD COLUMN ${column} ${columnTypeToSQL(col)}`;
|
|
186
|
-
if (!col.nullable) {
|
|
187
|
-
sql += " NOT NULL";
|
|
188
|
-
}
|
|
189
|
-
sql += defaultClause(col);
|
|
190
|
-
if (col.references) {
|
|
191
|
-
sql += ` REFERENCES ${col.references.table}(${col.references.column})`;
|
|
192
|
-
if (col.onDeleteAction) {
|
|
193
|
-
sql += ` ON DELETE ${onDeleteSQL(col.onDeleteAction)}`;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
return `${sql};`;
|
|
197
|
-
}
|
|
198
|
-
function generateDropColumn(table2, column) {
|
|
199
|
-
return `ALTER TABLE ${table2} DROP COLUMN ${column};`;
|
|
200
|
-
}
|
|
201
|
-
function generateDiffMigration(oldSchema, newSchema) {
|
|
202
|
-
const { addedTables, droppedTables, columnDiffs } = diffSchemas(oldSchema, newSchema);
|
|
203
|
-
if (addedTables.length === 0 && droppedTables.length === 0 && columnDiffs.length === 0) {
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
const upParts = [];
|
|
207
|
-
const downParts = [];
|
|
208
|
-
const newEnums = generateEnumTypes(
|
|
209
|
-
Object.fromEntries(addedTables.map((t) => [t.name, t]))
|
|
210
|
-
);
|
|
211
|
-
upParts.push(...newEnums);
|
|
212
|
-
for (const table2 of addedTables) {
|
|
213
|
-
upParts.push(generateCreateTable(table2));
|
|
214
|
-
downParts.push(generateDropTable(table2));
|
|
215
|
-
}
|
|
216
|
-
for (const table2 of droppedTables) {
|
|
217
|
-
upParts.push(generateDropTable(table2));
|
|
218
|
-
downParts.push(generateCreateTable(table2));
|
|
219
|
-
}
|
|
220
|
-
for (const diff of columnDiffs) {
|
|
221
|
-
if (diff.type === "add" && diff.def) {
|
|
222
|
-
upParts.push(generateAddColumn(diff.table, diff.column, diff.def));
|
|
223
|
-
downParts.push(generateDropColumn(diff.table, diff.column));
|
|
224
|
-
} else if (diff.type === "drop") {
|
|
225
|
-
upParts.push(generateDropColumn(diff.table, diff.column));
|
|
226
|
-
if (diff.oldDef) {
|
|
227
|
-
downParts.push(generateAddColumn(diff.table, diff.column, diff.oldDef));
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
const droppedEnums = generateDropEnumTypes(
|
|
232
|
-
Object.fromEntries(droppedTables.map((t) => [t.name, t]))
|
|
233
|
-
);
|
|
234
|
-
downParts.push(...droppedEnums);
|
|
235
|
-
return {
|
|
236
|
-
up: upParts.join("\n\n"),
|
|
237
|
-
down: downParts.join("\n\n")
|
|
238
|
-
};
|
|
239
|
-
}
|
|
16
|
+
} from "../chunk-EG7TTYHY.js";
|
|
240
17
|
export {
|
|
18
|
+
EXTENSION_DEPENDENCIES,
|
|
19
|
+
PALBASE_EXTENSIONS,
|
|
20
|
+
PolicyBuilder,
|
|
241
21
|
boolean,
|
|
242
22
|
defineSchema,
|
|
243
23
|
enumType,
|
|
244
|
-
generateDiffMigration,
|
|
245
|
-
generateMigration,
|
|
246
24
|
integer,
|
|
25
|
+
isPalbaseExtension,
|
|
247
26
|
jsonb,
|
|
248
|
-
|
|
27
|
+
makeTypedDB,
|
|
28
|
+
policy,
|
|
249
29
|
text,
|
|
250
30
|
timestamp,
|
|
251
31
|
uuid
|
package/dist/db/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/generator.ts"],"sourcesContent":["import type { ColumnDef, OnDeleteAction } from \"./columns.js\";\nimport type { SchemaDef, TableDef } from \"./schema.js\";\n\n/** Maps column types to their PostgreSQL SQL equivalents. */\nfunction columnTypeToSQL(col: ColumnDef): string {\n switch (col.type) {\n case \"uuid\":\n return \"UUID\";\n case \"text\":\n return \"TEXT\";\n case \"integer\":\n return \"INTEGER\";\n case \"boolean\":\n return \"BOOLEAN\";\n case \"timestamp\":\n return \"TIMESTAMPTZ\";\n case \"jsonb\":\n return \"JSONB\";\n case \"enum\":\n return col.enumName ?? \"TEXT\";\n default:\n return \"TEXT\";\n }\n}\n\n/** Generates the DEFAULT clause for a column. */\nfunction defaultClause(col: ColumnDef): string {\n if (col.defaultRandom) {\n return \" DEFAULT gen_random_uuid()\";\n }\n if (col.defaultNow) {\n return \" DEFAULT now()\";\n }\n if (col.defaultValue !== undefined) {\n if (typeof col.defaultValue === \"string\") {\n // Escape single quotes in string defaults\n const escaped = col.defaultValue.replace(/'/g, \"''\");\n return ` DEFAULT '${escaped}'`;\n }\n if (typeof col.defaultValue === \"boolean\") {\n return ` DEFAULT ${col.defaultValue ? \"true\" : \"false\"}`;\n }\n if (typeof col.defaultValue === \"object\" && col.defaultValue !== null) {\n const escaped = JSON.stringify(col.defaultValue).replace(/'/g, \"''\");\n return ` DEFAULT '${escaped}'::jsonb`;\n }\n return ` DEFAULT ${col.defaultValue}`;\n }\n return \"\";\n}\n\n/** Maps OnDeleteAction to SQL. */\nfunction onDeleteSQL(action: OnDeleteAction): string {\n switch (action) {\n case \"cascade\":\n return \"CASCADE\";\n case \"set null\":\n return \"SET NULL\";\n case \"restrict\":\n return \"RESTRICT\";\n case \"no action\":\n return \"NO ACTION\";\n }\n}\n\n/** Generates CREATE TYPE statements for enum columns. */\nfunction generateEnumTypes(tables: Record<string, TableDef>): string[] {\n const enums = new Map<string, string[]>();\n for (const tableDef of Object.values(tables)) {\n for (const col of Object.values(tableDef.columns)) {\n if (col._def.type === \"enum\" && col._def.enumName && col._def.enumValues) {\n enums.set(col._def.enumName, col._def.enumValues);\n }\n }\n }\n\n return Array.from(enums.entries()).map(([name, values]) => {\n const vals = values.map((v) => `'${v.replace(/'/g, \"''\")}'`).join(\", \");\n return `CREATE TYPE ${name} AS ENUM (${vals});`;\n });\n}\n\n/** Generates a CREATE TABLE statement for a single table. */\nfunction generateCreateTable(tableDef: TableDef): string {\n const lines: string[] = [];\n\n for (const [colName, colBuilder] of Object.entries(tableDef.columns)) {\n const col = colBuilder._def;\n let line = ` ${colName} ${columnTypeToSQL(col)}`;\n\n if (col.primaryKey) {\n line += \" PRIMARY KEY\";\n }\n\n if (!col.nullable && !col.primaryKey) {\n line += \" NOT NULL\";\n }\n\n line += defaultClause(col);\n\n if (col.references) {\n line += ` REFERENCES ${col.references.table}(${col.references.column})`;\n if (col.onDeleteAction) {\n line += ` ON DELETE ${onDeleteSQL(col.onDeleteAction)}`;\n }\n }\n\n lines.push(line);\n }\n\n return `CREATE TABLE ${tableDef.name} (\\n${lines.join(\",\\n\")}\\n);`;\n}\n\n/** Generates a DROP TABLE statement (for down migration). */\nfunction generateDropTable(tableDef: TableDef): string {\n return `DROP TABLE IF EXISTS ${tableDef.name};`;\n}\n\n/** Generates DROP TYPE statements for enum columns (for down migration). */\nfunction generateDropEnumTypes(tables: Record<string, TableDef>): string[] {\n const enums = new Set<string>();\n for (const tableDef of Object.values(tables)) {\n for (const col of Object.values(tableDef.columns)) {\n if (col._def.type === \"enum\" && col._def.enumName) {\n enums.add(col._def.enumName);\n }\n }\n }\n return Array.from(enums).map((name) => `DROP TYPE IF EXISTS ${name};`);\n}\n\n/** Result of migration generation. */\nexport interface MigrationResult {\n up: string;\n down: string;\n}\n\n/**\n * Generates a full migration (up + down) from a schema definition.\n * Used for initial schema creation.\n */\nexport function generateMigration(schema: SchemaDef): MigrationResult {\n const upParts: string[] = [];\n const downParts: string[] = [];\n\n // Enum types first (up) / last (down)\n const enumTypes = generateEnumTypes(schema.tables);\n upParts.push(...enumTypes);\n\n // Tables\n for (const tableDef of Object.values(schema.tables)) {\n upParts.push(generateCreateTable(tableDef));\n }\n\n // Down: reverse order — drop tables first, then types\n const tableNames = Object.values(schema.tables);\n for (let i = tableNames.length - 1; i >= 0; i--) {\n downParts.push(generateDropTable(tableNames[i]!));\n }\n downParts.push(...generateDropEnumTypes(schema.tables));\n\n return {\n up: upParts.join(\"\\n\\n\"),\n down: downParts.join(\"\\n\\n\"),\n };\n}\n\n/** Column diff operation. */\ninterface ColumnDiff {\n type: \"add\" | \"drop\" | \"alter\";\n table: string;\n column: string;\n def?: ColumnDef;\n oldDef?: ColumnDef;\n}\n\n/** Computes column-level diffs between two schemas. */\nfunction diffSchemas(\n oldSchema: SchemaDef,\n newSchema: SchemaDef,\n): { addedTables: TableDef[]; droppedTables: TableDef[]; columnDiffs: ColumnDiff[] } {\n const addedTables: TableDef[] = [];\n const droppedTables: TableDef[] = [];\n const columnDiffs: ColumnDiff[] = [];\n\n const oldTableNames = new Set(Object.keys(oldSchema.tables));\n const newTableNames = new Set(Object.keys(newSchema.tables));\n\n // New tables\n for (const name of newTableNames) {\n if (!oldTableNames.has(name)) {\n addedTables.push(newSchema.tables[name]!);\n }\n }\n\n // Dropped tables\n for (const name of oldTableNames) {\n if (!newTableNames.has(name)) {\n droppedTables.push(oldSchema.tables[name]!);\n }\n }\n\n // Column-level diffs for existing tables\n for (const name of newTableNames) {\n if (!oldTableNames.has(name)) continue;\n\n const oldTable = oldSchema.tables[name]!;\n const newTable = newSchema.tables[name]!;\n const oldCols = new Set(Object.keys(oldTable.columns));\n const newCols = new Set(Object.keys(newTable.columns));\n\n for (const col of newCols) {\n if (!oldCols.has(col)) {\n columnDiffs.push({\n type: \"add\",\n table: newTable.name,\n column: col,\n def: newTable.columns[col]!._def,\n });\n }\n }\n\n for (const col of oldCols) {\n if (!newCols.has(col)) {\n columnDiffs.push({\n type: \"drop\",\n table: oldTable.name,\n column: col,\n oldDef: oldTable.columns[col]!._def,\n });\n }\n }\n }\n\n return { addedTables, droppedTables, columnDiffs };\n}\n\n/** Generates an ALTER TABLE ADD COLUMN statement. */\nfunction generateAddColumn(table: string, column: string, col: ColumnDef): string {\n let sql = `ALTER TABLE ${table} ADD COLUMN ${column} ${columnTypeToSQL(col)}`;\n if (!col.nullable) {\n sql += \" NOT NULL\";\n }\n sql += defaultClause(col);\n if (col.references) {\n sql += ` REFERENCES ${col.references.table}(${col.references.column})`;\n if (col.onDeleteAction) {\n sql += ` ON DELETE ${onDeleteSQL(col.onDeleteAction)}`;\n }\n }\n return `${sql};`;\n}\n\n/** Generates an ALTER TABLE DROP COLUMN statement. */\nfunction generateDropColumn(table: string, column: string): string {\n return `ALTER TABLE ${table} DROP COLUMN ${column};`;\n}\n\n/**\n * Generates a diff migration between two schemas.\n * Returns null if there are no changes.\n */\nexport function generateDiffMigration(\n oldSchema: SchemaDef,\n newSchema: SchemaDef,\n): MigrationResult | null {\n const { addedTables, droppedTables, columnDiffs } = diffSchemas(oldSchema, newSchema);\n\n if (addedTables.length === 0 && droppedTables.length === 0 && columnDiffs.length === 0) {\n return null;\n }\n\n const upParts: string[] = [];\n const downParts: string[] = [];\n\n // New enum types from added tables\n const newEnums = generateEnumTypes(\n Object.fromEntries(addedTables.map((t) => [t.name, t])),\n );\n upParts.push(...newEnums);\n\n // New tables\n for (const table of addedTables) {\n upParts.push(generateCreateTable(table));\n downParts.push(generateDropTable(table));\n }\n\n // Dropped tables\n for (const table of droppedTables) {\n upParts.push(generateDropTable(table));\n downParts.push(generateCreateTable(table));\n }\n\n // Column diffs\n for (const diff of columnDiffs) {\n if (diff.type === \"add\" && diff.def) {\n upParts.push(generateAddColumn(diff.table, diff.column, diff.def));\n downParts.push(generateDropColumn(diff.table, diff.column));\n } else if (diff.type === \"drop\") {\n upParts.push(generateDropColumn(diff.table, diff.column));\n if (diff.oldDef) {\n downParts.push(generateAddColumn(diff.table, diff.column, diff.oldDef));\n }\n }\n }\n\n // Drop enum types from dropped tables (down)\n const droppedEnums = generateDropEnumTypes(\n Object.fromEntries(droppedTables.map((t) => [t.name, t])),\n );\n downParts.push(...droppedEnums);\n\n return {\n up: upParts.join(\"\\n\\n\"),\n down: downParts.join(\"\\n\\n\"),\n };\n}\n"],"mappings":"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","names":["table"]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ZodSchema, z } from 'zod';
|
|
2
|
-
import { S as SchemaDef } from './schema-BqfEhIC0.js';
|
|
3
2
|
|
|
4
3
|
/** Supported HTTP methods for endpoints. */
|
|
5
4
|
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
6
5
|
/** Authenticated user attached to the request context. */
|
|
7
6
|
interface User {
|
|
8
7
|
id: string;
|
|
9
|
-
email
|
|
8
|
+
/** User's email, if they signed up with one (absent for phone-only users). */
|
|
9
|
+
email?: string;
|
|
10
10
|
role: string;
|
|
11
11
|
metadata: Record<string, unknown>;
|
|
12
12
|
}
|
|
@@ -49,20 +49,17 @@ declare function defineMiddleware(fn: MiddlewareHandler): MiddlewareHandler;
|
|
|
49
49
|
|
|
50
50
|
/** HTTP error with structured error response format.
|
|
51
51
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* `EndpointConfig.errors`. The endpoint's OpenAPI spec describes each
|
|
59
|
-
* one, and the CLI codegen emits a typed `<Endpoint>.Error` enum so iOS
|
|
60
|
-
* callers can `catch <Endpoint>.Error.<case>` directly.
|
|
52
|
+
* The base class for the throwable error classes (`PalError`, `Conflict`,
|
|
53
|
+
* `NotFound`, …). Construct one directly with `throw new HttpError(404,
|
|
54
|
+
* "todo_not_found", "No such todo")`, or throw a named subclass
|
|
55
|
+
* (`throw new NotFound("todo not found")`). The runtime catches any `HttpError`
|
|
56
|
+
* and emits the standard envelope; on the wire (and to iOS) it surfaces as
|
|
57
|
+
* `BackendError.server(code, status, message, requestId)`.
|
|
61
58
|
*
|
|
62
59
|
* The optional `data` field carries a structured payload alongside the
|
|
63
|
-
* standard envelope —
|
|
64
|
-
* (e.g. `
|
|
65
|
-
* enum's associated value.
|
|
60
|
+
* standard envelope — for errors that need to ship extra context
|
|
61
|
+
* (e.g. `new Conflict("locked", "title_locked", { retryAfter: 30 })`). It rides
|
|
62
|
+
* through to the iOS typed enum's associated value.
|
|
66
63
|
*/
|
|
67
64
|
declare class HttpError extends Error {
|
|
68
65
|
readonly status: number;
|
|
@@ -84,6 +81,46 @@ declare class HttpError extends Error {
|
|
|
84
81
|
data?: unknown;
|
|
85
82
|
};
|
|
86
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Throw with a custom HTTP status + wire code. The general-purpose escape hatch
|
|
86
|
+
* when none of the named classes (`Conflict`/`NotFound`/…) fits.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* throw new PalError(418, "teapot", "I'm a teapot");
|
|
90
|
+
*/
|
|
91
|
+
declare class PalError extends HttpError {
|
|
92
|
+
constructor(status: number, code: string, description: string, data?: unknown);
|
|
93
|
+
}
|
|
94
|
+
/** Base for the named status classes. Each subclass fixes its HTTP status; the
|
|
95
|
+
* `code` defaults to the class's canonical wire code (overridable), and the
|
|
96
|
+
* `message` defaults to a human-readable label (overridable). */
|
|
97
|
+
declare abstract class NamedHttpError extends HttpError {
|
|
98
|
+
protected constructor(status: number, defaultCode: string, name: string, message?: string, code?: string, data?: unknown);
|
|
99
|
+
}
|
|
100
|
+
/** 400 — the request was malformed or failed validation. */
|
|
101
|
+
declare class BadRequest extends NamedHttpError {
|
|
102
|
+
constructor(message?: string, code?: string, data?: unknown);
|
|
103
|
+
}
|
|
104
|
+
/** 401 — the caller is not authenticated. */
|
|
105
|
+
declare class Unauthorized extends NamedHttpError {
|
|
106
|
+
constructor(message?: string, code?: string, data?: unknown);
|
|
107
|
+
}
|
|
108
|
+
/** 403 — the caller is authenticated but not allowed. */
|
|
109
|
+
declare class Forbidden extends NamedHttpError {
|
|
110
|
+
constructor(message?: string, code?: string, data?: unknown);
|
|
111
|
+
}
|
|
112
|
+
/** 404 — the requested resource does not exist. */
|
|
113
|
+
declare class NotFound extends NamedHttpError {
|
|
114
|
+
constructor(message?: string, code?: string, data?: unknown);
|
|
115
|
+
}
|
|
116
|
+
/** 409 — the request conflicts with the current state. */
|
|
117
|
+
declare class Conflict extends NamedHttpError {
|
|
118
|
+
constructor(message?: string, code?: string, data?: unknown);
|
|
119
|
+
}
|
|
120
|
+
/** 429 — the caller has exceeded the rate limit. */
|
|
121
|
+
declare class TooManyRequests extends NamedHttpError {
|
|
122
|
+
constructor(message?: string, code?: string, data?: unknown);
|
|
123
|
+
}
|
|
87
124
|
|
|
88
125
|
/**
|
|
89
126
|
* Local typed interfaces for the 9 Palbase module clients injected into
|
|
@@ -1161,10 +1198,9 @@ interface RateLimitConfig {
|
|
|
1161
1198
|
/** Window duration in seconds. */
|
|
1162
1199
|
window: number;
|
|
1163
1200
|
}
|
|
1164
|
-
/** The
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
interface DBClient {
|
|
1201
|
+
/** The six raw string-keyed DB operations shared by `DBClient` and the
|
|
1202
|
+
* transaction-scoped client. */
|
|
1203
|
+
interface DBOps {
|
|
1168
1204
|
/** Run a read-only SQL query (executes in a READ ONLY transaction). */
|
|
1169
1205
|
query(sql: string, params?: unknown[]): Promise<Record<string, unknown>[]>;
|
|
1170
1206
|
insert(table: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
@@ -1172,12 +1208,27 @@ interface DBClient {
|
|
|
1172
1208
|
delete(table: string, id: string): Promise<void>;
|
|
1173
1209
|
findById(table: string, id: string): Promise<Record<string, unknown> | null>;
|
|
1174
1210
|
findMany(table: string, query?: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
1211
|
+
}
|
|
1212
|
+
/** The transaction-scoped client passed to `db.transaction(fn)` — the raw DB
|
|
1213
|
+
* ops only. No nested transaction and no `asService` (the DB role is fixed once
|
|
1214
|
+
* when the transaction begins; see `Database.asService().transaction(...)`). */
|
|
1215
|
+
type TxClient = DBOps;
|
|
1216
|
+
/** Database client interface injected into endpoint context. */
|
|
1217
|
+
interface DBClient extends DBOps {
|
|
1175
1218
|
/**
|
|
1176
1219
|
* Run an interactive transaction. The callback receives a `tx` with the same
|
|
1177
1220
|
* DB ops as this client; returning normally commits, throwing rolls back.
|
|
1178
1221
|
* Nested transactions are not supported.
|
|
1179
1222
|
*/
|
|
1180
1223
|
transaction<T>(fn: (tx: TxClient) => Promise<T>): Promise<T>;
|
|
1224
|
+
/**
|
|
1225
|
+
* Return a sibling DB client that bypasses Row-Level Security by running as
|
|
1226
|
+
* the `service_role` (BYPASSRLS). Use sparingly and explicitly — the default
|
|
1227
|
+
* `Database.*` path is RLS-enforced. The returned client exposes the same op
|
|
1228
|
+
* surface (`query`/`insert`/.../`transaction`) but never re-exposes
|
|
1229
|
+
* `asService` (no double-bypass).
|
|
1230
|
+
*/
|
|
1231
|
+
asService(): Omit<DBClient, "asService">;
|
|
1181
1232
|
}
|
|
1182
1233
|
/** Logger interface injected into endpoint context. */
|
|
1183
1234
|
interface Logger {
|
|
@@ -1349,22 +1400,29 @@ type ErrorThrowers<TErrors extends ErrorMap | undefined> = TErrors extends Error
|
|
|
1349
1400
|
* error throwers. Services (`Database`, `Documents`, `Cache`, …) are NOT on
|
|
1350
1401
|
* the request: import them directly from `@palbase/backend` as singletons.
|
|
1351
1402
|
*
|
|
1352
|
-
* import {
|
|
1403
|
+
* import { Controller, Get, Req, Database } from "@palbase/backend";
|
|
1404
|
+
*
|
|
1405
|
+
* \@Controller("/todos")
|
|
1406
|
+
* export class TodosController {
|
|
1407
|
+
* \@Get("") list(\@Req() req: PBRequest): unknown {
|
|
1408
|
+
* return Database.findMany("todos");
|
|
1409
|
+
* }
|
|
1410
|
+
* }
|
|
1353
1411
|
*
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1356
|
-
*
|
|
1357
|
-
* });
|
|
1412
|
+
* Most controller methods reach individual request slices via their own
|
|
1413
|
+
* parameter decorator (`@Body`/`@Query`/`@Param`/`@User`/…); `@Req()` is the
|
|
1414
|
+
* escape hatch that injects this whole object.
|
|
1358
1415
|
*
|
|
1359
1416
|
* Generic parameters:
|
|
1360
|
-
* - `TInput` — the validated `input` type (
|
|
1361
|
-
*
|
|
1362
|
-
* `PBRequest<TodoInput>`.
|
|
1417
|
+
* - `TInput` — the validated `input` type (the `@Body` schema's `z.infer`). The
|
|
1418
|
+
* user-facing form is single-generic: `PBRequest<TodoInput>`.
|
|
1363
1419
|
* - `TAuthed` — whether `user` is non-null. DEFAULTS to `true` (the common
|
|
1364
1420
|
* case; the auth pipeline returns 401 before the handler when auth is
|
|
1365
|
-
* required, so a non-null `user` is runtime-honest).
|
|
1366
|
-
*
|
|
1367
|
-
* - `TErrors` — the
|
|
1421
|
+
* required, so a non-null `user` is runtime-honest). A route whose effective
|
|
1422
|
+
* auth is `false` yields `User | null`.
|
|
1423
|
+
* - `TErrors` — RETAINED for back-compat of the `errors` thrower shape; the
|
|
1424
|
+
* class-controller model throws global error classes
|
|
1425
|
+
* (`Conflict`/`NotFound`/…) instead, so `req.errors` is empty in practice.
|
|
1368
1426
|
*/
|
|
1369
1427
|
interface PBRequest<TInput = unknown, TAuthed extends boolean = true, TErrors extends ErrorMap | undefined = undefined> {
|
|
1370
1428
|
/** Validated request input (body for POST/PUT/PATCH; `{}` otherwise). */
|
|
@@ -1376,8 +1434,9 @@ interface PBRequest<TInput = unknown, TAuthed extends boolean = true, TErrors ex
|
|
|
1376
1434
|
/** Request headers (lowercase keys). */
|
|
1377
1435
|
headers: Record<string, string>;
|
|
1378
1436
|
/** Authenticated user. Non-null (`User`) by default; `User | null` only when
|
|
1379
|
-
* the
|
|
1380
|
-
*
|
|
1437
|
+
* the route's effective auth disables enforcement (driven by `TAuthed`, which
|
|
1438
|
+
* the runtime resolves from the route/controller `auth` cascade via
|
|
1439
|
+
* {@link IsAuthed}). */
|
|
1381
1440
|
user: TAuthed extends true ? User : User | null;
|
|
1382
1441
|
/** Calling-client metadata derived from request headers (all nullable). */
|
|
1383
1442
|
client: ClientInfo;
|
|
@@ -1391,8 +1450,9 @@ interface PBRequest<TInput = unknown, TAuthed extends boolean = true, TErrors ex
|
|
|
1391
1450
|
traceId: string;
|
|
1392
1451
|
/** W3C span id for this handler invocation. */
|
|
1393
1452
|
spanId: string;
|
|
1394
|
-
/** Typed throwers for the endpoint's declared errors.
|
|
1395
|
-
* `
|
|
1453
|
+
/** Typed throwers for the endpoint's declared errors. RETAINED for the
|
|
1454
|
+
* `@Req()` escape-hatch shape; the class-controller model throws global error
|
|
1455
|
+
* classes (`Conflict`/`NotFound`/…) instead, so this is empty in practice. */
|
|
1396
1456
|
errors: ErrorThrowers<TErrors>;
|
|
1397
1457
|
}
|
|
1398
1458
|
/** Middleware function signature — uses MiddlewareContext (no input, not yet validated). */
|
|
@@ -1404,67 +1464,5 @@ type Middleware = (ctx: MiddlewareContext, next: () => Promise<void>) => Promise
|
|
|
1404
1464
|
* omitted — which the runtime treats as `required: true` (see {@link IsAuthed}).
|
|
1405
1465
|
*/
|
|
1406
1466
|
type AuthSpec = boolean | Partial<AuthConfig>;
|
|
1407
|
-
/** Compute, at the type level, whether an endpoint authenticates its caller —
|
|
1408
|
-
* i.e. whether `req.user` should be `User` (non-null) instead of `User | null`.
|
|
1409
|
-
*
|
|
1410
|
-
* This mirrors the Go pipeline's enforcement exactly (extract_meta.js + auth.go):
|
|
1411
|
-
* a request is authenticated ⟺ `auth === true` OR (`auth` is an object whose
|
|
1412
|
-
* `required` is not explicitly `false`). So `req.user` stays nullable ONLY when
|
|
1413
|
-
* `auth` is absent (`undefined`), `auth === false`, or `auth: { required: false }`.
|
|
1414
|
-
*
|
|
1415
|
-
* | `TAuth` | `IsAuthed<TAuth>` |
|
|
1416
|
-
* |---------------------------------|-------------------|
|
|
1417
|
-
* | `undefined` | `false` |
|
|
1418
|
-
* | `true` | `true` |
|
|
1419
|
-
* | `false` | `false` |
|
|
1420
|
-
* | `{ required: true }` | `true` |
|
|
1421
|
-
* | `{ required: false }` | `false` |
|
|
1422
|
-
* | `{ role: 'admin' }` (no `required`) | `true` ⚠️ |
|
|
1423
|
-
*
|
|
1424
|
-
* Order matters: the `{ required: false }` branch is checked first so the
|
|
1425
|
-
* object case can't swallow it; `true` then `false` literals are handled
|
|
1426
|
-
* explicitly before the catch-all object branch (which encodes the
|
|
1427
|
-
* "required omitted ⇒ required" corner case).
|
|
1428
|
-
*/
|
|
1429
|
-
type IsAuthed<TAuth> = TAuth extends {
|
|
1430
|
-
required: false;
|
|
1431
|
-
} ? false : TAuth extends true ? true : TAuth extends false ? false : TAuth extends object ? true : false;
|
|
1432
|
-
/** Configuration for defining an endpoint.
|
|
1433
|
-
*
|
|
1434
|
-
* `TSchema` — optional `SchemaDef` carried on the `schema` field. The runtime
|
|
1435
|
-
* uses it to wire a typed `.tables.*` API; authors opt in per endpoint with
|
|
1436
|
-
* `typedDatabase(schema)` (see runtime.ts).
|
|
1437
|
-
*
|
|
1438
|
-
* `TAuth` — captures the literal type of the `auth` field (via the `const`
|
|
1439
|
-
* type parameter on `defineEndpoint`) so the handler's `req.user` can be
|
|
1440
|
-
* narrowed to non-null `User` when auth is required. See {@link IsAuthed}.
|
|
1441
|
-
*
|
|
1442
|
-
* `TErrors` — captures the literal `errors` map (via the `const` type
|
|
1443
|
-
* parameter on `defineEndpoint`) so `req.errors.<name>(...)` is typed per
|
|
1444
|
-
* declared entry. The OpenAPI generator emits each one as a discriminated
|
|
1445
|
-
* response under `responses[<status>]`, and the CLI codegen lowers them to
|
|
1446
|
-
* a typed `<Endpoint>.Error` enum on iOS.
|
|
1447
|
-
*/
|
|
1448
|
-
interface EndpointConfig<TInputSchema extends ZodSchema = ZodSchema, TOutputSchema extends ZodSchema = ZodSchema, TSchema extends SchemaDef | undefined = undefined, TAuth extends AuthSpec | undefined = undefined, TErrors extends ErrorMap | undefined = undefined> {
|
|
1449
|
-
method: HttpMethod;
|
|
1450
|
-
auth?: TAuth;
|
|
1451
|
-
rateLimit?: RateLimitConfig;
|
|
1452
|
-
input?: TInputSchema;
|
|
1453
|
-
output?: TOutputSchema;
|
|
1454
|
-
schema?: TSchema;
|
|
1455
|
-
errors?: TErrors;
|
|
1456
|
-
middleware?: Middleware[];
|
|
1457
|
-
handler: (req: PBRequest<z.infer<TInputSchema>, IsAuthed<TAuth>, TErrors>) => Promise<z.infer<TOutputSchema>>;
|
|
1458
|
-
}
|
|
1459
|
-
/** Define a type-safe endpoint. The input schema infers the `req.input` type.
|
|
1460
|
-
* Pass a `schema` (from `defineSchema`) and wrap `Database` with
|
|
1461
|
-
* `typedDatabase(schema)` in the handler for a typed `.tables.*` API.
|
|
1462
|
-
*
|
|
1463
|
-
* The `const TAuth` and `const TErrors` type parameters capture the `auth`
|
|
1464
|
-
* and `errors` fields as literals, so `req.user` narrows to non-null `User`
|
|
1465
|
-
* when auth is enforced and `req.errors.<name>(...)` is typed per declared
|
|
1466
|
-
* entry.
|
|
1467
|
-
*/
|
|
1468
|
-
declare function defineEndpoint<TInputSchema extends ZodSchema, TOutputSchema extends ZodSchema, TSchema extends SchemaDef | undefined = undefined, const TAuth extends AuthSpec | undefined = undefined, const TErrors extends ErrorMap | undefined = undefined>(config: EndpointConfig<TInputSchema, TOutputSchema, TSchema, TAuth, TErrors>): EndpointConfig<TInputSchema, TOutputSchema, TSchema, TAuth, TErrors>;
|
|
1469
1467
|
|
|
1470
|
-
export { type
|
|
1468
|
+
export { type PalbaseDocumentSnapshot as $, type AuthSpec as A, BadRequest as B, type CacheClient as C, type DBClient as D, type ErrorDef as E, type FileContext as F, type PalbaseBucketClient as G, HttpError as H, type PalbaseCmsClient as I, type PalbaseCmsFindOneOptions as J, type PalbaseCmsFindOptions as K, type Logger as L, type Middleware as M, NotFound as N, type PalbaseCohortQueryInput as O, type PBRequest as P, type QueueClient as Q, type RateLimitConfig as R, type PalbaseCohortResult as S, type PalbaseCollectionRef as T, type User as U, type PalbaseCountQueryInput as V, type PalbaseCountResult as W, type PalbaseCreateLinkParams as X, type PalbaseDeviceInfo as Y, type PalbaseDeviceTokenView as Z, type PalbaseDocumentRef as _, type PalbaseModuleClients as a, Unauthorized as a$, type PalbaseEmailClient as a0, type PalbaseEmailSendParams as a1, type PalbaseEmailSendResponse as a2, type PalbaseEventNamesResult as a3, type PalbaseEventsQueryInput as a4, type PalbaseEventsResult as a5, type PalbaseFileObject as a6, type PalbaseFlag as a7, type PalbaseFlagContext as a8, type PalbaseFlagVariant as a9, type PalbasePushSendParams as aA, type PalbasePushSendResponse as aB, type PalbaseQrCodeOptions as aC, type PalbaseQuerySnapshot as aD, type PalbaseRealtimeChannel as aE, type PalbaseRealtimeClient as aF, type PalbaseRealtimeMessage as aG, type PalbaseRegisterDeviceParams as aH, type PalbaseResult as aI, type PalbaseRetentionQueryInput as aJ, type PalbaseRetentionResult as aK, type PalbaseSession as aL, type PalbaseSignedUrlResponse as aM, type PalbaseSmsClient as aN, type PalbaseSmsSendParams as aO, type PalbaseSmsSendResponse as aP, type PalbaseTransformOptions as aQ, type PalbaseUpdateLinkParams as aR, type PalbaseUploadOptions as aS, type PalbaseUser as aT, type PalbaseUserDetailResult as aU, type PalbaseUsersQueryInput as aV, type PalbaseUsersResult as aW, type PalbaseVerifyRequestSignatureParams as aX, type PalbaseWhereOperator as aY, TooManyRequests as aZ, type TxClient as a_, type PalbaseFunctionsClient as aa, type PalbaseFunnelQueryInput as ab, type PalbaseFunnelResult as ac, type PalbaseIdentifyTraits as ad, type PalbaseInboxClient as ae, type PalbaseInboxListOptions as af, type PalbaseInboxListResult as ag, type PalbaseInboxMessage as ah, type PalbaseInboxSendParams as ai, type PalbaseInboxSendResponse as aj, type PalbaseInitialLink as ak, type PalbaseInvokeOptions as al, type PalbaseLink as am, type PalbaseLinkAnalytics as an, type PalbaseLinkDetails as ao, type PalbaseLinksClient as ap, type PalbaseListLinksOptions as aq, type PalbaseListLinksResult as ar, type PalbaseListOptions as as, type PalbaseMatchParams as at, type PalbaseMultiChannelResponse as au, type PalbaseOverviewResult as av, type PalbasePreferences as aw, type PalbasePreferencesClient as ax, type PalbasePublicUrlResponse as ay, type PalbasePushClient as az, type PalbaseDocsClient as b, defineMiddleware as b0, type PalbaseFlagsClient as c, type PalbaseNotificationsClient as d, type PalbaseStorageClient as e, type AuthConfig as f, type ClientInfo as g, Conflict as h, type DBOps as i, type ErrorMap as j, type ErrorThrowers as k, Forbidden as l, type HttpMethod as m, type MiddlewareContext as n, type MiddlewareHandler as o, PalError as p, type PalbaseAnalyticsClient as q, type PalbaseAnalyticsManagementNamespace as r, type PalbaseAnalyticsProperties as s, type PalbaseAnalyticsQueryNamespace as t, type PalbaseAttestAndroidParams as u, type PalbaseAttestAndroidResult as v, type PalbaseAttestiOSParams as w, type PalbaseAttestiOSResult as x, type PalbaseAuthClient as y, type PalbaseBindDeviceParams as z };
|