@base44-preview/cli 0.0.32-pr.271.5d60e7b → 0.0.33-pr.259.052dce4
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 +13 -7
- package/dist/cli/index.js +542 -14
- package/dist/cli/index.js.map +22 -9
- package/dist/deno-runtime/main.js +31 -0
- package/dist/deno-runtime/main.js.map +10 -0
- package/package.json +2 -1
package/dist/cli/index.js
CHANGED
|
@@ -178514,6 +178514,18 @@ class InvalidInputError extends UserError {
|
|
|
178514
178514
|
code = "INVALID_INPUT";
|
|
178515
178515
|
}
|
|
178516
178516
|
|
|
178517
|
+
class DependencyNotFoundError extends UserError {
|
|
178518
|
+
code = "DEPENDENCY_NOT_FOUND";
|
|
178519
|
+
constructor(message, options) {
|
|
178520
|
+
super(message, {
|
|
178521
|
+
hints: options?.hints ?? [
|
|
178522
|
+
{ message: "Install the required dependency and try again" }
|
|
178523
|
+
],
|
|
178524
|
+
cause: options?.cause
|
|
178525
|
+
});
|
|
178526
|
+
}
|
|
178527
|
+
}
|
|
178528
|
+
|
|
178517
178529
|
class ApiError extends SystemError {
|
|
178518
178530
|
code = "API_ERROR";
|
|
178519
178531
|
statusCode;
|
|
@@ -178629,6 +178641,21 @@ class FileReadError extends SystemError {
|
|
|
178629
178641
|
});
|
|
178630
178642
|
}
|
|
178631
178643
|
}
|
|
178644
|
+
|
|
178645
|
+
class InternalError extends SystemError {
|
|
178646
|
+
code = "INTERNAL_ERROR";
|
|
178647
|
+
constructor(message, options) {
|
|
178648
|
+
super(message, {
|
|
178649
|
+
hints: options?.hints ?? [
|
|
178650
|
+
{
|
|
178651
|
+
message: "This is an unexpected error. Please report it if it persists."
|
|
178652
|
+
}
|
|
178653
|
+
],
|
|
178654
|
+
cause: options?.cause
|
|
178655
|
+
});
|
|
178656
|
+
}
|
|
178657
|
+
}
|
|
178658
|
+
|
|
178632
178659
|
class TypeGenerationError extends SystemError {
|
|
178633
178660
|
code = "TYPE_GENERATION_ERROR";
|
|
178634
178661
|
constructor(message, entityName, cause) {
|
|
@@ -185915,6 +185942,118 @@ async function pushEntities(entities) {
|
|
|
185915
185942
|
}
|
|
185916
185943
|
return syncEntities(entities);
|
|
185917
185944
|
}
|
|
185945
|
+
// src/core/resources/entity/records-schema.ts
|
|
185946
|
+
var EntityRecordSchema = exports_external.looseObject({
|
|
185947
|
+
id: exports_external.string(),
|
|
185948
|
+
created_date: exports_external.string(),
|
|
185949
|
+
updated_date: exports_external.string(),
|
|
185950
|
+
created_by: exports_external.string().optional()
|
|
185951
|
+
});
|
|
185952
|
+
var DeleteRecordResponseSchema = exports_external.object({
|
|
185953
|
+
success: exports_external.boolean()
|
|
185954
|
+
});
|
|
185955
|
+
|
|
185956
|
+
// src/core/resources/entity/records-api.ts
|
|
185957
|
+
async function listRecords(entityName, options = {}) {
|
|
185958
|
+
const appClient = getAppClient();
|
|
185959
|
+
const searchParams = new URLSearchParams;
|
|
185960
|
+
if (options.filter) {
|
|
185961
|
+
searchParams.set("q", options.filter);
|
|
185962
|
+
}
|
|
185963
|
+
if (options.sort) {
|
|
185964
|
+
searchParams.set("sort", options.sort);
|
|
185965
|
+
}
|
|
185966
|
+
if (options.limit !== undefined) {
|
|
185967
|
+
searchParams.set("limit", String(options.limit));
|
|
185968
|
+
}
|
|
185969
|
+
if (options.skip !== undefined) {
|
|
185970
|
+
searchParams.set("skip", String(options.skip));
|
|
185971
|
+
}
|
|
185972
|
+
if (options.fields) {
|
|
185973
|
+
searchParams.set("fields", options.fields);
|
|
185974
|
+
}
|
|
185975
|
+
let response;
|
|
185976
|
+
try {
|
|
185977
|
+
response = await appClient.get(`admin/entities/${entityName}`, {
|
|
185978
|
+
searchParams
|
|
185979
|
+
});
|
|
185980
|
+
} catch (error48) {
|
|
185981
|
+
throw await ApiError.fromHttpError(error48, "listing records");
|
|
185982
|
+
}
|
|
185983
|
+
const json2 = await response.json();
|
|
185984
|
+
const records = Array.isArray(json2) ? json2 : [];
|
|
185985
|
+
return records.map((record2) => {
|
|
185986
|
+
const result = EntityRecordSchema.safeParse(record2);
|
|
185987
|
+
if (!result.success) {
|
|
185988
|
+
throw new SchemaValidationError("Invalid record in response", result.error);
|
|
185989
|
+
}
|
|
185990
|
+
return result.data;
|
|
185991
|
+
});
|
|
185992
|
+
}
|
|
185993
|
+
async function getRecord(entityName, recordId) {
|
|
185994
|
+
const appClient = getAppClient();
|
|
185995
|
+
let response;
|
|
185996
|
+
try {
|
|
185997
|
+
response = await appClient.get(`admin/entities/${entityName}/${recordId}`);
|
|
185998
|
+
} catch (error48) {
|
|
185999
|
+
throw await ApiError.fromHttpError(error48, "getting record");
|
|
186000
|
+
}
|
|
186001
|
+
const json2 = await response.json();
|
|
186002
|
+
const result = EntityRecordSchema.safeParse(json2);
|
|
186003
|
+
if (!result.success) {
|
|
186004
|
+
throw new SchemaValidationError("Invalid record response", result.error);
|
|
186005
|
+
}
|
|
186006
|
+
return result.data;
|
|
186007
|
+
}
|
|
186008
|
+
async function createRecord(entityName, data) {
|
|
186009
|
+
const appClient = getAppClient();
|
|
186010
|
+
let response;
|
|
186011
|
+
try {
|
|
186012
|
+
response = await appClient.post(`admin/entities/${entityName}`, {
|
|
186013
|
+
json: data
|
|
186014
|
+
});
|
|
186015
|
+
} catch (error48) {
|
|
186016
|
+
throw await ApiError.fromHttpError(error48, "creating record");
|
|
186017
|
+
}
|
|
186018
|
+
const json2 = await response.json();
|
|
186019
|
+
const result = EntityRecordSchema.safeParse(json2);
|
|
186020
|
+
if (!result.success) {
|
|
186021
|
+
throw new SchemaValidationError("Invalid record in create response", result.error);
|
|
186022
|
+
}
|
|
186023
|
+
return result.data;
|
|
186024
|
+
}
|
|
186025
|
+
async function updateRecord(entityName, recordId, data) {
|
|
186026
|
+
const appClient = getAppClient();
|
|
186027
|
+
let response;
|
|
186028
|
+
try {
|
|
186029
|
+
response = await appClient.put(`admin/entities/${entityName}/${recordId}`, {
|
|
186030
|
+
json: data
|
|
186031
|
+
});
|
|
186032
|
+
} catch (error48) {
|
|
186033
|
+
throw await ApiError.fromHttpError(error48, "updating record");
|
|
186034
|
+
}
|
|
186035
|
+
const json2 = await response.json();
|
|
186036
|
+
const result = EntityRecordSchema.safeParse(json2);
|
|
186037
|
+
if (!result.success) {
|
|
186038
|
+
throw new SchemaValidationError("Invalid record in update response", result.error);
|
|
186039
|
+
}
|
|
186040
|
+
return result.data;
|
|
186041
|
+
}
|
|
186042
|
+
async function deleteRecord(entityName, recordId) {
|
|
186043
|
+
const appClient = getAppClient();
|
|
186044
|
+
let response;
|
|
186045
|
+
try {
|
|
186046
|
+
response = await appClient.delete(`admin/entities/${entityName}/${recordId}`);
|
|
186047
|
+
} catch (error48) {
|
|
186048
|
+
throw await ApiError.fromHttpError(error48, "deleting record");
|
|
186049
|
+
}
|
|
186050
|
+
const json2 = await response.json();
|
|
186051
|
+
const result = DeleteRecordResponseSchema.safeParse(json2);
|
|
186052
|
+
if (!result.success) {
|
|
186053
|
+
throw new SchemaValidationError("Invalid delete response", result.error);
|
|
186054
|
+
}
|
|
186055
|
+
return result.data;
|
|
186056
|
+
}
|
|
185918
186057
|
// src/core/resources/entity/resource.ts
|
|
185919
186058
|
var entityResource = {
|
|
185920
186059
|
readAll: readAllEntities,
|
|
@@ -187064,7 +187203,9 @@ var theme = {
|
|
|
187064
187203
|
styles: {
|
|
187065
187204
|
header: source_default.dim,
|
|
187066
187205
|
bold: source_default.bold,
|
|
187067
|
-
dim: source_default.dim
|
|
187206
|
+
dim: source_default.dim,
|
|
187207
|
+
error: source_default.red,
|
|
187208
|
+
warn: source_default.yellow
|
|
187068
187209
|
},
|
|
187069
187210
|
format: {
|
|
187070
187211
|
errorContext(ctx) {
|
|
@@ -193846,7 +193987,7 @@ var {
|
|
|
193846
193987
|
// package.json
|
|
193847
193988
|
var package_default = {
|
|
193848
193989
|
name: "base44",
|
|
193849
|
-
version: "0.0.
|
|
193990
|
+
version: "0.0.33",
|
|
193850
193991
|
description: "Base44 CLI - Unified interface for managing Base44 applications",
|
|
193851
193992
|
type: "module",
|
|
193852
193993
|
bin: {
|
|
@@ -193887,6 +194028,7 @@ var package_default = {
|
|
|
193887
194028
|
"@types/bun": "^1.2.15",
|
|
193888
194029
|
"@types/common-tags": "^1.8.4",
|
|
193889
194030
|
"@types/cors": "^2.8.19",
|
|
194031
|
+
"@types/deno": "^2.5.0",
|
|
193890
194032
|
"@types/ejs": "^3.1.5",
|
|
193891
194033
|
"@types/express": "^5.0.6",
|
|
193892
194034
|
"@types/json-schema": "^7.0.15",
|
|
@@ -194982,9 +195124,152 @@ async function pushEntitiesAction() {
|
|
|
194982
195124
|
return { outroMessage: "Entities pushed to Base44" };
|
|
194983
195125
|
}
|
|
194984
195126
|
function getEntitiesPushCommand(context) {
|
|
194985
|
-
return new Command("
|
|
195127
|
+
return new Command("push").description("Push local entity schemas to Base44").action(async () => {
|
|
194986
195128
|
await runCommand(pushEntitiesAction, { requireAuth: true }, context);
|
|
194987
|
-
})
|
|
195129
|
+
});
|
|
195130
|
+
}
|
|
195131
|
+
|
|
195132
|
+
// src/cli/commands/entities/records/parseRecordData.ts
|
|
195133
|
+
var import_json52 = __toESM(require_lib(), 1);
|
|
195134
|
+
async function parseRecordData(options, exampleHint) {
|
|
195135
|
+
if (options.data && options.file) {
|
|
195136
|
+
throw new InvalidInputError("Cannot use both --data and --file. Choose one.", {
|
|
195137
|
+
hints: [
|
|
195138
|
+
{
|
|
195139
|
+
message: `Pass --data for inline JSON or --file for a JSON file path, not both.`
|
|
195140
|
+
}
|
|
195141
|
+
]
|
|
195142
|
+
});
|
|
195143
|
+
}
|
|
195144
|
+
if (options.data) {
|
|
195145
|
+
try {
|
|
195146
|
+
return import_json52.default.parse(options.data);
|
|
195147
|
+
} catch {
|
|
195148
|
+
throw new InvalidInputError("Invalid JSON in --data flag. Provide valid JSON.", {
|
|
195149
|
+
hints: [{ message: `Example: --data '${exampleHint}'` }]
|
|
195150
|
+
});
|
|
195151
|
+
}
|
|
195152
|
+
}
|
|
195153
|
+
if (options.file) {
|
|
195154
|
+
return readJsonFile(options.file);
|
|
195155
|
+
}
|
|
195156
|
+
throw new InvalidInputError("Provide record data with --data or --file flag", {
|
|
195157
|
+
hints: [
|
|
195158
|
+
{
|
|
195159
|
+
message: `Example: --data '${exampleHint}' or --file record.json`
|
|
195160
|
+
}
|
|
195161
|
+
]
|
|
195162
|
+
});
|
|
195163
|
+
}
|
|
195164
|
+
|
|
195165
|
+
// src/cli/commands/entities/records/create.ts
|
|
195166
|
+
async function createRecordAction(entityName, options) {
|
|
195167
|
+
const data = await parseRecordData(options, '{"name": "John", "email": "john@example.com"}');
|
|
195168
|
+
const record2 = await runTask(`Creating ${entityName} record...`, async () => {
|
|
195169
|
+
return await createRecord(entityName, data);
|
|
195170
|
+
}, {
|
|
195171
|
+
successMessage: `Created ${entityName} record`,
|
|
195172
|
+
errorMessage: `Failed to create ${entityName} record`
|
|
195173
|
+
});
|
|
195174
|
+
R2.info(JSON.stringify(record2, null, 2));
|
|
195175
|
+
return { outroMessage: `Record created with ID: ${record2.id}` };
|
|
195176
|
+
}
|
|
195177
|
+
function getRecordsCreateCommand(context) {
|
|
195178
|
+
return new Command("create").description("Create a new entity record").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").option("-d, --data <json>", "JSON object with record data").option("--file <path>", "Read record data from a JSON/JSONC file").action(async (entityName, options) => {
|
|
195179
|
+
await runCommand(() => createRecordAction(entityName, options), { requireAuth: true }, context);
|
|
195180
|
+
});
|
|
195181
|
+
}
|
|
195182
|
+
|
|
195183
|
+
// src/cli/commands/entities/records/delete.ts
|
|
195184
|
+
async function deleteRecordAction(entityName, recordId, options) {
|
|
195185
|
+
if (!options.yes) {
|
|
195186
|
+
const confirmed = await Re({
|
|
195187
|
+
message: `Delete ${entityName} record ${recordId}?`
|
|
195188
|
+
});
|
|
195189
|
+
if (confirmed !== true) {
|
|
195190
|
+
throw new CLIExitError(0);
|
|
195191
|
+
}
|
|
195192
|
+
}
|
|
195193
|
+
await runTask(`Deleting ${entityName} record...`, async () => {
|
|
195194
|
+
return await deleteRecord(entityName, recordId);
|
|
195195
|
+
}, {
|
|
195196
|
+
successMessage: `Deleted ${entityName} record`,
|
|
195197
|
+
errorMessage: `Failed to delete ${entityName} record`
|
|
195198
|
+
});
|
|
195199
|
+
return { outroMessage: `Record ${recordId} deleted` };
|
|
195200
|
+
}
|
|
195201
|
+
function getRecordsDeleteCommand(context) {
|
|
195202
|
+
return new Command("delete").description("Delete an entity record").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").argument("<record-id>", "ID of the record to delete").option("-y, --yes", "Skip confirmation prompt").action(async (entityName, recordId, options) => {
|
|
195203
|
+
await runCommand(() => deleteRecordAction(entityName, recordId, options), { requireAuth: true }, context);
|
|
195204
|
+
});
|
|
195205
|
+
}
|
|
195206
|
+
|
|
195207
|
+
// src/cli/commands/entities/records/get.ts
|
|
195208
|
+
async function getRecordAction(entityName, recordId) {
|
|
195209
|
+
const record2 = await runTask(`Fetching ${entityName} record...`, async () => {
|
|
195210
|
+
return await getRecord(entityName, recordId);
|
|
195211
|
+
}, {
|
|
195212
|
+
successMessage: `Fetched ${entityName} record`,
|
|
195213
|
+
errorMessage: `Failed to fetch ${entityName} record`
|
|
195214
|
+
});
|
|
195215
|
+
R2.info(JSON.stringify(record2, null, 2));
|
|
195216
|
+
return {};
|
|
195217
|
+
}
|
|
195218
|
+
function getRecordsGetCommand(context) {
|
|
195219
|
+
return new Command("get").description("Get a single entity record by ID").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").argument("<record-id>", "ID of the record").action(async (entityName, recordId) => {
|
|
195220
|
+
await runCommand(() => getRecordAction(entityName, recordId), { requireAuth: true }, context);
|
|
195221
|
+
});
|
|
195222
|
+
}
|
|
195223
|
+
|
|
195224
|
+
// src/cli/commands/entities/records/list.ts
|
|
195225
|
+
async function listRecordsAction(entityName, options) {
|
|
195226
|
+
const records = await runTask(`Fetching ${entityName} records...`, async () => {
|
|
195227
|
+
return await listRecords(entityName, {
|
|
195228
|
+
filter: options.filter,
|
|
195229
|
+
sort: options.sort,
|
|
195230
|
+
limit: options.limit ? Number(options.limit) : 50,
|
|
195231
|
+
skip: options.skip ? Number(options.skip) : undefined,
|
|
195232
|
+
fields: options.fields
|
|
195233
|
+
});
|
|
195234
|
+
}, {
|
|
195235
|
+
successMessage: `Fetched ${entityName} records`,
|
|
195236
|
+
errorMessage: `Failed to fetch ${entityName} records`
|
|
195237
|
+
});
|
|
195238
|
+
R2.info(JSON.stringify(records, null, 2));
|
|
195239
|
+
return { outroMessage: `Found ${records.length} ${entityName} record(s)` };
|
|
195240
|
+
}
|
|
195241
|
+
function getRecordsListCommand(context) {
|
|
195242
|
+
return new Command("list").description("List entity records").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").option("-f, --filter <json>", `JSON filter object (e.g. '{"status":"active"}' or '{"age":{"$gt":18}}')`).option("-s, --sort <field>", "Sort field name, prefix with - for descending (e.g. -created_date)").option("-l, --limit <n>", "Max number of records to return", "50").option("--skip <n>", "Number of records to skip (for pagination)").option("--fields <fields>", "Comma-separated list of fields to return (e.g. id,name,email)").action(async (entityName, options) => {
|
|
195243
|
+
await runCommand(() => listRecordsAction(entityName, options), { requireAuth: true }, context);
|
|
195244
|
+
});
|
|
195245
|
+
}
|
|
195246
|
+
|
|
195247
|
+
// src/cli/commands/entities/records/update.ts
|
|
195248
|
+
async function updateRecordAction(entityName, recordId, options) {
|
|
195249
|
+
const data = await parseRecordData(options, '{"status": "active"}');
|
|
195250
|
+
const record2 = await runTask(`Updating ${entityName} record...`, async () => {
|
|
195251
|
+
return await updateRecord(entityName, recordId, data);
|
|
195252
|
+
}, {
|
|
195253
|
+
successMessage: `Updated ${entityName} record`,
|
|
195254
|
+
errorMessage: `Failed to update ${entityName} record`
|
|
195255
|
+
});
|
|
195256
|
+
R2.info(JSON.stringify(record2, null, 2));
|
|
195257
|
+
return { outroMessage: `Record ${recordId} updated` };
|
|
195258
|
+
}
|
|
195259
|
+
function getRecordsUpdateCommand(context) {
|
|
195260
|
+
return new Command("update").description("Update an entity record").argument("<entity-name>", "Name of the entity (e.g. Users, Products)").argument("<record-id>", "ID of the record to update").option("-d, --data <json>", "JSON object with fields to update").option("--file <path>", "Read update data from a JSON/JSONC file").action(async (entityName, recordId, options) => {
|
|
195261
|
+
await runCommand(() => updateRecordAction(entityName, recordId, options), { requireAuth: true }, context);
|
|
195262
|
+
});
|
|
195263
|
+
}
|
|
195264
|
+
|
|
195265
|
+
// src/cli/commands/entities/records/index.ts
|
|
195266
|
+
function getRecordsCommand(context) {
|
|
195267
|
+
return new Command("records").description("CRUD operations on entity records").addCommand(getRecordsListCommand(context)).addCommand(getRecordsGetCommand(context)).addCommand(getRecordsCreateCommand(context)).addCommand(getRecordsUpdateCommand(context)).addCommand(getRecordsDeleteCommand(context));
|
|
195268
|
+
}
|
|
195269
|
+
|
|
195270
|
+
// src/cli/commands/entities/index.ts
|
|
195271
|
+
function getEntitiesCommand(context) {
|
|
195272
|
+
return new Command("entities").description("Manage project entities").addCommand(getEntitiesPushCommand(context)).addCommand(getRecordsCommand(context));
|
|
194988
195273
|
}
|
|
194989
195274
|
|
|
194990
195275
|
// src/cli/commands/functions/deploy.ts
|
|
@@ -195610,9 +195895,12 @@ function getTypesCommand(context) {
|
|
|
195610
195895
|
return new Command("types").description("Manage TypeScript type generation").addCommand(getTypesGenerateCommand(context));
|
|
195611
195896
|
}
|
|
195612
195897
|
|
|
195898
|
+
// src/cli/commands/dev.ts
|
|
195899
|
+
import { dirname as dirname12, join as join16 } from "node:path";
|
|
195900
|
+
|
|
195613
195901
|
// src/cli/dev/dev-server/main.ts
|
|
195614
195902
|
var import_cors = __toESM(require_lib4(), 1);
|
|
195615
|
-
var
|
|
195903
|
+
var import_express2 = __toESM(require_express(), 1);
|
|
195616
195904
|
|
|
195617
195905
|
// node_modules/get-port/index.js
|
|
195618
195906
|
import net from "node:net";
|
|
@@ -195729,13 +196017,232 @@ async function getPorts(options8) {
|
|
|
195729
196017
|
}
|
|
195730
196018
|
|
|
195731
196019
|
// src/cli/dev/dev-server/main.ts
|
|
196020
|
+
var import_http_proxy_middleware2 = __toESM(require_dist2(), 1);
|
|
196021
|
+
|
|
196022
|
+
// src/cli/dev/createDevLogger.ts
|
|
196023
|
+
var colorByType = {
|
|
196024
|
+
error: theme.styles.error,
|
|
196025
|
+
warn: theme.styles.warn,
|
|
196026
|
+
log: (text) => text
|
|
196027
|
+
};
|
|
196028
|
+
function createDevLogger() {
|
|
196029
|
+
const print = (type, msg) => {
|
|
196030
|
+
const colorize = colorByType[type];
|
|
196031
|
+
console[type](colorize(msg));
|
|
196032
|
+
};
|
|
196033
|
+
return {
|
|
196034
|
+
log: (msg) => print("log", msg),
|
|
196035
|
+
error: (msg, err) => {
|
|
196036
|
+
print("error", msg);
|
|
196037
|
+
if (err) {
|
|
196038
|
+
print("error", String(err));
|
|
196039
|
+
}
|
|
196040
|
+
},
|
|
196041
|
+
warn: (msg) => print("warn", msg)
|
|
196042
|
+
};
|
|
196043
|
+
}
|
|
196044
|
+
|
|
196045
|
+
// src/cli/dev/dev-server/function-manager.ts
|
|
196046
|
+
import { spawn as spawn2, spawnSync as spawnSync2 } from "node:child_process";
|
|
196047
|
+
import { dirname as dirname11, join as join15 } from "node:path";
|
|
196048
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
196049
|
+
var __dirname5 = dirname11(fileURLToPath7(import.meta.url));
|
|
196050
|
+
var WRAPPER_PATH = join15(__dirname5, "../deno-runtime/main.js");
|
|
196051
|
+
var READY_TIMEOUT = 30000;
|
|
196052
|
+
|
|
196053
|
+
class FunctionManager {
|
|
196054
|
+
functions;
|
|
196055
|
+
running = new Map;
|
|
196056
|
+
starting = new Map;
|
|
196057
|
+
logger;
|
|
196058
|
+
constructor(functions, logger) {
|
|
196059
|
+
this.functions = new Map(functions.map((f7) => [f7.name, f7]));
|
|
196060
|
+
this.logger = logger;
|
|
196061
|
+
if (functions.length > 0) {
|
|
196062
|
+
this.verifyDenoIsInstalled();
|
|
196063
|
+
}
|
|
196064
|
+
}
|
|
196065
|
+
verifyDenoIsInstalled() {
|
|
196066
|
+
const result = spawnSync2("deno", ["--version"]);
|
|
196067
|
+
if (result.error) {
|
|
196068
|
+
throw new DependencyNotFoundError("Deno is required to run functions", {
|
|
196069
|
+
hints: [{ message: "Install Deno from https://deno.com/download" }]
|
|
196070
|
+
});
|
|
196071
|
+
}
|
|
196072
|
+
}
|
|
196073
|
+
getFunctionNames() {
|
|
196074
|
+
return Array.from(this.functions.keys());
|
|
196075
|
+
}
|
|
196076
|
+
async ensureRunning(name2) {
|
|
196077
|
+
const backendFunction = this.functions.get(name2);
|
|
196078
|
+
if (!backendFunction) {
|
|
196079
|
+
throw new InvalidInputError(`Function "${name2}" not found`, {
|
|
196080
|
+
hints: [{ message: "Check available functions in your project" }]
|
|
196081
|
+
});
|
|
196082
|
+
}
|
|
196083
|
+
const existing = this.running.get(name2);
|
|
196084
|
+
if (existing?.ready) {
|
|
196085
|
+
return existing.port;
|
|
196086
|
+
}
|
|
196087
|
+
const pending = this.starting.get(name2);
|
|
196088
|
+
if (pending) {
|
|
196089
|
+
return pending;
|
|
196090
|
+
}
|
|
196091
|
+
const promise2 = this.startFunction(name2, backendFunction);
|
|
196092
|
+
this.starting.set(name2, promise2);
|
|
196093
|
+
try {
|
|
196094
|
+
return await promise2;
|
|
196095
|
+
} finally {
|
|
196096
|
+
this.starting.delete(name2);
|
|
196097
|
+
}
|
|
196098
|
+
}
|
|
196099
|
+
async startFunction(name2, backendFunction) {
|
|
196100
|
+
const port = await this.allocatePort();
|
|
196101
|
+
const process21 = this.spawnFunction(backendFunction, port);
|
|
196102
|
+
const runningFunc = {
|
|
196103
|
+
process: process21,
|
|
196104
|
+
port,
|
|
196105
|
+
ready: false
|
|
196106
|
+
};
|
|
196107
|
+
this.running.set(name2, runningFunc);
|
|
196108
|
+
this.setupProcessHandlers(name2, process21);
|
|
196109
|
+
return this.waitForReady(name2, runningFunc);
|
|
196110
|
+
}
|
|
196111
|
+
stopAll() {
|
|
196112
|
+
for (const [name2, { process: process21 }] of this.running) {
|
|
196113
|
+
this.logger.log(`[dev-server] Stopping function: ${name2}`);
|
|
196114
|
+
process21.kill();
|
|
196115
|
+
}
|
|
196116
|
+
this.running.clear();
|
|
196117
|
+
this.starting.clear();
|
|
196118
|
+
}
|
|
196119
|
+
async allocatePort() {
|
|
196120
|
+
const usedPorts = Array.from(this.running.values()).map((r5) => r5.port);
|
|
196121
|
+
return getPorts({ exclude: usedPorts });
|
|
196122
|
+
}
|
|
196123
|
+
spawnFunction(func, port) {
|
|
196124
|
+
this.logger.log(`[dev-server] Spawning function "${func.name}" on port ${port}`);
|
|
196125
|
+
const process21 = spawn2("deno", ["run", "--allow-all", WRAPPER_PATH], {
|
|
196126
|
+
env: {
|
|
196127
|
+
...globalThis.process.env,
|
|
196128
|
+
FUNCTION_PATH: func.entryPath,
|
|
196129
|
+
FUNCTION_PORT: String(port),
|
|
196130
|
+
FUNCTION_NAME: func.name
|
|
196131
|
+
},
|
|
196132
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
196133
|
+
});
|
|
196134
|
+
return process21;
|
|
196135
|
+
}
|
|
196136
|
+
setupProcessHandlers(name2, process21) {
|
|
196137
|
+
process21.stdout?.on("data", (data) => {
|
|
196138
|
+
const lines = data.toString().trim().split(`
|
|
196139
|
+
`);
|
|
196140
|
+
for (const line3 of lines) {
|
|
196141
|
+
this.logger.log(line3);
|
|
196142
|
+
}
|
|
196143
|
+
});
|
|
196144
|
+
process21.stderr?.on("data", (data) => {
|
|
196145
|
+
const lines = data.toString().trim().split(`
|
|
196146
|
+
`);
|
|
196147
|
+
for (const line3 of lines) {
|
|
196148
|
+
this.logger.error(line3);
|
|
196149
|
+
}
|
|
196150
|
+
});
|
|
196151
|
+
process21.on("exit", (code2) => {
|
|
196152
|
+
this.logger.log(`[dev-server] Function "${name2}" exited with code ${code2}`);
|
|
196153
|
+
this.running.delete(name2);
|
|
196154
|
+
});
|
|
196155
|
+
process21.on("error", (error48) => {
|
|
196156
|
+
this.logger.error(`[dev-server] Function "${name2}" error:`, error48);
|
|
196157
|
+
this.running.delete(name2);
|
|
196158
|
+
});
|
|
196159
|
+
}
|
|
196160
|
+
waitForReady(name2, runningFunc) {
|
|
196161
|
+
return new Promise((resolve5, reject) => {
|
|
196162
|
+
runningFunc.process.on("exit", (code2) => {
|
|
196163
|
+
if (!runningFunc.ready) {
|
|
196164
|
+
clearTimeout(timeout3);
|
|
196165
|
+
reject(new InternalError(`Function "${name2}" exited with code ${code2}`, {
|
|
196166
|
+
hints: [{ message: "Check the function code for errors" }]
|
|
196167
|
+
}));
|
|
196168
|
+
}
|
|
196169
|
+
});
|
|
196170
|
+
const timeout3 = setTimeout(() => {
|
|
196171
|
+
runningFunc.process.kill();
|
|
196172
|
+
reject(new InternalError(`Function "${name2}" failed to start within ${READY_TIMEOUT / 1000}s timeout`, {
|
|
196173
|
+
hints: [
|
|
196174
|
+
{ message: "Check the function code for startup errors" }
|
|
196175
|
+
]
|
|
196176
|
+
}));
|
|
196177
|
+
}, READY_TIMEOUT);
|
|
196178
|
+
const onData = (data) => {
|
|
196179
|
+
const output = data.toString();
|
|
196180
|
+
if (output.includes("Listening on")) {
|
|
196181
|
+
runningFunc.ready = true;
|
|
196182
|
+
clearTimeout(timeout3);
|
|
196183
|
+
runningFunc.process.stdout?.off("data", onData);
|
|
196184
|
+
resolve5(runningFunc.port);
|
|
196185
|
+
}
|
|
196186
|
+
};
|
|
196187
|
+
runningFunc.process.stdout?.on("data", onData);
|
|
196188
|
+
});
|
|
196189
|
+
}
|
|
196190
|
+
}
|
|
196191
|
+
|
|
196192
|
+
// src/cli/dev/dev-server/routes/functions.ts
|
|
196193
|
+
var import_express = __toESM(require_express(), 1);
|
|
195732
196194
|
var import_http_proxy_middleware = __toESM(require_dist2(), 1);
|
|
196195
|
+
import { ServerResponse } from "node:http";
|
|
196196
|
+
function createFunctionRouter(manager, logger) {
|
|
196197
|
+
const router = import_express.Router({ mergeParams: true });
|
|
196198
|
+
const portsByRequest = new WeakMap;
|
|
196199
|
+
const proxy = import_http_proxy_middleware.createProxyMiddleware({
|
|
196200
|
+
router: (req) => `http://localhost:${portsByRequest.get(req)}`,
|
|
196201
|
+
changeOrigin: true,
|
|
196202
|
+
on: {
|
|
196203
|
+
proxyReq: (proxyReq, req) => {
|
|
196204
|
+
const xAppId = req.headers["x-app-id"];
|
|
196205
|
+
if (xAppId) {
|
|
196206
|
+
proxyReq.setHeader("Base44-App-Id", xAppId);
|
|
196207
|
+
}
|
|
196208
|
+
proxyReq.setHeader("Base44-Api-Url", `${req.protocol}://${req.headers.host}`);
|
|
196209
|
+
},
|
|
196210
|
+
error: (err, _req, res) => {
|
|
196211
|
+
logger.error("Function proxy error:", err);
|
|
196212
|
+
if (res instanceof ServerResponse && !res.headersSent) {
|
|
196213
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
196214
|
+
res.end(JSON.stringify({
|
|
196215
|
+
error: "Failed to proxy request to function",
|
|
196216
|
+
details: err.message
|
|
196217
|
+
}));
|
|
196218
|
+
}
|
|
196219
|
+
}
|
|
196220
|
+
}
|
|
196221
|
+
});
|
|
196222
|
+
router.all("/:functionName", async (req, res, next) => {
|
|
196223
|
+
const { functionName } = req.params;
|
|
196224
|
+
try {
|
|
196225
|
+
const port = await manager.ensureRunning(functionName);
|
|
196226
|
+
portsByRequest.set(req, port);
|
|
196227
|
+
next();
|
|
196228
|
+
} catch (error48) {
|
|
196229
|
+
logger.error("Function error:", error48);
|
|
196230
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
196231
|
+
res.status(500).json({ error: message });
|
|
196232
|
+
}
|
|
196233
|
+
}, proxy);
|
|
196234
|
+
return router;
|
|
196235
|
+
}
|
|
196236
|
+
|
|
196237
|
+
// src/cli/dev/dev-server/main.ts
|
|
195733
196238
|
var DEFAULT_PORT = 4400;
|
|
195734
196239
|
var BASE44_APP_URL = "https://base44.app";
|
|
195735
|
-
async function createDevServer(options8
|
|
195736
|
-
const
|
|
195737
|
-
const
|
|
195738
|
-
const
|
|
196240
|
+
async function createDevServer(options8) {
|
|
196241
|
+
const { port: userPort } = options8;
|
|
196242
|
+
const port = userPort ?? await getPorts({ port: DEFAULT_PORT });
|
|
196243
|
+
const { functions } = await options8.loadResources();
|
|
196244
|
+
const app = import_express2.default();
|
|
196245
|
+
const remoteProxy = import_http_proxy_middleware2.createProxyMiddleware({
|
|
195739
196246
|
target: BASE44_APP_URL,
|
|
195740
196247
|
changeOrigin: true
|
|
195741
196248
|
});
|
|
@@ -195751,6 +196258,13 @@ async function createDevServer(options8 = {}) {
|
|
|
195751
196258
|
}
|
|
195752
196259
|
next();
|
|
195753
196260
|
});
|
|
196261
|
+
const devLogger = createDevLogger();
|
|
196262
|
+
const functionManager = new FunctionManager(functions, devLogger);
|
|
196263
|
+
if (functionManager.getFunctionNames().length > 0) {
|
|
196264
|
+
R2.info(`Loaded functions: ${functionManager.getFunctionNames().join(", ")}`);
|
|
196265
|
+
const functionRoutes = createFunctionRouter(functionManager, devLogger);
|
|
196266
|
+
app.use("/api/apps/:appId/functions", functionRoutes);
|
|
196267
|
+
}
|
|
195754
196268
|
app.use((req, res, next) => {
|
|
195755
196269
|
return remoteProxy(req, res, next);
|
|
195756
196270
|
});
|
|
@@ -195763,6 +196277,12 @@ async function createDevServer(options8 = {}) {
|
|
|
195763
196277
|
reject(err);
|
|
195764
196278
|
}
|
|
195765
196279
|
} else {
|
|
196280
|
+
const shutdown = () => {
|
|
196281
|
+
functionManager.stopAll();
|
|
196282
|
+
server.close();
|
|
196283
|
+
};
|
|
196284
|
+
process.on("SIGINT", shutdown);
|
|
196285
|
+
process.on("SIGTERM", shutdown);
|
|
195766
196286
|
resolve5({
|
|
195767
196287
|
port,
|
|
195768
196288
|
server
|
|
@@ -195775,7 +196295,15 @@ async function createDevServer(options8 = {}) {
|
|
|
195775
196295
|
// src/cli/commands/dev.ts
|
|
195776
196296
|
async function devAction(options8) {
|
|
195777
196297
|
const port = options8.port ? Number(options8.port) : undefined;
|
|
195778
|
-
const { port: resolvedPort } = await createDevServer({
|
|
196298
|
+
const { port: resolvedPort } = await createDevServer({
|
|
196299
|
+
port,
|
|
196300
|
+
loadResources: async () => {
|
|
196301
|
+
const { project: project2 } = await readProjectConfig();
|
|
196302
|
+
const configDir = dirname12(project2.configPath);
|
|
196303
|
+
const functions = await functionResource.readAll(join16(configDir, project2.functionsDir));
|
|
196304
|
+
return { functions };
|
|
196305
|
+
}
|
|
196306
|
+
});
|
|
195779
196307
|
return {
|
|
195780
196308
|
outroMessage: `Dev server is available at ${theme.colors.links(`http://localhost:${resolvedPort}`)}`
|
|
195781
196309
|
};
|
|
@@ -195892,7 +196420,7 @@ function createProgram(context) {
|
|
|
195892
196420
|
program2.addCommand(getDeployCommand(context));
|
|
195893
196421
|
program2.addCommand(getLinkCommand(context));
|
|
195894
196422
|
program2.addCommand(getEjectCommand(context));
|
|
195895
|
-
program2.addCommand(
|
|
196423
|
+
program2.addCommand(getEntitiesCommand(context));
|
|
195896
196424
|
program2.addCommand(getAgentsCommand(context));
|
|
195897
196425
|
program2.addCommand(getConnectorsCommand(context));
|
|
195898
196426
|
program2.addCommand(getFunctionsDeployCommand(context));
|
|
@@ -195937,7 +196465,7 @@ function nanoid3(size = 21) {
|
|
|
195937
196465
|
}
|
|
195938
196466
|
|
|
195939
196467
|
// node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
195940
|
-
import { dirname as
|
|
196468
|
+
import { dirname as dirname13, posix, sep } from "path";
|
|
195941
196469
|
function createModulerModifier() {
|
|
195942
196470
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
195943
196471
|
return async (frames) => {
|
|
@@ -195946,7 +196474,7 @@ function createModulerModifier() {
|
|
|
195946
196474
|
return frames;
|
|
195947
196475
|
};
|
|
195948
196476
|
}
|
|
195949
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
196477
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname13(process.argv[1]) : process.cwd(), isWindows4 = sep === "\\") {
|
|
195950
196478
|
const normalizedBase = isWindows4 ? normalizeWindowsPath2(basePath) : basePath;
|
|
195951
196479
|
return (filename) => {
|
|
195952
196480
|
if (!filename)
|
|
@@ -200163,4 +200691,4 @@ export {
|
|
|
200163
200691
|
CLIExitError
|
|
200164
200692
|
};
|
|
200165
200693
|
|
|
200166
|
-
//# debugId=
|
|
200694
|
+
//# debugId=CC4F575130E0CA5564756E2164756E21
|