@base44-preview/cli 0.0.15-pr.100.7c491cf → 0.0.15-pr.102.ec2ad29
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 +93 -1
- package/dist/cli/index.js +467 -451
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,6 +70,93 @@ base44 deploy
|
|
|
70
70
|
|---------|-------------|
|
|
71
71
|
| `base44 site deploy` | Deploy built site files to Base44 hosting |
|
|
72
72
|
|
|
73
|
+
### Types
|
|
74
|
+
|
|
75
|
+
| Command | Description |
|
|
76
|
+
|---------|-------------|
|
|
77
|
+
| `base44 types` | Generate TypeScript types from entity schemas |
|
|
78
|
+
|
|
79
|
+
**Options:**
|
|
80
|
+
- `-o, --output <dir>` - Output directory (default: `src/base44`)
|
|
81
|
+
- `--entities-only` - Only generate entity types, skip client types
|
|
82
|
+
|
|
83
|
+
## TypeScript Type Generation
|
|
84
|
+
|
|
85
|
+
Generate fully-typed interfaces from your entity schemas for type-safe SDK usage.
|
|
86
|
+
|
|
87
|
+
### Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Generate types (outputs to src/base44/)
|
|
91
|
+
base44 types
|
|
92
|
+
|
|
93
|
+
# Custom output directory
|
|
94
|
+
base44 types --output ./types
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Generated Files
|
|
98
|
+
|
|
99
|
+
| File | Contents |
|
|
100
|
+
|------|----------|
|
|
101
|
+
| `entities.ts` | Entity interfaces, CreateInput, UpdateInput, Filter types |
|
|
102
|
+
| `client.ts` | Typed SDK client interface |
|
|
103
|
+
| `index.ts` | Barrel exports |
|
|
104
|
+
|
|
105
|
+
### Setup with @base44/sdk
|
|
106
|
+
|
|
107
|
+
1. Generate types:
|
|
108
|
+
```bash
|
|
109
|
+
base44 types
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
2. Add to `tsconfig.json`:
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"include": ["src", "src/base44/entities.ts"]
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
3. Use in your code:
|
|
120
|
+
```typescript
|
|
121
|
+
import { createClient } from '@base44/sdk';
|
|
122
|
+
import type { TypedBase44Client } from './base44/client';
|
|
123
|
+
|
|
124
|
+
const base44 = createClient({ appId: 'my-app' }) as TypedBase44Client;
|
|
125
|
+
|
|
126
|
+
// Fully typed!
|
|
127
|
+
const { items: tasks } = await base44.entities.Task.list();
|
|
128
|
+
await base44.entities.Task.create({ title: 'Buy milk' });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Example
|
|
132
|
+
|
|
133
|
+
Given an entity schema:
|
|
134
|
+
```jsonc
|
|
135
|
+
// base44/entities/task.jsonc
|
|
136
|
+
{
|
|
137
|
+
"name": "Task",
|
|
138
|
+
"type": "object",
|
|
139
|
+
"properties": {
|
|
140
|
+
"title": { "type": "string", "description": "Task title" },
|
|
141
|
+
"completed": { "type": "boolean", "default": false }
|
|
142
|
+
},
|
|
143
|
+
"required": ["title"]
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Generated types:
|
|
148
|
+
```typescript
|
|
149
|
+
export interface Task extends BaseEntity {
|
|
150
|
+
title: string;
|
|
151
|
+
completed?: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface TaskCreateInput {
|
|
155
|
+
title: string;
|
|
156
|
+
completed?: boolean;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
73
160
|
## Configuration
|
|
74
161
|
|
|
75
162
|
### Project Configuration
|
|
@@ -115,7 +202,12 @@ my-project/
|
|
|
115
202
|
│ └── my-function/
|
|
116
203
|
│ ├── config.jsonc
|
|
117
204
|
│ └── index.js
|
|
118
|
-
├── src/
|
|
205
|
+
├── src/
|
|
206
|
+
│ ├── base44/ # Generated types (from `base44 types`)
|
|
207
|
+
│ │ ├── entities.ts
|
|
208
|
+
│ │ ├── client.ts
|
|
209
|
+
│ │ └── index.ts
|
|
210
|
+
│ └── ... # Your frontend code
|
|
119
211
|
├── dist/ # Built site files (for deployment)
|
|
120
212
|
└── package.json
|
|
121
213
|
```
|
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { EventEmitter, addAbortListener, on, once, setMaxListeners } from "node:events";
|
|
4
4
|
import childProcess, { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
5
|
-
import path, { basename, dirname, join, posix, resolve, win32 } from "node:path";
|
|
5
|
+
import path, { basename, dirname, join, posix, relative, resolve, win32 } from "node:path";
|
|
6
6
|
import fs, { appendFileSync, createReadStream, createWriteStream, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import y, { execArgv, execPath, hrtime, platform, stdin, stdout } from "node:process";
|
|
8
8
|
import { aborted, callbackify, debuglog, inspect, promisify, stripVTControlCharacters } from "node:util";
|
|
@@ -4547,7 +4547,6 @@ const string$1 = (params) => {
|
|
|
4547
4547
|
};
|
|
4548
4548
|
const integer = /^-?\d+$/;
|
|
4549
4549
|
const number$1 = /^-?\d+(?:\.\d+)?$/;
|
|
4550
|
-
const boolean$1 = /^(?:true|false)$/i;
|
|
4551
4550
|
const lowercase = /^[^A-Z]*$/;
|
|
4552
4551
|
const uppercase = /^[^a-z]*$/;
|
|
4553
4552
|
|
|
@@ -5326,24 +5325,6 @@ const $ZodNumberFormat = /* @__PURE__ */ $constructor("$ZodNumberFormat", (inst,
|
|
|
5326
5325
|
$ZodCheckNumberFormat.init(inst, def);
|
|
5327
5326
|
$ZodNumber.init(inst, def);
|
|
5328
5327
|
});
|
|
5329
|
-
const $ZodBoolean = /* @__PURE__ */ $constructor("$ZodBoolean", (inst, def) => {
|
|
5330
|
-
$ZodType.init(inst, def);
|
|
5331
|
-
inst._zod.pattern = boolean$1;
|
|
5332
|
-
inst._zod.parse = (payload, _ctx) => {
|
|
5333
|
-
if (def.coerce) try {
|
|
5334
|
-
payload.value = Boolean(payload.value);
|
|
5335
|
-
} catch (_$2) {}
|
|
5336
|
-
const input = payload.value;
|
|
5337
|
-
if (typeof input === "boolean") return payload;
|
|
5338
|
-
payload.issues.push({
|
|
5339
|
-
expected: "boolean",
|
|
5340
|
-
code: "invalid_type",
|
|
5341
|
-
input,
|
|
5342
|
-
inst
|
|
5343
|
-
});
|
|
5344
|
-
return payload;
|
|
5345
|
-
};
|
|
5346
|
-
});
|
|
5347
5328
|
const $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
|
|
5348
5329
|
$ZodType.init(inst, def);
|
|
5349
5330
|
inst._zod.parse = (payload) => payload;
|
|
@@ -5819,6 +5800,97 @@ function handleTupleResult(result, final, index) {
|
|
|
5819
5800
|
if (result.issues.length) final.issues.push(...prefixIssues(index, result.issues));
|
|
5820
5801
|
final.value[index] = result.value;
|
|
5821
5802
|
}
|
|
5803
|
+
const $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
5804
|
+
$ZodType.init(inst, def);
|
|
5805
|
+
inst._zod.parse = (payload, ctx) => {
|
|
5806
|
+
const input = payload.value;
|
|
5807
|
+
if (!isPlainObject$1(input)) {
|
|
5808
|
+
payload.issues.push({
|
|
5809
|
+
expected: "record",
|
|
5810
|
+
code: "invalid_type",
|
|
5811
|
+
input,
|
|
5812
|
+
inst
|
|
5813
|
+
});
|
|
5814
|
+
return payload;
|
|
5815
|
+
}
|
|
5816
|
+
const proms = [];
|
|
5817
|
+
const values = def.keyType._zod.values;
|
|
5818
|
+
if (values) {
|
|
5819
|
+
payload.value = {};
|
|
5820
|
+
const recordKeys = /* @__PURE__ */ new Set();
|
|
5821
|
+
for (const key of values) if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
|
|
5822
|
+
recordKeys.add(typeof key === "number" ? key.toString() : key);
|
|
5823
|
+
const result = def.valueType._zod.run({
|
|
5824
|
+
value: input[key],
|
|
5825
|
+
issues: []
|
|
5826
|
+
}, ctx);
|
|
5827
|
+
if (result instanceof Promise) proms.push(result.then((result$1) => {
|
|
5828
|
+
if (result$1.issues.length) payload.issues.push(...prefixIssues(key, result$1.issues));
|
|
5829
|
+
payload.value[key] = result$1.value;
|
|
5830
|
+
}));
|
|
5831
|
+
else {
|
|
5832
|
+
if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
|
|
5833
|
+
payload.value[key] = result.value;
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
let unrecognized;
|
|
5837
|
+
for (const key in input) if (!recordKeys.has(key)) {
|
|
5838
|
+
unrecognized = unrecognized ?? [];
|
|
5839
|
+
unrecognized.push(key);
|
|
5840
|
+
}
|
|
5841
|
+
if (unrecognized && unrecognized.length > 0) payload.issues.push({
|
|
5842
|
+
code: "unrecognized_keys",
|
|
5843
|
+
input,
|
|
5844
|
+
inst,
|
|
5845
|
+
keys: unrecognized
|
|
5846
|
+
});
|
|
5847
|
+
} else {
|
|
5848
|
+
payload.value = {};
|
|
5849
|
+
for (const key of Reflect.ownKeys(input)) {
|
|
5850
|
+
if (key === "__proto__") continue;
|
|
5851
|
+
let keyResult = def.keyType._zod.run({
|
|
5852
|
+
value: key,
|
|
5853
|
+
issues: []
|
|
5854
|
+
}, ctx);
|
|
5855
|
+
if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
|
|
5856
|
+
if (typeof key === "string" && number$1.test(key) && keyResult.issues.length && keyResult.issues.some((iss) => iss.code === "invalid_type" && iss.expected === "number")) {
|
|
5857
|
+
const retryResult = def.keyType._zod.run({
|
|
5858
|
+
value: Number(key),
|
|
5859
|
+
issues: []
|
|
5860
|
+
}, ctx);
|
|
5861
|
+
if (retryResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
|
|
5862
|
+
if (retryResult.issues.length === 0) keyResult = retryResult;
|
|
5863
|
+
}
|
|
5864
|
+
if (keyResult.issues.length) {
|
|
5865
|
+
if (def.mode === "loose") payload.value[key] = input[key];
|
|
5866
|
+
else payload.issues.push({
|
|
5867
|
+
code: "invalid_key",
|
|
5868
|
+
origin: "record",
|
|
5869
|
+
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
|
|
5870
|
+
input: key,
|
|
5871
|
+
path: [key],
|
|
5872
|
+
inst
|
|
5873
|
+
});
|
|
5874
|
+
continue;
|
|
5875
|
+
}
|
|
5876
|
+
const result = def.valueType._zod.run({
|
|
5877
|
+
value: input[key],
|
|
5878
|
+
issues: []
|
|
5879
|
+
}, ctx);
|
|
5880
|
+
if (result instanceof Promise) proms.push(result.then((result$1) => {
|
|
5881
|
+
if (result$1.issues.length) payload.issues.push(...prefixIssues(key, result$1.issues));
|
|
5882
|
+
payload.value[keyResult.value] = result$1.value;
|
|
5883
|
+
}));
|
|
5884
|
+
else {
|
|
5885
|
+
if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
|
|
5886
|
+
payload.value[keyResult.value] = result.value;
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
if (proms.length) return Promise.all(proms).then(() => payload);
|
|
5891
|
+
return payload;
|
|
5892
|
+
};
|
|
5893
|
+
});
|
|
5822
5894
|
const $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
|
|
5823
5895
|
$ZodType.init(inst, def);
|
|
5824
5896
|
const values = getEnumValues(def.entries);
|
|
@@ -5837,6 +5909,24 @@ const $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => {
|
|
|
5837
5909
|
return payload;
|
|
5838
5910
|
};
|
|
5839
5911
|
});
|
|
5912
|
+
const $ZodLiteral = /* @__PURE__ */ $constructor("$ZodLiteral", (inst, def) => {
|
|
5913
|
+
$ZodType.init(inst, def);
|
|
5914
|
+
if (def.values.length === 0) throw new Error("Cannot create literal schema with no valid values");
|
|
5915
|
+
const values = new Set(def.values);
|
|
5916
|
+
inst._zod.values = values;
|
|
5917
|
+
inst._zod.pattern = /* @__PURE__ */ new RegExp(`^(${def.values.map((o$2) => typeof o$2 === "string" ? escapeRegex(o$2) : o$2 ? escapeRegex(o$2.toString()) : String(o$2)).join("|")})$`);
|
|
5918
|
+
inst._zod.parse = (payload, _ctx) => {
|
|
5919
|
+
const input = payload.value;
|
|
5920
|
+
if (values.has(input)) return payload;
|
|
5921
|
+
payload.issues.push({
|
|
5922
|
+
code: "invalid_value",
|
|
5923
|
+
values: def.values,
|
|
5924
|
+
input,
|
|
5925
|
+
inst
|
|
5926
|
+
});
|
|
5927
|
+
return payload;
|
|
5928
|
+
};
|
|
5929
|
+
});
|
|
5840
5930
|
const $ZodTransform = /* @__PURE__ */ $constructor("$ZodTransform", (inst, def) => {
|
|
5841
5931
|
$ZodType.init(inst, def);
|
|
5842
5932
|
inst._zod.parse = (payload, ctx) => {
|
|
@@ -6398,13 +6488,6 @@ function _int(Class, params) {
|
|
|
6398
6488
|
});
|
|
6399
6489
|
}
|
|
6400
6490
|
/* @__NO_SIDE_EFFECTS__ */
|
|
6401
|
-
function _boolean(Class, params) {
|
|
6402
|
-
return new Class({
|
|
6403
|
-
type: "boolean",
|
|
6404
|
-
...normalizeParams(params)
|
|
6405
|
-
});
|
|
6406
|
-
}
|
|
6407
|
-
/* @__NO_SIDE_EFFECTS__ */
|
|
6408
6491
|
function _unknown(Class) {
|
|
6409
6492
|
return new Class({ type: "unknown" });
|
|
6410
6493
|
}
|
|
@@ -6975,9 +7058,6 @@ const numberProcessor = (schema, ctx, _json, _params) => {
|
|
|
6975
7058
|
}
|
|
6976
7059
|
if (typeof multipleOf === "number") json.multipleOf = multipleOf;
|
|
6977
7060
|
};
|
|
6978
|
-
const booleanProcessor = (_schema, _ctx, json, _params) => {
|
|
6979
|
-
json.type = "boolean";
|
|
6980
|
-
};
|
|
6981
7061
|
const neverProcessor = (_schema, _ctx, json, _params) => {
|
|
6982
7062
|
json.not = {};
|
|
6983
7063
|
};
|
|
@@ -6989,6 +7069,27 @@ const enumProcessor = (schema, _ctx, json, _params) => {
|
|
|
6989
7069
|
if (values.every((v$1) => typeof v$1 === "string")) json.type = "string";
|
|
6990
7070
|
json.enum = values;
|
|
6991
7071
|
};
|
|
7072
|
+
const literalProcessor = (schema, ctx, json, _params) => {
|
|
7073
|
+
const def = schema._zod.def;
|
|
7074
|
+
const vals = [];
|
|
7075
|
+
for (const val of def.values) if (val === void 0) {
|
|
7076
|
+
if (ctx.unrepresentable === "throw") throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
|
7077
|
+
} else if (typeof val === "bigint") if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
|
|
7078
|
+
else vals.push(Number(val));
|
|
7079
|
+
else vals.push(val);
|
|
7080
|
+
if (vals.length === 0) {} else if (vals.length === 1) {
|
|
7081
|
+
const val = vals[0];
|
|
7082
|
+
json.type = val === null ? "null" : typeof val;
|
|
7083
|
+
if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") json.enum = [val];
|
|
7084
|
+
else json.const = val;
|
|
7085
|
+
} else {
|
|
7086
|
+
if (vals.every((v$1) => typeof v$1 === "number")) json.type = "number";
|
|
7087
|
+
if (vals.every((v$1) => typeof v$1 === "string")) json.type = "string";
|
|
7088
|
+
if (vals.every((v$1) => typeof v$1 === "boolean")) json.type = "boolean";
|
|
7089
|
+
if (vals.every((v$1) => v$1 === null)) json.type = "null";
|
|
7090
|
+
json.enum = vals;
|
|
7091
|
+
}
|
|
7092
|
+
};
|
|
6992
7093
|
const customProcessor = (_schema, ctx, _json, _params) => {
|
|
6993
7094
|
if (ctx.unrepresentable === "throw") throw new Error("Custom types cannot be represented in JSON Schema");
|
|
6994
7095
|
};
|
|
@@ -7109,6 +7210,39 @@ const tupleProcessor = (schema, ctx, _json, params) => {
|
|
|
7109
7210
|
if (typeof minimum === "number") json.minItems = minimum;
|
|
7110
7211
|
if (typeof maximum === "number") json.maxItems = maximum;
|
|
7111
7212
|
};
|
|
7213
|
+
const recordProcessor = (schema, ctx, _json, params) => {
|
|
7214
|
+
const json = _json;
|
|
7215
|
+
const def = schema._zod.def;
|
|
7216
|
+
json.type = "object";
|
|
7217
|
+
const keyType = def.keyType;
|
|
7218
|
+
const patterns = keyType._zod.bag?.patterns;
|
|
7219
|
+
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
|
7220
|
+
const valueSchema = process$2(def.valueType, ctx, {
|
|
7221
|
+
...params,
|
|
7222
|
+
path: [
|
|
7223
|
+
...params.path,
|
|
7224
|
+
"patternProperties",
|
|
7225
|
+
"*"
|
|
7226
|
+
]
|
|
7227
|
+
});
|
|
7228
|
+
json.patternProperties = {};
|
|
7229
|
+
for (const pattern of patterns) json.patternProperties[pattern.source] = valueSchema;
|
|
7230
|
+
} else {
|
|
7231
|
+
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") json.propertyNames = process$2(def.keyType, ctx, {
|
|
7232
|
+
...params,
|
|
7233
|
+
path: [...params.path, "propertyNames"]
|
|
7234
|
+
});
|
|
7235
|
+
json.additionalProperties = process$2(def.valueType, ctx, {
|
|
7236
|
+
...params,
|
|
7237
|
+
path: [...params.path, "additionalProperties"]
|
|
7238
|
+
});
|
|
7239
|
+
}
|
|
7240
|
+
const keyValues = keyType._zod.values;
|
|
7241
|
+
if (keyValues) {
|
|
7242
|
+
const validKeyValues = [...keyValues].filter((v$1) => typeof v$1 === "string" || typeof v$1 === "number");
|
|
7243
|
+
if (validKeyValues.length > 0) json.required = validKeyValues;
|
|
7244
|
+
}
|
|
7245
|
+
};
|
|
7112
7246
|
const nullableProcessor = (schema, ctx, json, params) => {
|
|
7113
7247
|
const def = schema._zod.def;
|
|
7114
7248
|
const inner = process$2(def.innerType, ctx, params);
|
|
@@ -7501,14 +7635,6 @@ const ZodNumberFormat = /* @__PURE__ */ $constructor("ZodNumberFormat", (inst, d
|
|
|
7501
7635
|
function int(params) {
|
|
7502
7636
|
return _int(ZodNumberFormat, params);
|
|
7503
7637
|
}
|
|
7504
|
-
const ZodBoolean = /* @__PURE__ */ $constructor("ZodBoolean", (inst, def) => {
|
|
7505
|
-
$ZodBoolean.init(inst, def);
|
|
7506
|
-
ZodType.init(inst, def);
|
|
7507
|
-
inst._zod.processJSONSchema = (ctx, json, params) => booleanProcessor(inst, ctx, json, params);
|
|
7508
|
-
});
|
|
7509
|
-
function boolean(params) {
|
|
7510
|
-
return _boolean(ZodBoolean, params);
|
|
7511
|
-
}
|
|
7512
7638
|
const ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
|
|
7513
7639
|
$ZodUnknown.init(inst, def);
|
|
7514
7640
|
ZodType.init(inst, def);
|
|
@@ -7638,6 +7764,21 @@ function tuple(items, _paramsOrRest, _params) {
|
|
|
7638
7764
|
...normalizeParams(params)
|
|
7639
7765
|
});
|
|
7640
7766
|
}
|
|
7767
|
+
const ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
7768
|
+
$ZodRecord.init(inst, def);
|
|
7769
|
+
ZodType.init(inst, def);
|
|
7770
|
+
inst._zod.processJSONSchema = (ctx, json, params) => recordProcessor(inst, ctx, json, params);
|
|
7771
|
+
inst.keyType = def.keyType;
|
|
7772
|
+
inst.valueType = def.valueType;
|
|
7773
|
+
});
|
|
7774
|
+
function record(keyType, valueType, params) {
|
|
7775
|
+
return new ZodRecord({
|
|
7776
|
+
type: "record",
|
|
7777
|
+
keyType,
|
|
7778
|
+
valueType,
|
|
7779
|
+
...normalizeParams(params)
|
|
7780
|
+
});
|
|
7781
|
+
}
|
|
7641
7782
|
const ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
|
|
7642
7783
|
$ZodEnum.init(inst, def);
|
|
7643
7784
|
ZodType.init(inst, def);
|
|
@@ -7675,6 +7816,23 @@ function _enum(values, params) {
|
|
|
7675
7816
|
...normalizeParams(params)
|
|
7676
7817
|
});
|
|
7677
7818
|
}
|
|
7819
|
+
const ZodLiteral = /* @__PURE__ */ $constructor("ZodLiteral", (inst, def) => {
|
|
7820
|
+
$ZodLiteral.init(inst, def);
|
|
7821
|
+
ZodType.init(inst, def);
|
|
7822
|
+
inst._zod.processJSONSchema = (ctx, json, params) => literalProcessor(inst, ctx, json, params);
|
|
7823
|
+
inst.values = new Set(def.values);
|
|
7824
|
+
Object.defineProperty(inst, "value", { get() {
|
|
7825
|
+
if (def.values.length > 1) throw new Error("This schema contains multiple valid literal values. Use `.values` instead.");
|
|
7826
|
+
return def.values[0];
|
|
7827
|
+
} });
|
|
7828
|
+
});
|
|
7829
|
+
function literal(value, params) {
|
|
7830
|
+
return new ZodLiteral({
|
|
7831
|
+
type: "literal",
|
|
7832
|
+
values: Array.isArray(value) ? value : [value],
|
|
7833
|
+
...normalizeParams(params)
|
|
7834
|
+
});
|
|
7835
|
+
}
|
|
7678
7836
|
const ZodTransform = /* @__PURE__ */ $constructor("ZodTransform", (inst, def) => {
|
|
7679
7837
|
$ZodTransform.init(inst, def);
|
|
7680
7838
|
ZodType.init(inst, def);
|
|
@@ -7900,19 +8058,6 @@ var AuthValidationError = class extends Error {
|
|
|
7900
8058
|
this.name = "AuthValidationError";
|
|
7901
8059
|
}
|
|
7902
8060
|
};
|
|
7903
|
-
var ConnectorApiError = class extends Error {
|
|
7904
|
-
constructor(message, cause) {
|
|
7905
|
-
super(message);
|
|
7906
|
-
this.cause = cause;
|
|
7907
|
-
this.name = "ConnectorApiError";
|
|
7908
|
-
}
|
|
7909
|
-
};
|
|
7910
|
-
var ConnectorValidationError = class extends Error {
|
|
7911
|
-
constructor(message) {
|
|
7912
|
-
super(message);
|
|
7913
|
-
this.name = "ConnectorValidationError";
|
|
7914
|
-
}
|
|
7915
|
-
};
|
|
7916
8061
|
|
|
7917
8062
|
//#endregion
|
|
7918
8063
|
//#region src/core/consts.ts
|
|
@@ -13637,10 +13782,10 @@ var require_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
13637
13782
|
exports.removeDuplicateSlashes = removeDuplicateSlashes;
|
|
13638
13783
|
function partitionAbsoluteAndRelative(patterns) {
|
|
13639
13784
|
const absolute = [];
|
|
13640
|
-
const relative = [];
|
|
13785
|
+
const relative$1 = [];
|
|
13641
13786
|
for (const pattern of patterns) if (isAbsolute(pattern)) absolute.push(pattern);
|
|
13642
|
-
else relative.push(pattern);
|
|
13643
|
-
return [absolute, relative];
|
|
13787
|
+
else relative$1.push(pattern);
|
|
13788
|
+
return [absolute, relative$1];
|
|
13644
13789
|
}
|
|
13645
13790
|
exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
|
|
13646
13791
|
function isAbsolute(pattern) {
|
|
@@ -28065,7 +28210,7 @@ const FILE$1 = Symbol("file");
|
|
|
28065
28210
|
const DIRECTORY$1 = Symbol("directory");
|
|
28066
28211
|
const SYMLINK$1 = Symbol("symlink");
|
|
28067
28212
|
const HARDLINK$1 = Symbol("hardlink");
|
|
28068
|
-
const HEADER = Symbol("header");
|
|
28213
|
+
const HEADER$1 = Symbol("header");
|
|
28069
28214
|
const READ = Symbol("read");
|
|
28070
28215
|
const LSTAT = Symbol("lstat");
|
|
28071
28216
|
const ONLSTAT = Symbol("onlstat");
|
|
@@ -28184,7 +28329,7 @@ var WriteEntry = class extends Minipass {
|
|
|
28184
28329
|
[PREFIX](path$16) {
|
|
28185
28330
|
return prefixPath(path$16, this.prefix);
|
|
28186
28331
|
}
|
|
28187
|
-
[HEADER]() {
|
|
28332
|
+
[HEADER$1]() {
|
|
28188
28333
|
/* c8 ignore start */
|
|
28189
28334
|
if (!this.stat) throw new Error("cannot write header before stat");
|
|
28190
28335
|
/* c8 ignore stop */
|
|
@@ -28229,7 +28374,7 @@ var WriteEntry = class extends Minipass {
|
|
|
28229
28374
|
/* c8 ignore stop */
|
|
28230
28375
|
if (this.path.slice(-1) !== "/") this.path += "/";
|
|
28231
28376
|
this.stat.size = 0;
|
|
28232
|
-
this[HEADER]();
|
|
28377
|
+
this[HEADER$1]();
|
|
28233
28378
|
this.end();
|
|
28234
28379
|
}
|
|
28235
28380
|
[SYMLINK$1]() {
|
|
@@ -28240,7 +28385,7 @@ var WriteEntry = class extends Minipass {
|
|
|
28240
28385
|
}
|
|
28241
28386
|
[ONREADLINK](linkpath) {
|
|
28242
28387
|
this.linkpath = normalizeWindowsPath(linkpath);
|
|
28243
|
-
this[HEADER]();
|
|
28388
|
+
this[HEADER$1]();
|
|
28244
28389
|
this.end();
|
|
28245
28390
|
}
|
|
28246
28391
|
[HARDLINK$1](linkpath) {
|
|
@@ -28250,7 +28395,7 @@ var WriteEntry = class extends Minipass {
|
|
|
28250
28395
|
this.type = "Link";
|
|
28251
28396
|
this.linkpath = normalizeWindowsPath(path$1.relative(this.cwd, linkpath));
|
|
28252
28397
|
this.stat.size = 0;
|
|
28253
|
-
this[HEADER]();
|
|
28398
|
+
this[HEADER$1]();
|
|
28254
28399
|
this.end();
|
|
28255
28400
|
}
|
|
28256
28401
|
[FILE$1]() {
|
|
@@ -28263,7 +28408,7 @@ var WriteEntry = class extends Minipass {
|
|
|
28263
28408
|
if (linkpath?.indexOf(this.cwd) === 0) return this[HARDLINK$1](linkpath);
|
|
28264
28409
|
this.linkCache.set(linkKey, this.absolute);
|
|
28265
28410
|
}
|
|
28266
|
-
this[HEADER]();
|
|
28411
|
+
this[HEADER$1]();
|
|
28267
28412
|
if (this.stat.size === 0) return this.end();
|
|
28268
28413
|
this[OPENFILE]();
|
|
28269
28414
|
}
|
|
@@ -31005,10 +31150,7 @@ const theme = {
|
|
|
31005
31150
|
base44OrangeBackground: source_default.bgHex("#E86B3C"),
|
|
31006
31151
|
shinyOrange: source_default.hex("#FFD700"),
|
|
31007
31152
|
links: source_default.hex("#00D4FF"),
|
|
31008
|
-
white: source_default.white
|
|
31009
|
-
success: source_default.green,
|
|
31010
|
-
warning: source_default.yellow,
|
|
31011
|
-
error: source_default.red
|
|
31153
|
+
white: source_default.white
|
|
31012
31154
|
},
|
|
31013
31155
|
styles: {
|
|
31014
31156
|
header: source_default.dim,
|
|
@@ -31319,6 +31461,237 @@ const logoutCommand = new Command("logout").description("Logout from current dev
|
|
|
31319
31461
|
await runCommand(logout, { requireAppConfig: false });
|
|
31320
31462
|
});
|
|
31321
31463
|
|
|
31464
|
+
//#endregion
|
|
31465
|
+
//#region src/core/types/schema.ts
|
|
31466
|
+
/**
|
|
31467
|
+
* Schema for a full entity definition
|
|
31468
|
+
* Uses loose validation to allow JSON Schema flexibility
|
|
31469
|
+
*/
|
|
31470
|
+
const EntityDefinitionSchema = object({
|
|
31471
|
+
name: string().min(1, "Entity name cannot be empty"),
|
|
31472
|
+
type: literal("object").optional(),
|
|
31473
|
+
properties: record(string(), unknown()).optional(),
|
|
31474
|
+
required: array(string()).optional()
|
|
31475
|
+
}).passthrough();
|
|
31476
|
+
|
|
31477
|
+
//#endregion
|
|
31478
|
+
//#region src/core/types/generator.ts
|
|
31479
|
+
const HEADER = `// Auto-generated by Base44 CLI - DO NOT EDIT
|
|
31480
|
+
// Regenerate with: base44 types
|
|
31481
|
+
`;
|
|
31482
|
+
/**
|
|
31483
|
+
* Convert a JSON Schema type to TypeScript type
|
|
31484
|
+
*/
|
|
31485
|
+
function fieldToTypeScript(field, required$1) {
|
|
31486
|
+
let tsType;
|
|
31487
|
+
if (field.enum && field.enum.length > 0) tsType = field.enum.map((v$1) => `'${v$1}'`).join(" | ");
|
|
31488
|
+
else switch (field.type) {
|
|
31489
|
+
case "string":
|
|
31490
|
+
tsType = "string";
|
|
31491
|
+
break;
|
|
31492
|
+
case "number":
|
|
31493
|
+
tsType = "number";
|
|
31494
|
+
break;
|
|
31495
|
+
case "boolean":
|
|
31496
|
+
tsType = "boolean";
|
|
31497
|
+
break;
|
|
31498
|
+
case "array":
|
|
31499
|
+
if (field.items) tsType = `${fieldToTypeScript(field.items, true)}[]`;
|
|
31500
|
+
else tsType = "unknown[]";
|
|
31501
|
+
break;
|
|
31502
|
+
case "object":
|
|
31503
|
+
if (field.properties) tsType = `{ ${Object.entries(field.properties).map(([name$1, prop]) => {
|
|
31504
|
+
const isRequired = field.required?.includes(name$1) ?? false;
|
|
31505
|
+
const propType = fieldToTypeScript(prop, isRequired);
|
|
31506
|
+
return `${name$1}${isRequired ? "" : "?"}: ${propType}`;
|
|
31507
|
+
}).join("; ")} }`;
|
|
31508
|
+
else tsType = "Record<string, unknown>";
|
|
31509
|
+
break;
|
|
31510
|
+
default: tsType = "unknown";
|
|
31511
|
+
}
|
|
31512
|
+
return tsType;
|
|
31513
|
+
}
|
|
31514
|
+
/**
|
|
31515
|
+
* Generate interface fields for an entity
|
|
31516
|
+
*/
|
|
31517
|
+
function generateInterfaceFields(entity, options = {}) {
|
|
31518
|
+
if (!entity.properties) return "";
|
|
31519
|
+
const requiredFields = new Set(entity.required ?? []);
|
|
31520
|
+
return Object.entries(entity.properties).map(([fieldName, field]) => {
|
|
31521
|
+
const isRequired = options.allOptional ? false : requiredFields.has(fieldName);
|
|
31522
|
+
const tsType = fieldToTypeScript(field, isRequired);
|
|
31523
|
+
const optional$1 = isRequired ? "" : "?";
|
|
31524
|
+
return `${field.description ? ` /** ${field.description} */\n` : ""} ${fieldName}${optional$1}: ${tsType};`;
|
|
31525
|
+
}).join("\n");
|
|
31526
|
+
}
|
|
31527
|
+
/**
|
|
31528
|
+
* Generate TypeScript interfaces for a single entity
|
|
31529
|
+
*/
|
|
31530
|
+
function generateEntityTypes(entity) {
|
|
31531
|
+
const name$1 = entity.name;
|
|
31532
|
+
return `
|
|
31533
|
+
/** ${name$1} entity */
|
|
31534
|
+
export interface ${name$1} extends BaseEntity {
|
|
31535
|
+
${generateInterfaceFields(entity)}
|
|
31536
|
+
}
|
|
31537
|
+
|
|
31538
|
+
/** Input for creating a ${name$1} */
|
|
31539
|
+
export interface ${name$1}CreateInput {
|
|
31540
|
+
${generateInterfaceFields(entity)}
|
|
31541
|
+
}
|
|
31542
|
+
|
|
31543
|
+
/** Input for updating a ${name$1} (all fields optional) */
|
|
31544
|
+
export interface ${name$1}UpdateInput {
|
|
31545
|
+
${generateInterfaceFields(entity, { allOptional: true })}
|
|
31546
|
+
}
|
|
31547
|
+
|
|
31548
|
+
/** Filter query for ${name$1} */
|
|
31549
|
+
export interface ${name$1}Filter {
|
|
31550
|
+
${generateInterfaceFields(entity, { allOptional: true })}
|
|
31551
|
+
}
|
|
31552
|
+
`;
|
|
31553
|
+
}
|
|
31554
|
+
/**
|
|
31555
|
+
* Generate the entities.ts file content
|
|
31556
|
+
*/
|
|
31557
|
+
function generateEntitiesFile(entities) {
|
|
31558
|
+
const baseEntity = `
|
|
31559
|
+
/** System fields present on all entities */
|
|
31560
|
+
export interface BaseEntity {
|
|
31561
|
+
id: string;
|
|
31562
|
+
created_date: string;
|
|
31563
|
+
updated_date: string;
|
|
31564
|
+
}
|
|
31565
|
+
`;
|
|
31566
|
+
const entityTypes = entities.map(generateEntityTypes).join("\n");
|
|
31567
|
+
const entityNames = entities.map((e$1) => `'${e$1.name}'`).join(" | ");
|
|
31568
|
+
const entityNamesType = entities.length > 0 ? `\n/** All entity names in this project */\nexport type EntityName = ${entityNames};\n` : "";
|
|
31569
|
+
return HEADER + baseEntity + entityTypes + entityNamesType;
|
|
31570
|
+
}
|
|
31571
|
+
/**
|
|
31572
|
+
* Generate the client.ts file content with typed SDK wrapper
|
|
31573
|
+
*/
|
|
31574
|
+
function generateClientFile(entities) {
|
|
31575
|
+
if (entities.length === 0) return HEADER + "\n// No entities found\nexport {};\n";
|
|
31576
|
+
return `${HEADER}
|
|
31577
|
+
import type {
|
|
31578
|
+
${entities.map((e$1) => `${e$1.name}, ${e$1.name}CreateInput, ${e$1.name}UpdateInput, ${e$1.name}Filter`).join(",\n ")}
|
|
31579
|
+
} from './entities.js';
|
|
31580
|
+
|
|
31581
|
+
/** Response for list/filter operations */
|
|
31582
|
+
export interface ListResponse<T> {
|
|
31583
|
+
items: T[];
|
|
31584
|
+
total: number;
|
|
31585
|
+
}
|
|
31586
|
+
|
|
31587
|
+
/** Real-time subscription event */
|
|
31588
|
+
export interface RealtimeEvent<T> {
|
|
31589
|
+
type: 'create' | 'update' | 'delete';
|
|
31590
|
+
data: T;
|
|
31591
|
+
id: string;
|
|
31592
|
+
timestamp: string;
|
|
31593
|
+
}
|
|
31594
|
+
|
|
31595
|
+
/** Entity handler with CRUD operations */
|
|
31596
|
+
export interface EntityHandler<T, TCreate, TUpdate, TFilter> {
|
|
31597
|
+
/** Get all records with optional pagination */
|
|
31598
|
+
list(sort?: string, limit?: number, skip?: number, fields?: (keyof T)[]): Promise<ListResponse<T>>;
|
|
31599
|
+
/** Get records matching filter criteria */
|
|
31600
|
+
filter(query: TFilter, sort?: string, limit?: number, skip?: number, fields?: (keyof T)[]): Promise<ListResponse<T>>;
|
|
31601
|
+
/** Get a single record by ID */
|
|
31602
|
+
get(id: string): Promise<T>;
|
|
31603
|
+
/** Create a new record */
|
|
31604
|
+
create(data: TCreate): Promise<T>;
|
|
31605
|
+
/** Update an existing record */
|
|
31606
|
+
update(id: string, data: TUpdate): Promise<T>;
|
|
31607
|
+
/** Delete a record */
|
|
31608
|
+
delete(id: string): Promise<void>;
|
|
31609
|
+
/** Delete multiple records matching filter */
|
|
31610
|
+
deleteMany(query: TFilter): Promise<void>;
|
|
31611
|
+
/** Create multiple records at once */
|
|
31612
|
+
bulkCreate(data: TCreate[]): Promise<T[]>;
|
|
31613
|
+
/** Subscribe to real-time updates */
|
|
31614
|
+
subscribe(callback: (event: RealtimeEvent<T>) => void): () => void;
|
|
31615
|
+
}
|
|
31616
|
+
|
|
31617
|
+
/** Typed entities interface for Base44 SDK */
|
|
31618
|
+
export interface TypedEntities {
|
|
31619
|
+
${entities.map((e$1) => ` ${e$1.name}: EntityHandler<${e$1.name}, ${e$1.name}CreateInput, ${e$1.name}UpdateInput, ${e$1.name}Filter>;`).join("\n")}
|
|
31620
|
+
}
|
|
31621
|
+
|
|
31622
|
+
/**
|
|
31623
|
+
* Typed Base44 client interface.
|
|
31624
|
+
* Use with the Base44 SDK for type-safe entity access.
|
|
31625
|
+
*
|
|
31626
|
+
* @example
|
|
31627
|
+
* import { createClient } from '@base44/sdk';
|
|
31628
|
+
* import type { TypedBase44Client } from './base44/client';
|
|
31629
|
+
*
|
|
31630
|
+
* const base44 = createClient({ appId: 'your-app-id' }) as TypedBase44Client;
|
|
31631
|
+
* const tasks = await base44.entities.Task.list();
|
|
31632
|
+
*/
|
|
31633
|
+
export interface TypedBase44Client {
|
|
31634
|
+
entities: TypedEntities;
|
|
31635
|
+
}
|
|
31636
|
+
`;
|
|
31637
|
+
}
|
|
31638
|
+
/**
|
|
31639
|
+
* Generate the index.ts barrel file
|
|
31640
|
+
*/
|
|
31641
|
+
function generateIndexFile() {
|
|
31642
|
+
return `${HEADER}
|
|
31643
|
+
export * from './entities.js';
|
|
31644
|
+
export * from './client.js';
|
|
31645
|
+
`;
|
|
31646
|
+
}
|
|
31647
|
+
|
|
31648
|
+
//#endregion
|
|
31649
|
+
//#region src/core/types/types.ts
|
|
31650
|
+
/**
|
|
31651
|
+
* Parse entities into EntityDefinition format for type generation
|
|
31652
|
+
*/
|
|
31653
|
+
function parseEntities(entities) {
|
|
31654
|
+
return entities.map((entity) => {
|
|
31655
|
+
const result = EntityDefinitionSchema.safeParse(entity);
|
|
31656
|
+
if (result.success) return result.data;
|
|
31657
|
+
const entityName = entity.name ?? "unknown";
|
|
31658
|
+
console.warn(`Skipping invalid entity "${entityName}": ${result.error.issues.map((i$1) => `${i$1.path.join(".")}: ${i$1.message}`).join(", ")}`);
|
|
31659
|
+
return null;
|
|
31660
|
+
}).filter((e$1) => e$1 !== null);
|
|
31661
|
+
}
|
|
31662
|
+
/**
|
|
31663
|
+
* Generate TypeScript types from local entity schemas
|
|
31664
|
+
*/
|
|
31665
|
+
async function generateTypes(options = {}) {
|
|
31666
|
+
const { output = "src/base44", entitiesOnly = false } = options;
|
|
31667
|
+
const projectRoot = await findProjectRoot();
|
|
31668
|
+
if (!projectRoot) throw new Error("Project root not found. Please run this command from within a Base44 project.");
|
|
31669
|
+
const { entities } = await readProjectConfig(projectRoot.root);
|
|
31670
|
+
const parsedEntities = parseEntities(entities);
|
|
31671
|
+
const outputDir = join(projectRoot.root, output);
|
|
31672
|
+
await mkdir(outputDir, { recursive: true });
|
|
31673
|
+
const files = [];
|
|
31674
|
+
const entitiesContent = generateEntitiesFile(parsedEntities);
|
|
31675
|
+
const entitiesPath = join(outputDir, "entities.ts");
|
|
31676
|
+
await writeFile(entitiesPath, entitiesContent, "utf-8");
|
|
31677
|
+
files.push(entitiesPath);
|
|
31678
|
+
if (!entitiesOnly) {
|
|
31679
|
+
const clientContent = generateClientFile(parsedEntities);
|
|
31680
|
+
const clientPath = join(outputDir, "client.ts");
|
|
31681
|
+
await writeFile(clientPath, clientContent, "utf-8");
|
|
31682
|
+
files.push(clientPath);
|
|
31683
|
+
const indexContent = generateIndexFile();
|
|
31684
|
+
const indexPath = join(outputDir, "index.ts");
|
|
31685
|
+
await writeFile(indexPath, indexContent, "utf-8");
|
|
31686
|
+
files.push(indexPath);
|
|
31687
|
+
}
|
|
31688
|
+
return {
|
|
31689
|
+
entityCount: parsedEntities.length,
|
|
31690
|
+
files,
|
|
31691
|
+
outputDir
|
|
31692
|
+
};
|
|
31693
|
+
}
|
|
31694
|
+
|
|
31322
31695
|
//#endregion
|
|
31323
31696
|
//#region src/cli/commands/entities/push.ts
|
|
31324
31697
|
async function pushEntitiesAction() {
|
|
@@ -31364,6 +31737,35 @@ const functionsDeployCommand = new Command("functions").description("Manage proj
|
|
|
31364
31737
|
await runCommand(deployFunctionsAction, { requireAuth: true });
|
|
31365
31738
|
}));
|
|
31366
31739
|
|
|
31740
|
+
//#endregion
|
|
31741
|
+
//#region src/cli/commands/types/generate.ts
|
|
31742
|
+
async function generateTypesAction(options) {
|
|
31743
|
+
const result = await runTask("Generating TypeScript types", async () => {
|
|
31744
|
+
return await generateTypes({
|
|
31745
|
+
output: options.output,
|
|
31746
|
+
entitiesOnly: options.entitiesOnly
|
|
31747
|
+
});
|
|
31748
|
+
}, {
|
|
31749
|
+
successMessage: "Types generated successfully",
|
|
31750
|
+
errorMessage: "Failed to generate types"
|
|
31751
|
+
});
|
|
31752
|
+
if (result.entityCount === 0) {
|
|
31753
|
+
M.warn("No entities found in project");
|
|
31754
|
+
return { outroMessage: "No types generated" };
|
|
31755
|
+
}
|
|
31756
|
+
M.info(`Generated types for ${result.entityCount} entities`);
|
|
31757
|
+
M.info(`Output directory: ${result.outputDir}`);
|
|
31758
|
+
const fileList = result.files.map((f) => ` - ${relative(process.cwd(), f)}`).join("\n");
|
|
31759
|
+
M.info(`Files:\n${fileList}`);
|
|
31760
|
+
return { outroMessage: "Types generated successfully!" };
|
|
31761
|
+
}
|
|
31762
|
+
const typesGenerateCommand = new Command("types").description("Generate TypeScript types from entity schemas").option("-o, --output <dir>", "Output directory", "src/base44").option("--entities-only", "Only generate entity types, skip client types").action(async (options) => {
|
|
31763
|
+
await runCommand(() => generateTypesAction(options), {
|
|
31764
|
+
requireAuth: false,
|
|
31765
|
+
requireAppConfig: false
|
|
31766
|
+
});
|
|
31767
|
+
});
|
|
31768
|
+
|
|
31367
31769
|
//#endregion
|
|
31368
31770
|
//#region node_modules/is-plain-obj/index.js
|
|
31369
31771
|
function isPlainObject(value) {
|
|
@@ -38842,390 +39244,6 @@ const siteDeployCommand = new Command("site").description("Manage site deploymen
|
|
|
38842
39244
|
await runCommand(() => deployAction(options), { requireAuth: true });
|
|
38843
39245
|
}));
|
|
38844
39246
|
|
|
38845
|
-
//#endregion
|
|
38846
|
-
//#region src/core/connectors/schema.ts
|
|
38847
|
-
/**
|
|
38848
|
-
* Response from POST /api/apps/{app_id}/external-auth/initiate
|
|
38849
|
-
*/
|
|
38850
|
-
const InitiateResponseSchema = object({
|
|
38851
|
-
redirect_url: string().optional(),
|
|
38852
|
-
connection_id: string().optional(),
|
|
38853
|
-
already_authorized: boolean().optional(),
|
|
38854
|
-
other_user_email: string().optional(),
|
|
38855
|
-
error: string().optional()
|
|
38856
|
-
});
|
|
38857
|
-
/**
|
|
38858
|
-
* Response from GET /api/apps/{app_id}/external-auth/status
|
|
38859
|
-
*/
|
|
38860
|
-
const StatusResponseSchema = object({
|
|
38861
|
-
status: _enum([
|
|
38862
|
-
"ACTIVE",
|
|
38863
|
-
"PENDING",
|
|
38864
|
-
"FAILED"
|
|
38865
|
-
]),
|
|
38866
|
-
account_email: string().optional(),
|
|
38867
|
-
error: string().optional()
|
|
38868
|
-
});
|
|
38869
|
-
/**
|
|
38870
|
-
* A connected integration from the list endpoint
|
|
38871
|
-
*/
|
|
38872
|
-
const ConnectorSchema = object({
|
|
38873
|
-
integration_type: string(),
|
|
38874
|
-
status: string(),
|
|
38875
|
-
connected_at: string().optional(),
|
|
38876
|
-
account_info: object({
|
|
38877
|
-
email: string().optional(),
|
|
38878
|
-
name: string().optional()
|
|
38879
|
-
}).optional()
|
|
38880
|
-
}).transform((data) => ({
|
|
38881
|
-
integrationType: data.integration_type,
|
|
38882
|
-
status: data.status,
|
|
38883
|
-
connectedAt: data.connected_at,
|
|
38884
|
-
accountInfo: data.account_info
|
|
38885
|
-
}));
|
|
38886
|
-
/**
|
|
38887
|
-
* Response from GET /api/apps/{app_id}/external-auth/list
|
|
38888
|
-
*/
|
|
38889
|
-
const ListResponseSchema = object({ integrations: array(ConnectorSchema) });
|
|
38890
|
-
/**
|
|
38891
|
-
* Generic API error response
|
|
38892
|
-
*/
|
|
38893
|
-
const ApiErrorSchema = object({
|
|
38894
|
-
error: string(),
|
|
38895
|
-
detail: string().optional()
|
|
38896
|
-
});
|
|
38897
|
-
|
|
38898
|
-
//#endregion
|
|
38899
|
-
//#region src/core/connectors/api.ts
|
|
38900
|
-
/**
|
|
38901
|
-
* Initiates OAuth flow for a connector integration.
|
|
38902
|
-
* Returns a redirect URL to open in the browser.
|
|
38903
|
-
*/
|
|
38904
|
-
async function initiateOAuth(integrationType, scopes = null) {
|
|
38905
|
-
const response = await getAppClient().post("external-auth/initiate", {
|
|
38906
|
-
json: {
|
|
38907
|
-
integration_type: integrationType,
|
|
38908
|
-
scopes
|
|
38909
|
-
},
|
|
38910
|
-
throwHttpErrors: false
|
|
38911
|
-
});
|
|
38912
|
-
const json = await response.json();
|
|
38913
|
-
if (!response.ok) {
|
|
38914
|
-
const errorResult = ApiErrorSchema.safeParse(json);
|
|
38915
|
-
if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
|
|
38916
|
-
throw new ConnectorApiError(`Failed to initiate OAuth: ${response.status} ${response.statusText}`);
|
|
38917
|
-
}
|
|
38918
|
-
const result = InitiateResponseSchema.safeParse(json);
|
|
38919
|
-
if (!result.success) throw new ConnectorValidationError(`Invalid initiate response from server: ${result.error.message}`);
|
|
38920
|
-
return result.data;
|
|
38921
|
-
}
|
|
38922
|
-
/**
|
|
38923
|
-
* Checks the status of an OAuth connection attempt.
|
|
38924
|
-
*/
|
|
38925
|
-
async function checkOAuthStatus(integrationType, connectionId) {
|
|
38926
|
-
const response = await getAppClient().get("external-auth/status", {
|
|
38927
|
-
searchParams: {
|
|
38928
|
-
integration_type: integrationType,
|
|
38929
|
-
connection_id: connectionId
|
|
38930
|
-
},
|
|
38931
|
-
throwHttpErrors: false
|
|
38932
|
-
});
|
|
38933
|
-
const json = await response.json();
|
|
38934
|
-
if (!response.ok) {
|
|
38935
|
-
const errorResult = ApiErrorSchema.safeParse(json);
|
|
38936
|
-
if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
|
|
38937
|
-
throw new ConnectorApiError(`Failed to check OAuth status: ${response.status} ${response.statusText}`);
|
|
38938
|
-
}
|
|
38939
|
-
const result = StatusResponseSchema.safeParse(json);
|
|
38940
|
-
if (!result.success) throw new ConnectorValidationError(`Invalid status response from server: ${result.error.message}`);
|
|
38941
|
-
return result.data;
|
|
38942
|
-
}
|
|
38943
|
-
/**
|
|
38944
|
-
* Lists all connected integrations for the current app.
|
|
38945
|
-
*/
|
|
38946
|
-
async function listConnectors() {
|
|
38947
|
-
const response = await getAppClient().get("external-auth/list", { throwHttpErrors: false });
|
|
38948
|
-
const json = await response.json();
|
|
38949
|
-
if (!response.ok) {
|
|
38950
|
-
const errorResult = ApiErrorSchema.safeParse(json);
|
|
38951
|
-
if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
|
|
38952
|
-
throw new ConnectorApiError(`Failed to list connectors: ${response.status} ${response.statusText}`);
|
|
38953
|
-
}
|
|
38954
|
-
const result = ListResponseSchema.safeParse(json);
|
|
38955
|
-
if (!result.success) throw new ConnectorValidationError(`Invalid list response from server: ${result.error.message}`);
|
|
38956
|
-
return result.data.integrations;
|
|
38957
|
-
}
|
|
38958
|
-
/**
|
|
38959
|
-
* Disconnects (soft delete) a connector integration.
|
|
38960
|
-
*/
|
|
38961
|
-
async function disconnectConnector(integrationType) {
|
|
38962
|
-
const response = await getAppClient().delete(`external-auth/integrations/${integrationType}`, { throwHttpErrors: false });
|
|
38963
|
-
if (!response.ok) {
|
|
38964
|
-
const json = await response.json();
|
|
38965
|
-
const errorResult = ApiErrorSchema.safeParse(json);
|
|
38966
|
-
if (errorResult.success) throw new ConnectorApiError(errorResult.data.error);
|
|
38967
|
-
throw new ConnectorApiError(`Failed to disconnect connector: ${response.status} ${response.statusText}`);
|
|
38968
|
-
}
|
|
38969
|
-
}
|
|
38970
|
-
|
|
38971
|
-
//#endregion
|
|
38972
|
-
//#region src/core/connectors/constants.ts
|
|
38973
|
-
/**
|
|
38974
|
-
* Supported OAuth connector integrations.
|
|
38975
|
-
* Based on apper/backend/app/external_auth/models/constants.py
|
|
38976
|
-
*/
|
|
38977
|
-
const SUPPORTED_INTEGRATIONS = [
|
|
38978
|
-
"googlecalendar",
|
|
38979
|
-
"googledrive",
|
|
38980
|
-
"gmail",
|
|
38981
|
-
"googlesheets",
|
|
38982
|
-
"googledocs",
|
|
38983
|
-
"googleslides",
|
|
38984
|
-
"slack",
|
|
38985
|
-
"notion",
|
|
38986
|
-
"salesforce",
|
|
38987
|
-
"hubspot",
|
|
38988
|
-
"linkedin",
|
|
38989
|
-
"tiktok"
|
|
38990
|
-
];
|
|
38991
|
-
/**
|
|
38992
|
-
* Display names for integrations (for CLI output)
|
|
38993
|
-
*/
|
|
38994
|
-
const INTEGRATION_DISPLAY_NAMES = {
|
|
38995
|
-
googlecalendar: "Google Calendar",
|
|
38996
|
-
googledrive: "Google Drive",
|
|
38997
|
-
gmail: "Gmail",
|
|
38998
|
-
googlesheets: "Google Sheets",
|
|
38999
|
-
googledocs: "Google Docs",
|
|
39000
|
-
googleslides: "Google Slides",
|
|
39001
|
-
slack: "Slack",
|
|
39002
|
-
notion: "Notion",
|
|
39003
|
-
salesforce: "Salesforce",
|
|
39004
|
-
hubspot: "HubSpot",
|
|
39005
|
-
linkedin: "LinkedIn",
|
|
39006
|
-
tiktok: "TikTok"
|
|
39007
|
-
};
|
|
39008
|
-
function isValidIntegration(type) {
|
|
39009
|
-
return SUPPORTED_INTEGRATIONS.includes(type);
|
|
39010
|
-
}
|
|
39011
|
-
function getIntegrationDisplayName(type) {
|
|
39012
|
-
if (isValidIntegration(type)) return INTEGRATION_DISPLAY_NAMES[type];
|
|
39013
|
-
return type;
|
|
39014
|
-
}
|
|
39015
|
-
|
|
39016
|
-
//#endregion
|
|
39017
|
-
//#region src/cli/commands/connectors/add.ts
|
|
39018
|
-
const POLL_INTERVAL_MS = 2e3;
|
|
39019
|
-
const POLL_TIMEOUT_MS = 300 * 1e3;
|
|
39020
|
-
async function promptForIntegrationType() {
|
|
39021
|
-
const selected = await ve({
|
|
39022
|
-
message: "Select an integration to connect:",
|
|
39023
|
-
options: SUPPORTED_INTEGRATIONS.map((type) => ({
|
|
39024
|
-
value: type,
|
|
39025
|
-
label: getIntegrationDisplayName(type)
|
|
39026
|
-
}))
|
|
39027
|
-
});
|
|
39028
|
-
if (pD(selected)) return null;
|
|
39029
|
-
return selected;
|
|
39030
|
-
}
|
|
39031
|
-
async function waitForOAuthCompletion(integrationType, connectionId) {
|
|
39032
|
-
let accountEmail;
|
|
39033
|
-
let error;
|
|
39034
|
-
try {
|
|
39035
|
-
await runTask("Waiting for authorization...", async (updateMessage) => {
|
|
39036
|
-
await pWaitFor(async () => {
|
|
39037
|
-
const status = await checkOAuthStatus(integrationType, connectionId);
|
|
39038
|
-
if (status.status === "ACTIVE") {
|
|
39039
|
-
accountEmail = status.account_email;
|
|
39040
|
-
return true;
|
|
39041
|
-
}
|
|
39042
|
-
if (status.status === "FAILED") {
|
|
39043
|
-
error = status.error || "Authorization failed";
|
|
39044
|
-
throw new Error(error);
|
|
39045
|
-
}
|
|
39046
|
-
updateMessage("Waiting for authorization in browser...");
|
|
39047
|
-
return false;
|
|
39048
|
-
}, {
|
|
39049
|
-
interval: POLL_INTERVAL_MS,
|
|
39050
|
-
timeout: POLL_TIMEOUT_MS
|
|
39051
|
-
});
|
|
39052
|
-
}, {
|
|
39053
|
-
successMessage: "Authorization completed!",
|
|
39054
|
-
errorMessage: "Authorization failed"
|
|
39055
|
-
});
|
|
39056
|
-
return {
|
|
39057
|
-
success: true,
|
|
39058
|
-
accountEmail
|
|
39059
|
-
};
|
|
39060
|
-
} catch (err) {
|
|
39061
|
-
if (err instanceof Error && err.message.includes("timed out")) return {
|
|
39062
|
-
success: false,
|
|
39063
|
-
error: "Authorization timed out. Please try again."
|
|
39064
|
-
};
|
|
39065
|
-
return {
|
|
39066
|
-
success: false,
|
|
39067
|
-
error: error || (err instanceof Error ? err.message : "Unknown error")
|
|
39068
|
-
};
|
|
39069
|
-
}
|
|
39070
|
-
}
|
|
39071
|
-
async function addConnector(integrationType) {
|
|
39072
|
-
let selectedType;
|
|
39073
|
-
if (!integrationType) {
|
|
39074
|
-
const prompted = await promptForIntegrationType();
|
|
39075
|
-
if (!prompted) return { outroMessage: "Cancelled" };
|
|
39076
|
-
selectedType = prompted;
|
|
39077
|
-
} else {
|
|
39078
|
-
if (!isValidIntegration(integrationType)) {
|
|
39079
|
-
const supportedList = SUPPORTED_INTEGRATIONS.join(", ");
|
|
39080
|
-
throw new Error(`Unsupported connector: ${integrationType}\nSupported connectors: ${supportedList}`);
|
|
39081
|
-
}
|
|
39082
|
-
selectedType = integrationType;
|
|
39083
|
-
}
|
|
39084
|
-
const displayName = getIntegrationDisplayName(selectedType);
|
|
39085
|
-
const initiateResponse = await runTask(`Initiating ${displayName} connection...`, async () => {
|
|
39086
|
-
return await initiateOAuth(selectedType);
|
|
39087
|
-
}, {
|
|
39088
|
-
successMessage: `${displayName} OAuth initiated`,
|
|
39089
|
-
errorMessage: `Failed to initiate ${displayName} connection`
|
|
39090
|
-
});
|
|
39091
|
-
if (initiateResponse.already_authorized) return { outroMessage: `Already connected to ${theme.styles.bold(displayName)}` };
|
|
39092
|
-
if (initiateResponse.error === "different_user" && initiateResponse.other_user_email) throw new Error(`This app is already connected to ${displayName} by ${initiateResponse.other_user_email}`);
|
|
39093
|
-
if (!initiateResponse.redirect_url || !initiateResponse.connection_id) throw new Error("Invalid response from server: missing redirect URL or connection ID");
|
|
39094
|
-
M.info(`Opening browser for ${displayName} authorization...`);
|
|
39095
|
-
await open_default(initiateResponse.redirect_url);
|
|
39096
|
-
const result = await waitForOAuthCompletion(selectedType, initiateResponse.connection_id);
|
|
39097
|
-
if (!result.success) throw new Error(result.error || "Authorization failed");
|
|
39098
|
-
const accountInfo = result.accountEmail ? ` as ${theme.styles.bold(result.accountEmail)}` : "";
|
|
39099
|
-
return { outroMessage: `Successfully connected to ${theme.styles.bold(displayName)}${accountInfo}` };
|
|
39100
|
-
}
|
|
39101
|
-
const connectorsAddCommand = new Command("connectors:add").argument("[type]", "Integration type (e.g., slack, notion, googlecalendar)").description("Connect an OAuth integration").action(async (type) => {
|
|
39102
|
-
await runCommand(() => addConnector(type), {
|
|
39103
|
-
requireAuth: true,
|
|
39104
|
-
requireAppConfig: true
|
|
39105
|
-
});
|
|
39106
|
-
});
|
|
39107
|
-
|
|
39108
|
-
//#endregion
|
|
39109
|
-
//#region src/cli/commands/connectors/list.ts
|
|
39110
|
-
function formatDate(dateString) {
|
|
39111
|
-
if (!dateString) return "-";
|
|
39112
|
-
try {
|
|
39113
|
-
return new Date(dateString).toLocaleDateString("en-US", {
|
|
39114
|
-
year: "numeric",
|
|
39115
|
-
month: "short",
|
|
39116
|
-
day: "numeric"
|
|
39117
|
-
});
|
|
39118
|
-
} catch {
|
|
39119
|
-
return dateString;
|
|
39120
|
-
}
|
|
39121
|
-
}
|
|
39122
|
-
function formatStatus(status) {
|
|
39123
|
-
const normalized = status.toLowerCase();
|
|
39124
|
-
if (normalized === "active" || normalized === "connected") return theme.colors.success("● active");
|
|
39125
|
-
if (normalized === "expired") return theme.colors.warning("● expired");
|
|
39126
|
-
if (normalized === "failed" || normalized === "disconnected") return theme.colors.error("● disconnected");
|
|
39127
|
-
return status;
|
|
39128
|
-
}
|
|
39129
|
-
async function listConnectorsCommand() {
|
|
39130
|
-
const connectors = await runTask("Fetching connectors...", async () => {
|
|
39131
|
-
return await listConnectors();
|
|
39132
|
-
}, {
|
|
39133
|
-
successMessage: "Connectors loaded",
|
|
39134
|
-
errorMessage: "Failed to fetch connectors"
|
|
39135
|
-
});
|
|
39136
|
-
if (connectors.length === 0) {
|
|
39137
|
-
M.info("No connectors configured for this app.");
|
|
39138
|
-
M.info(`Run ${theme.styles.bold("base44 connectors:add")} to connect an integration.`);
|
|
39139
|
-
return { outroMessage: "" };
|
|
39140
|
-
}
|
|
39141
|
-
console.log();
|
|
39142
|
-
console.log(theme.styles.bold("Connected Integrations:"));
|
|
39143
|
-
console.log();
|
|
39144
|
-
const headers = [
|
|
39145
|
-
"Type",
|
|
39146
|
-
"Account",
|
|
39147
|
-
"Status",
|
|
39148
|
-
"Connected"
|
|
39149
|
-
];
|
|
39150
|
-
const colWidths = [
|
|
39151
|
-
20,
|
|
39152
|
-
30,
|
|
39153
|
-
15,
|
|
39154
|
-
15
|
|
39155
|
-
];
|
|
39156
|
-
const headerRow = headers.map((h$2, i$1) => h$2.padEnd(colWidths[i$1])).join(" ");
|
|
39157
|
-
console.log(theme.styles.dim(headerRow));
|
|
39158
|
-
console.log(theme.styles.dim("─".repeat(headerRow.length)));
|
|
39159
|
-
for (const connector of connectors) {
|
|
39160
|
-
const type = getIntegrationDisplayName(connector.integrationType).padEnd(colWidths[0]);
|
|
39161
|
-
const account = (connector.accountInfo?.email || connector.accountInfo?.name || "-").padEnd(colWidths[1]);
|
|
39162
|
-
const status = formatStatus(connector.status);
|
|
39163
|
-
const connected = formatDate(connector.connectedAt).padEnd(colWidths[3]);
|
|
39164
|
-
console.log(`${type} ${account} ${status.padEnd(colWidths[2] + 10)} ${connected}`);
|
|
39165
|
-
}
|
|
39166
|
-
console.log();
|
|
39167
|
-
return { outroMessage: `${connectors.length} connector${connectors.length === 1 ? "" : "s"} configured` };
|
|
39168
|
-
}
|
|
39169
|
-
const connectorsListCommand = new Command("connectors:list").description("List all connected OAuth integrations").action(async () => {
|
|
39170
|
-
await runCommand(listConnectorsCommand, {
|
|
39171
|
-
requireAuth: true,
|
|
39172
|
-
requireAppConfig: true
|
|
39173
|
-
});
|
|
39174
|
-
});
|
|
39175
|
-
|
|
39176
|
-
//#endregion
|
|
39177
|
-
//#region src/cli/commands/connectors/remove.ts
|
|
39178
|
-
async function promptForConnectorToRemove(connectors) {
|
|
39179
|
-
const selected = await ve({
|
|
39180
|
-
message: "Select a connector to remove:",
|
|
39181
|
-
options: connectors.map((c$1) => ({
|
|
39182
|
-
value: c$1.integrationType,
|
|
39183
|
-
label: `${getIntegrationDisplayName(c$1.integrationType)}${c$1.accountInfo?.email ? ` (${c$1.accountInfo.email})` : ""}`
|
|
39184
|
-
}))
|
|
39185
|
-
});
|
|
39186
|
-
if (pD(selected)) return null;
|
|
39187
|
-
return selected;
|
|
39188
|
-
}
|
|
39189
|
-
async function removeConnectorCommand(integrationType) {
|
|
39190
|
-
const connectors = await runTask("Fetching connectors...", async () => {
|
|
39191
|
-
return await listConnectors();
|
|
39192
|
-
}, {
|
|
39193
|
-
successMessage: "Connectors loaded",
|
|
39194
|
-
errorMessage: "Failed to fetch connectors"
|
|
39195
|
-
});
|
|
39196
|
-
if (connectors.length === 0) return { outroMessage: "No connectors to remove" };
|
|
39197
|
-
let selectedType;
|
|
39198
|
-
if (!integrationType) {
|
|
39199
|
-
const prompted = await promptForConnectorToRemove(connectors);
|
|
39200
|
-
if (!prompted) return { outroMessage: "Cancelled" };
|
|
39201
|
-
selectedType = prompted;
|
|
39202
|
-
} else {
|
|
39203
|
-
if (!isValidIntegration(integrationType)) throw new Error(`Invalid connector type: ${integrationType}`);
|
|
39204
|
-
if (!connectors.some((c$1) => c$1.integrationType === integrationType)) throw new Error(`No ${getIntegrationDisplayName(integrationType)} connector found for this app`);
|
|
39205
|
-
selectedType = integrationType;
|
|
39206
|
-
}
|
|
39207
|
-
const displayName = getIntegrationDisplayName(selectedType);
|
|
39208
|
-
const connector = connectors.find((c$1) => c$1.integrationType === selectedType);
|
|
39209
|
-
const shouldRemove = await ye({
|
|
39210
|
-
message: `Disconnect ${displayName}${connector?.accountInfo?.email ? ` (${connector.accountInfo.email})` : ""}?`,
|
|
39211
|
-
initialValue: false
|
|
39212
|
-
});
|
|
39213
|
-
if (pD(shouldRemove) || !shouldRemove) return { outroMessage: "Cancelled" };
|
|
39214
|
-
await runTask(`Disconnecting ${displayName}...`, async () => {
|
|
39215
|
-
await disconnectConnector(selectedType);
|
|
39216
|
-
}, {
|
|
39217
|
-
successMessage: `${displayName} disconnected`,
|
|
39218
|
-
errorMessage: `Failed to disconnect ${displayName}`
|
|
39219
|
-
});
|
|
39220
|
-
return { outroMessage: `Successfully disconnected ${theme.styles.bold(displayName)}` };
|
|
39221
|
-
}
|
|
39222
|
-
const connectorsRemoveCommand = new Command("connectors:remove").argument("[type]", "Integration type to remove (e.g., slack, notion)").description("Disconnect an OAuth integration").action(async (type) => {
|
|
39223
|
-
await runCommand(() => removeConnectorCommand(type), {
|
|
39224
|
-
requireAuth: true,
|
|
39225
|
-
requireAppConfig: true
|
|
39226
|
-
});
|
|
39227
|
-
});
|
|
39228
|
-
|
|
39229
39247
|
//#endregion
|
|
39230
39248
|
//#region package.json
|
|
39231
39249
|
var version = "0.0.15";
|
|
@@ -39244,10 +39262,8 @@ program.addCommand(deployCommand);
|
|
|
39244
39262
|
program.addCommand(linkCommand);
|
|
39245
39263
|
program.addCommand(entitiesPushCommand);
|
|
39246
39264
|
program.addCommand(functionsDeployCommand);
|
|
39265
|
+
program.addCommand(typesGenerateCommand);
|
|
39247
39266
|
program.addCommand(siteDeployCommand);
|
|
39248
|
-
program.addCommand(connectorsAddCommand);
|
|
39249
|
-
program.addCommand(connectorsListCommand);
|
|
39250
|
-
program.addCommand(connectorsRemoveCommand);
|
|
39251
39267
|
program.parse();
|
|
39252
39268
|
|
|
39253
39269
|
//#endregion
|
package/package.json
CHANGED