@base44-preview/cli 0.1.32-pr.663.16bdb8e → 0.1.32-pr.663.1a6cb8e

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/cli/index.js CHANGED
@@ -39570,9 +39570,9 @@ function tm(e, t, r) {
39570
39570
  return l(["<", r("name"), r("typeArguments"), " ", ...e.map(r, "attributes"), n.selfClosing ? " />" : ">"]);
39571
39571
  let i = n.attributes?.some((u) => V3(u.value) && u.value.value.includes(`
39572
39572
  `)), o = t.singleAttributePerLine && n.attributes.length > 1 ? E : A;
39573
- return l(["<", r("name"), r("typeArguments"), m(e.map(() => [o, r()], "attributes")), ...rm2(n, t, s)], { shouldBreak: i });
39573
+ return l(["<", r("name"), r("typeArguments"), m(e.map(() => [o, r()], "attributes")), ...rm3(n, t, s)], { shouldBreak: i });
39574
39574
  }
39575
- function rm2(e, t, r) {
39575
+ function rm3(e, t, r) {
39576
39576
  return e.selfClosing ? [A, "/>"] : nm(e, t, r) ? [">"] : [f, ">"];
39577
39577
  }
39578
39578
  function nm(e, t, r) {
@@ -111521,7 +111521,7 @@ function N62(e) {
111521
111521
  function nm2(e, t, a) {
111522
111522
  return ie10(t, a, e.typeParameters) || ie10(t, a, e.parameters) || S7(t, e.type);
111523
111523
  }
111524
- function rm3(e, t, a) {
111524
+ function rm4(e, t, a) {
111525
111525
  return ie10(t, a, e.types);
111526
111526
  }
111527
111527
  function im2(e, t, a) {
@@ -114489,7 +114489,7 @@ ${Me.join(`
114489
114489
  return S7(a, t.elementType);
114490
114490
  }, 190: function(t, a, _) {
114491
114491
  return ie10(a, _, t.elements);
114492
- }, 193: rm3, 194: rm3, 195: function(t, a, _) {
114492
+ }, 193: rm4, 194: rm4, 195: function(t, a, _) {
114493
114493
  return S7(a, t.checkType) || S7(a, t.extendsType) || S7(a, t.trueType) || S7(a, t.falseType);
114494
114494
  }, 196: function(t, a, _) {
114495
114495
  return S7(a, t.typeParameter);
@@ -239451,7 +239451,7 @@ var package_default = {
239451
239451
  jsonwebtoken: "^9.0.3",
239452
239452
  ky: "^1.14.2",
239453
239453
  lodash: "^4.17.23",
239454
- minisearch: "7.1.2",
239454
+ minisearch: "^7.1.2",
239455
239455
  ms: "^2.1.3",
239456
239456
  multer: "^2.0.0",
239457
239457
  nanoid: "^5.1.6",
@@ -249234,7 +249234,7 @@ var ParameterSchema = looseObject({
249234
249234
  example: unknown().optional()
249235
249235
  });
249236
249236
  var MediaTypesSchema = record(string2(), looseObject({ schema: unknown().optional() }));
249237
- var RequestBodySchema = looseObject({
249237
+ var MediaContentSchema = looseObject({
249238
249238
  content: MediaTypesSchema.optional()
249239
249239
  });
249240
249240
  var ResponseObjectSchema = looseObject({
@@ -249251,18 +249251,22 @@ var OperationSchema = looseObject({
249251
249251
  responses: record(string2(), unknown()).optional(),
249252
249252
  security: array(SecurityRequirementSchema).optional()
249253
249253
  });
249254
- var PathItemSchema = looseObject({
249255
- ...Object.fromEntries(HTTP_METHODS.map((method) => [method, OperationSchema.optional()])),
249256
- parameters: array(unknown()).optional()
249257
- });
249258
249254
  var OpenApiSpecSchema = looseObject({
249259
249255
  openapi: string2(),
249260
- paths: record(string2(), PathItemSchema),
249256
+ paths: record(string2(), record(string2(), unknown())),
249261
249257
  components: looseObject({
249262
249258
  schemas: record(string2(), unknown()).optional()
249263
249259
  }).optional(),
249264
- security: array(SecurityRequirementSchema).optional()
249260
+ security: array(SecurityRequirementSchema).optional().catch(undefined)
249265
249261
  });
249262
+ var ParameterListSchema = array(unknown());
249263
+
249264
+ // src/core/api-spec/text.ts
249265
+ var DOCS_CALLOUT = /<(Info|Note|Tip|Check|Warning)>([\s\S]*?)<\/\1>/g;
249266
+ var RELATIVE_LINK = /\[([^\]]+)\]\(\/[^)]*\)/g;
249267
+ function cleanText(text) {
249268
+ return text.replace(DOCS_CALLOUT, (_, kind, body) => kind === "Warning" ? `Warning: ${body}` : body).replace(RELATIVE_LINK, "$1");
249269
+ }
249266
249270
 
249267
249271
  // src/core/api-spec/operations.ts
249268
249272
  var PERSONAL_ACCESS_TOKEN_SCHEME = "PersonalAccessTokenAuth";
@@ -249274,9 +249278,19 @@ function isReadOnly(operation, spec) {
249274
249278
  function parameterText(operation) {
249275
249279
  return (operation.parameters ?? []).flatMap((parameter) => {
249276
249280
  const parsed = ParameterSchema.safeParse(parameter);
249277
- return parsed.success ? [parsed.data.name, parsed.data.description ?? ""] : [];
249281
+ return parsed.success ? [parsed.data.name, cleanText(parsed.data.description ?? "")] : [];
249278
249282
  }).join(" ");
249279
249283
  }
249284
+ var COMPONENT_REF = /^#\/components\/([^/]+)\/([^/]+)$/;
249285
+ function resolveComponentRef(spec, value) {
249286
+ const ref = value !== null && typeof value === "object" ? value.$ref : undefined;
249287
+ const match = typeof ref === "string" ? COMPONENT_REF.exec(ref) : null;
249288
+ if (!match?.[1] || !match[2]) {
249289
+ return value;
249290
+ }
249291
+ const components = spec.components;
249292
+ return components?.[match[1]]?.[match[2]] ?? value;
249293
+ }
249280
249294
  function mergeParameters(shared, own) {
249281
249295
  if (!shared?.length) {
249282
249296
  return own;
@@ -249293,13 +249307,15 @@ function mergeParameters(shared, own) {
249293
249307
  }
249294
249308
  function listOperations(spec) {
249295
249309
  return Object.entries(spec.paths).flatMap(([path, item]) => HTTP_METHODS.flatMap((method) => {
249296
- const declared = item[method];
249297
- if (!declared) {
249310
+ const parsed = OperationSchema.safeParse(item[method]);
249311
+ if (!parsed.success) {
249298
249312
  return [];
249299
249313
  }
249314
+ const shared = ParameterListSchema.safeParse(item.parameters);
249315
+ const resolve = (list) => list?.map((parameter) => resolveComponentRef(spec, parameter));
249300
249316
  const operation = {
249301
- ...declared,
249302
- parameters: mergeParameters(item.parameters, declared.parameters)
249317
+ ...parsed.data,
249318
+ parameters: mergeParameters(resolve(shared.success ? shared.data : undefined), resolve(parsed.data.parameters))
249303
249319
  };
249304
249320
  return [
249305
249321
  {
@@ -249307,7 +249323,7 @@ function listOperations(spec) {
249307
249323
  method: method.toUpperCase(),
249308
249324
  path,
249309
249325
  summary: operation.summary ?? "",
249310
- description: operation.description ?? "",
249326
+ description: cleanText(operation.description ?? ""),
249311
249327
  parameterText: parameterText(operation),
249312
249328
  readOnly: isReadOnly(operation, spec),
249313
249329
  operation
@@ -249317,22 +249333,14 @@ function listOperations(spec) {
249317
249333
  }
249318
249334
  function pathSegments(path) {
249319
249335
  const pathname = path.split(/[?#]/, 1)[0] ?? "";
249320
- return pathname.split("/").filter(Boolean);
249336
+ return pathname.replace(/^\/+|\/+$/g, "").split("/");
249321
249337
  }
249322
249338
  function segmentPattern(segment) {
249323
249339
  const source = segment.split(/(\{[^}]+\})/).map((part) => part.startsWith("{") ? "[^/]+" : part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("");
249324
249340
  return new RegExp(`^${source}$`);
249325
249341
  }
249326
- function specificity(templateSegments) {
249327
- return templateSegments.map((segment) => segment.includes("{") ? 0 : 1);
249328
- }
249329
- function compareSpecificity(a, b) {
249330
- for (let i = 0;i < a.length; i++) {
249331
- if (a[i] !== b[i]) {
249332
- return (b[i] ?? 0) - (a[i] ?? 0);
249333
- }
249334
- }
249335
- return 0;
249342
+ function literalSegments(path) {
249343
+ return pathSegments(path).map((segment) => segment.includes("{") ? "0" : "1").join("");
249336
249344
  }
249337
249345
  function matchOperation(operations, method, concretePath) {
249338
249346
  const upperMethod = method.toUpperCase();
@@ -249344,11 +249352,12 @@ function matchOperation(operations, method, concretePath) {
249344
249352
  const template = pathSegments(op.path);
249345
249353
  return template.length === segments.length && template.every((part, i) => segmentPattern(part).test(segments[i] ?? ""));
249346
249354
  });
249347
- return candidates.sort((a, b) => compareSpecificity(specificity(pathSegments(a.path)), specificity(pathSegments(b.path))))[0];
249355
+ return candidates.sort((a, b) => literalSegments(b.path).localeCompare(literalSegments(a.path)))[0];
249348
249356
  }
249357
+
249358
+ // src/core/api-spec/contract.ts
249349
249359
  var SCHEMA_REF_PREFIX = "#/components/schemas/";
249350
249360
  var MAX_REF_DEPTH = 4;
249351
- var FRAMEWORK_VALIDATION_ERROR = "Validation Error";
249352
249361
  function inlineRefs(node, schemas, seen = new Set, depth = 0) {
249353
249362
  if (depth > MAX_REF_DEPTH) {
249354
249363
  return node;
@@ -249373,12 +249382,8 @@ function inlineRefs(node, schemas, seen = new Set, depth = 0) {
249373
249382
  inlineRefs(value, schemas, seen, depth)
249374
249383
  ]));
249375
249384
  }
249376
- function jsonSchemaOf(value) {
249377
- const parsed = RequestBodySchema.safeParse(value);
249378
- return parsed.success ? parsed.data.content?.["application/json"]?.schema : undefined;
249379
- }
249380
- function requestBodyOf(value) {
249381
- const parsed = RequestBodySchema.safeParse(value);
249385
+ function bodyOf(value) {
249386
+ const parsed = MediaContentSchema.safeParse(value);
249382
249387
  const content = parsed.success ? parsed.data.content ?? {} : {};
249383
249388
  const contentType = "application/json" in content ? "application/json" : Object.keys(content)[0];
249384
249389
  return contentType ? { contentType, schema: content[contentType]?.schema } : {};
@@ -249392,8 +249397,10 @@ function leftoverRefs(node, schemas, found = new Map) {
249392
249397
  for (const [key, value] of Object.entries(node)) {
249393
249398
  if (key === "$ref" && typeof value === "string" && value.startsWith(SCHEMA_REF_PREFIX) && !found.has(value)) {
249394
249399
  const schema = schemas[value.slice(SCHEMA_REF_PREFIX.length)];
249395
- found.set(value, schema);
249396
- leftoverRefs(schema, schemas, found);
249400
+ if (schema !== undefined) {
249401
+ found.set(value, schema);
249402
+ leftoverRefs(schema, schemas, found);
249403
+ }
249397
249404
  } else {
249398
249405
  leftoverRefs(value, schemas, found);
249399
249406
  }
@@ -249401,16 +249408,51 @@ function leftoverRefs(node, schemas, found = new Map) {
249401
249408
  }
249402
249409
  return found;
249403
249410
  }
249411
+ var SCHEMA_DATA_KEYS = new Set([
249412
+ "example",
249413
+ "examples",
249414
+ "default",
249415
+ "enum",
249416
+ "const"
249417
+ ]);
249418
+ function slimSchema(node, isPropertyMap = false) {
249419
+ if (Array.isArray(node)) {
249420
+ return node.map((item) => slimSchema(item));
249421
+ }
249422
+ if (node === null || typeof node !== "object") {
249423
+ return node;
249424
+ }
249425
+ const entries = Object.entries(node);
249426
+ if (isPropertyMap) {
249427
+ return Object.fromEntries(entries.map(([name, value]) => [name, slimSchema(value)]));
249428
+ }
249429
+ const slim = {};
249430
+ for (const [key, value] of entries) {
249431
+ if (key === "title" && typeof value === "string") {
249432
+ continue;
249433
+ }
249434
+ slim[key] = SCHEMA_DATA_KEYS.has(key) ? value : key === "properties" ? slimSchema(value, true) : key === "description" && typeof value === "string" ? cleanText(value) : slimSchema(value);
249435
+ }
249436
+ const { anyOf, ...rest } = slim;
249437
+ const nullable = Array.isArray(anyOf) && anyOf.length === 2 ? nullableType(anyOf) : undefined;
249438
+ return nullable ? { type: [nullable, "null"], ...rest } : slim;
249439
+ }
249440
+ function nullableType(variants) {
249441
+ const types = variants.map((variant) => variant !== null && typeof variant === "object" && Object.keys(variant).length === 1 && typeof variant.type === "string" ? variant.type : undefined);
249442
+ if (types.includes(undefined) || !types.includes("null")) {
249443
+ return;
249444
+ }
249445
+ return types.find((type) => type !== "null");
249446
+ }
249404
249447
  function getOperationContract(spec, op) {
249405
249448
  const schemas = spec.components?.schemas ?? {};
249406
- const inline = (node) => node === undefined ? null : inlineRefs(node, schemas);
249449
+ const inline = (node) => node === undefined ? null : slimSchema(inlineRefs(node, schemas));
249407
249450
  const responses = Object.entries(op.operation.responses ?? {}).flatMap(([status, response]) => {
249408
- const parsed = ResponseObjectSchema.safeParse(response);
249451
+ const parsed = ResponseObjectSchema.safeParse(resolveComponentRef(spec, response));
249409
249452
  return parsed.success ? [[status, parsed.data]] : [];
249410
249453
  });
249411
- const success = responses.filter(([status]) => status.startsWith("2")).sort(([a], [b]) => a.localeCompare(b)).map(([, response]) => jsonSchemaOf(response)).find(Boolean);
249412
- const errors = responses.filter(([status, response]) => !status.startsWith("2") && response.description !== FRAMEWORK_VALIDATION_ERROR);
249413
- const requestBody = requestBodyOf(op.operation.requestBody);
249454
+ const success = responses.filter(([status]) => status.startsWith("2")).sort(([a], [b]) => a.localeCompare(b)).map(([, response]) => bodyOf(response).schema).find(Boolean);
249455
+ const requestBody = bodyOf(resolveComponentRef(spec, op.operation.requestBody));
249414
249456
  const contract = {
249415
249457
  endpoint: op.endpoint,
249416
249458
  method: op.method,
@@ -249420,7 +249462,7 @@ function getOperationContract(spec, op) {
249420
249462
  parameters: (op.operation.parameters ?? []).flatMap((parameter) => {
249421
249463
  const parsed = ParameterSchema.safeParse(parameter);
249422
249464
  if (!parsed.success) {
249423
- return [];
249465
+ return [parameter];
249424
249466
  }
249425
249467
  const { name, in: location2, required, schema, description } = parsed.data;
249426
249468
  const inSchema = schema !== null && typeof schema === "object" && "description" in schema;
@@ -249430,23 +249472,23 @@ function getOperationContract(spec, op) {
249430
249472
  in: location2,
249431
249473
  required: required ?? false,
249432
249474
  schema: inline(schema ?? {}),
249433
- ...description && !inSchema ? { description } : {},
249475
+ ...description && !inSchema ? { description: cleanText(description) } : {},
249434
249476
  ..."example" in parsed.data ? { example: parsed.data.example } : {}
249435
249477
  }
249436
249478
  ];
249437
249479
  }),
249438
249480
  ...requestBody.contentType && requestBody.contentType !== "application/json" ? { requestBodyContentType: requestBody.contentType } : {},
249439
249481
  requestBodySchema: inline(requestBody.schema),
249440
- responseSchema: inline(success),
249441
- errors: Object.fromEntries(errors.map(([status, response]) => [status, response.description ?? ""]))
249482
+ responseSchema: inline(success)
249442
249483
  };
249443
249484
  const refs = leftoverRefs(contract, schemas);
249444
249485
  return {
249445
249486
  ...contract,
249446
- ...refs.size > 0 ? { refs: Object.fromEntries(refs) } : {}
249487
+ ...refs.size > 0 ? {
249488
+ refs: Object.fromEntries([...refs].map(([ref, schema]) => [ref, slimSchema(schema)]))
249489
+ } : {}
249447
249490
  };
249448
249491
  }
249449
-
249450
249492
  // src/core/api-spec/grep.ts
249451
249493
  function grepOperations(spec, operations, pattern, options = {}) {
249452
249494
  const regex = new RegExp(pattern, options.ignoreCase ? "i" : "");
@@ -249454,6 +249496,7 @@ function grepOperations(spec, operations, pattern, options = {}) {
249454
249496
  }
249455
249497
  // src/core/api-spec/request.ts
249456
249498
  var MAX_ERROR_BODY_CHARS = 4000;
249499
+ var QUERY_STRING = /\?\S*/g;
249457
249500
  async function readBody(response) {
249458
249501
  try {
249459
249502
  const text = (await response.clone().text()).trim();
@@ -249462,13 +249505,18 @@ async function readBody(response) {
249462
249505
  return;
249463
249506
  }
249464
249507
  }
249508
+ function normalizePath(path) {
249509
+ const url = new URL(path.replace(/^\/*/, "/"), "http://placeholder");
249510
+ return `${url.pathname}${url.search}`;
249511
+ }
249465
249512
  async function sendApiRequest(method, path, body) {
249466
249513
  try {
249467
249514
  const response = await base44Client(path.replace(/^\/+/, ""), {
249468
249515
  method,
249469
249516
  body,
249470
- headers: body === undefined ? {} : { "Content-Type": "application/json" },
249471
- timeout: false
249517
+ headers: typeof body === "string" ? { "Content-Type": "application/json" } : {},
249518
+ timeout: false,
249519
+ retry: { methods: ["get", "head"] }
249472
249520
  });
249473
249521
  return {
249474
249522
  status: response.status,
@@ -249477,19 +249525,30 @@ async function sendApiRequest(method, path, body) {
249477
249525
  };
249478
249526
  } catch (error) {
249479
249527
  const apiError = await ApiError.fromHttpError(error, `calling ${method} ${path}`);
249480
- const raw = error instanceof HTTPError ? await readBody(error.response) : undefined;
249481
- throw new ApiError(apiError.message, {
249482
- statusCode: apiError.statusCode,
249483
- requestUrl: apiError.requestUrl,
249484
- requestMethod: apiError.requestMethod,
249485
- requestId: apiError.requestId,
249486
- hints: apiError.hints,
249487
- details: raw ? [`Response body: ${raw}`] : apiError.details
249488
- });
249489
- }
249528
+ const response = error instanceof HTTPError ? error.response : undefined;
249529
+ const raw = response ? await readBody(response) : undefined;
249530
+ const retryAfter = response?.headers.get("retry-after") ?? undefined;
249531
+ throw redactedApiError(apiError, raw, method, path, retryAfter);
249532
+ }
249533
+ }
249534
+ function redactedApiError(apiError, raw, method, path, retryAfter) {
249535
+ const wait = retryAfter ? ` (retry after ${/^\d+$/.test(retryAfter) ? `${retryAfter} s` : retryAfter})` : "";
249536
+ const reason = apiError.statusCode !== undefined ? `HTTP ${apiError.statusCode}${wait}` : (apiError.cause?.message ?? "request failed").replace(QUERY_STRING, "");
249537
+ const message = `Error calling ${method} ${path.split("?", 1)[0]}: ${reason}`;
249538
+ return new ApiError(message, {
249539
+ statusCode: apiError.statusCode,
249540
+ requestUrl: apiError.requestUrl?.split("?", 1)[0],
249541
+ requestMethod: apiError.requestMethod,
249542
+ requestId: apiError.requestId,
249543
+ hints: apiError.hints,
249544
+ details: [
249545
+ ...raw ? [`Response body: ${raw}`] : apiError.details ?? [],
249546
+ ...apiError.requestId ? [`Request ID: ${apiError.requestId}`] : []
249547
+ ]
249548
+ });
249490
249549
  }
249491
249550
  // src/core/api-spec/spec.ts
249492
- import { rename, stat as stat3 } from "node:fs/promises";
249551
+ import { rename, rm, stat as stat3 } from "node:fs/promises";
249493
249552
  import { join as join22 } from "node:path";
249494
249553
  var SPEC_CACHE_TTL_MS = 60 * 60 * 1000;
249495
249554
  function getSpecCachePath(apiUrl = getBase44ApiUrl()) {
@@ -249544,12 +249603,18 @@ async function loadSpec(options = {}) {
249544
249603
  const text = await downloadSpec();
249545
249604
  const spec = parseSpec(text);
249546
249605
  const partial = `${path}.${process.pid}.tmp`;
249547
- await writeFile(partial, text);
249548
- await rename(partial, path);
249606
+ try {
249607
+ await writeFile(partial, text);
249608
+ await rename(partial, path);
249609
+ } catch {
249610
+ await rm(partial, { force: true }).catch(() => {
249611
+ return;
249612
+ });
249613
+ }
249549
249614
  return { text, spec };
249550
249615
  }
249551
249616
  // src/cli/commands/api/shared.ts
249552
- import { fstatSync } from "node:fs";
249617
+ import { basename as basename6 } from "node:path";
249553
249618
  function formatJson(value) {
249554
249619
  return toJsonStdout(value, { compact: !process.stdout.isTTY });
249555
249620
  }
@@ -249581,36 +249646,60 @@ function operationsInArea(operations, area) {
249581
249646
  }
249582
249647
  return inArea;
249583
249648
  }
249584
- function isPipedStdin() {
249585
- try {
249586
- const stats = fstatSync(0);
249587
- return stats.isFIFO() || stats.isFile();
249588
- } catch {
249589
- return false;
249590
- }
249591
- }
249592
- async function readRequestBody(flag, log) {
249649
+ async function readRequestBody(flag) {
249593
249650
  if (flag === "@-") {
249594
249651
  return readStdin("--body @-", { trim: false });
249595
249652
  }
249596
249653
  if (flag?.startsWith("@")) {
249597
249654
  return readTextFile(flag.slice(1));
249598
249655
  }
249599
- if (flag === undefined && isPipedStdin()) {
249600
- log.warn("stdin is ignored; pass --body @- to send it as the body.");
249601
- }
249602
249656
  return flag;
249603
249657
  }
249658
+ var FORM_FILE = /^@(.+?)(?:;type=([^;]+))?$/;
249659
+ async function readFormBody(fields) {
249660
+ const form = new FormData;
249661
+ for (const field of fields) {
249662
+ const separator = field.indexOf("=");
249663
+ if (separator < 1) {
249664
+ throw new InvalidInputError(`--form takes name=value or name=@file, got "${field}".`);
249665
+ }
249666
+ const name = field.slice(0, separator);
249667
+ const value = field.slice(separator + 1);
249668
+ const file = FORM_FILE.exec(value);
249669
+ if (file?.[1]) {
249670
+ const type = file[2] ?? "application/octet-stream";
249671
+ const blob = new Blob([await readFile(file[1])], { type });
249672
+ form.append(name, blob, basename6(file[1]));
249673
+ } else {
249674
+ form.append(name, value);
249675
+ }
249676
+ }
249677
+ return form;
249678
+ }
249679
+ function withoutQuery(path) {
249680
+ return path.split("?", 1)[0] ?? "";
249681
+ }
249682
+ function reportedFetchCommand(method, path, options) {
249683
+ return {
249684
+ name: "api fetch",
249685
+ args: [method, withoutQuery(path)],
249686
+ options: {
249687
+ ...options,
249688
+ ...options.body === undefined ? {} : { body: "[not reported]" },
249689
+ ...Array.isArray(options.form) && options.form.length > 0 ? { form: "[not reported]" } : {}
249690
+ }
249691
+ };
249692
+ }
249604
249693
 
249605
249694
  // src/cli/commands/api/catalog.ts
249606
249695
  async function catalogAction(ctx, area, options) {
249607
249696
  const { spec } = await loadSpecTask(ctx, options.refresh);
249608
249697
  const operations = listOperations(spec);
249609
- const readOnly = (ops) => options.readOnly ? ops.filter((op) => op.readOnly) : ops;
249610
- if (area === undefined) {
249611
- return { stdout: formatJson(catalogIndex(readOnly(operations))) };
249698
+ const filterReadOnly = (ops) => options.readOnly ? ops.filter((op) => op.readOnly) : ops;
249699
+ if (!area) {
249700
+ return { stdout: formatJson(catalogIndex(filterReadOnly(operations))) };
249612
249701
  }
249613
- const inArea = readOnly(operationsInArea(operations, area));
249702
+ const inArea = filterReadOnly(operationsInArea(operations, area));
249614
249703
  return { stdout: formatJson(areaListing(area, inArea)) };
249615
249704
  }
249616
249705
  function getApiCatalogCommand() {
@@ -249625,7 +249714,7 @@ async function findOperation(ctx, method, path, { refresh, flag }) {
249625
249714
  const { spec } = await loadSpecTask(ctx, refresh);
249626
249715
  const op = matchOperation(listOperations(spec), method, path);
249627
249716
  if (!op) {
249628
- throw new InvalidInputError(`No operation in the API spec matches ${method} ${path}.`, {
249717
+ throw new InvalidInputError(`No operation in the API spec matches ${method} ${withoutQuery(path)}.`, {
249629
249718
  hints: [
249630
249719
  {
249631
249720
  message: "The cached spec may predate this route; fetch it again",
@@ -249640,6 +249729,22 @@ async function findOperation(ctx, method, path, { refresh, flag }) {
249640
249729
  }
249641
249730
  return { spec, op };
249642
249731
  }
249732
+ var ENCODED_SEPARATOR = /%(2f|5c|2e)/i;
249733
+ async function fillAppId(ctx, path, command) {
249734
+ let filled = path;
249735
+ if (withoutQuery(path).includes("{app_id}")) {
249736
+ const { appId } = command.optsWithGlobals();
249737
+ await ensureAppContext(ctx, { appId });
249738
+ filled = path.replaceAll("{app_id}", requireApp(ctx).id);
249739
+ }
249740
+ const placeholder = withoutQuery(filled).match(/\{[^}/]*\}/);
249741
+ if (placeholder) {
249742
+ throw new InvalidInputError(`Replace ${placeholder[0]} in the path with a real value; only {app_id} is filled in.`);
249743
+ }
249744
+ return filled;
249745
+ }
249746
+ var MULTIPART_HINT = "Send this body with --form name=value, and --form name=@path for files.";
249747
+ var CONTRACT_HELP = "The contract is JSON; cut it down with e.g. | jq '.parameters, .requestBodySchema'";
249643
249748
  var TEXT_CONTENT_TYPES = [
249644
249749
  "json",
249645
249750
  "text/",
@@ -249647,18 +249752,27 @@ var TEXT_CONTENT_TYPES = [
249647
249752
  "javascript",
249648
249753
  "x-www-form-urlencoded"
249649
249754
  ];
249650
- function isText(contentType) {
249651
- return !contentType || TEXT_CONTENT_TYPES.some((hint) => contentType.toLowerCase().includes(hint));
249755
+ function decodeText(body, contentType) {
249756
+ const type = contentType.toLowerCase();
249757
+ if (type && !TEXT_CONTENT_TYPES.some((hint) => type.includes(hint))) {
249758
+ return;
249759
+ }
249760
+ try {
249761
+ return new TextDecoder("utf-8", { fatal: true }).decode(body);
249762
+ } catch {
249763
+ return;
249764
+ }
249652
249765
  }
249653
249766
  async function formatResponse({ status, contentType, body }, output, jsonMode) {
249654
- if (output !== undefined) {
249767
+ if (output) {
249655
249768
  await writeFile(output, body);
249656
249769
  return formatJson({ status, contentType, bytes: body.length, output });
249657
249770
  }
249658
249771
  if (body.length === 0) {
249659
249772
  return formatJson({ status });
249660
249773
  }
249661
- if (!isText(contentType)) {
249774
+ const text = decodeText(body, contentType);
249775
+ if (text === undefined) {
249662
249776
  return formatJson({
249663
249777
  status,
249664
249778
  contentType,
@@ -249666,36 +249780,46 @@ async function formatResponse({ status, contentType, body }, output, jsonMode) {
249666
249780
  note: "Binary body not printed; add --output <file> to save it."
249667
249781
  });
249668
249782
  }
249669
- const text = new TextDecoder().decode(body);
249670
- try {
249671
- return formatJson(JSON.parse(text));
249672
- } catch {
249673
- if (jsonMode) {
249674
- return formatJson({ status, contentType, body: text });
249675
- }
249676
- return text.endsWith(`
249783
+ const asIs = text.endsWith(`
249677
249784
  `) ? text : `${text}
249678
249785
  `;
249786
+ try {
249787
+ const parsed = JSON.parse(text);
249788
+ return process.stdout.isTTY ? formatJson(parsed) : asIs;
249789
+ } catch {
249790
+ return jsonMode ? formatJson({ status, contentType, body: text }) : asIs;
249679
249791
  }
249680
249792
  }
249681
249793
  async function fetchAction(ctx, methodArg, path, options, command) {
249794
+ ctx.errorReporter.setContext({
249795
+ command: reportedFetchCommand(methodArg ?? "", path ?? "", { ...options })
249796
+ });
249682
249797
  if (!methodArg || !path) {
249683
249798
  if (options.help) {
249684
- const usage = command.helpInformation();
249799
+ const usage = `${command.helpInformation()}
249800
+ ${CONTRACT_HELP}
249801
+ `;
249685
249802
  return { stdout: ctx.jsonMode ? formatJson({ usage }) : usage };
249686
249803
  }
249687
249804
  throw new InvalidInputError("Provide a method and a path, e.g. GET /api/apps.");
249688
249805
  }
249689
249806
  const method = methodArg.toUpperCase();
249690
249807
  if (options.help) {
249691
- const { spec, op } = await findOperation(ctx, method, path, {
249808
+ const { spec, op } = await findOperation(ctx, method, normalizePath(path), {
249692
249809
  refresh: options.refresh,
249693
249810
  flag: "--help"
249694
249811
  });
249695
- return { stdout: formatJson(getOperationContract(spec, op)) };
249812
+ const contract = getOperationContract(spec, op);
249813
+ return {
249814
+ stdout: formatJson(contract.requestBodyContentType === "multipart/form-data" ? { ...contract, hint: MULTIPART_HINT } : contract)
249815
+ };
249696
249816
  }
249817
+ const target = normalizePath(await fillAppId(ctx, path, command));
249697
249818
  if (options.readOnly) {
249698
- const { op } = await findOperation(ctx, method, path, {
249819
+ if (ENCODED_SEPARATOR.test(withoutQuery(target))) {
249820
+ throw new InvalidInputError("Refused under --read-only: the path has an encoded /, \\ or ., so it can't be checked. Nothing was sent.");
249821
+ }
249822
+ const { op } = await findOperation(ctx, method, target, {
249699
249823
  refresh: options.refresh,
249700
249824
  flag: "--read-only"
249701
249825
  });
@@ -249703,17 +249827,13 @@ async function fetchAction(ctx, methodArg, path, options, command) {
249703
249827
  throw new InvalidInputError(`Refused under --read-only: ${op.method} ${op.path} is not a read-only operation. Nothing was sent.`);
249704
249828
  }
249705
249829
  }
249706
- ctx.errorReporter.setContext({
249707
- command: {
249708
- name: "api fetch",
249709
- args: [method, path.split("?", 1)[0] ?? ""],
249710
- options: { ...options, body: options.body && "[not reported]" }
249711
- }
249712
- });
249830
+ if (options.body !== undefined && options.form.length > 0) {
249831
+ throw new InvalidInputError("Use --body or --form, not both.");
249832
+ }
249713
249833
  await ensureAuth(ctx);
249714
- const body = await readRequestBody(options.body, ctx.log);
249715
- const response = await ctx.runTask(`Calling ${method} ${path}`, () => sendApiRequest(method, path, body), {
249716
- successMessage: `Called ${method} ${path}`,
249834
+ const body = options.form.length > 0 ? await readFormBody(options.form) : await readRequestBody(options.body);
249835
+ const response = await ctx.runTask(`Calling ${method} ${target}`, () => sendApiRequest(method, target, body), {
249836
+ successMessage: `Called ${method} ${target}`,
249717
249837
  errorMessage: "Request failed"
249718
249838
  });
249719
249839
  return {
@@ -249724,7 +249844,8 @@ function getApiFetchCommand() {
249724
249844
  return new Base44Command("fetch", {
249725
249845
  requireAuth: false,
249726
249846
  requireAppContext: false
249727
- }).description("Call a Base44 API operation; add --help after a path for its contract").helpOption(false).argument("[method]", "HTTP method, e.g. GET or POST").argument("[path]", 'Concrete path with any query string, e.g. "/api/apps/<app-id>/workflows/runs?status=failed"').option("--body <json>", "Request body: JSON, @file.json, or @- to read it from stdin").option("--output <file>", "Save the response body to a file").option("--read-only", "Refuse, before sending, any operation the spec does not mark read-only").option("--refresh", REFRESH_OPTION_HELP).option("-h, --help", "With a method and path, print that operation's contract; otherwise this help").action(fetchAction);
249847
+ }).description("Call a Base44 API operation; add --help after a path for its contract").helpOption(false).argument("[method]", "HTTP method, e.g. GET or POST").argument("[path]", 'Path with any query string; {app_id} is filled from --app-id or the project, e.g. "/api/apps/{app_id}/workflows/runs?status=failed"').option("--body <json>", "Request body: JSON, @file.json, or @- to read it from stdin").option("--form <field>", "Multipart field: name=value, or name=@file to attach a file (repeatable)", (field, fields) => [...fields, field], []).option("--output <file>", "Save the response body to a file").option("--read-only", "Refuse, before sending, any operation the spec does not mark read-only").option("--refresh", REFRESH_OPTION_HELP).option("-h, --help", "With a method and path, print that operation's contract; otherwise this help").addHelpText("after", `
249848
+ ${CONTRACT_HELP}`).action(fetchAction);
249728
249849
  }
249729
249850
 
249730
249851
  // src/cli/commands/api/grep.ts
@@ -249740,7 +249861,7 @@ async function grepAction(ctx, pattern, options) {
249740
249861
  assertValidRegex(pattern);
249741
249862
  const { spec } = await loadSpecTask(ctx, options.refresh);
249742
249863
  let operations = listOperations(spec);
249743
- if (options.area !== undefined) {
249864
+ if (options.area) {
249744
249865
  operations = operationsInArea(operations, options.area);
249745
249866
  }
249746
249867
  if (options.readOnly) {
@@ -249756,7 +249877,7 @@ function getApiGrepCommand() {
249756
249877
  return new Base44Command("grep", {
249757
249878
  requireAuth: false,
249758
249879
  requireAppContext: false
249759
- }).description("Search the API spec with a regular expression, for when search misses").argument("<pattern>", 'Regular expression, e.g. "runs.*status"').option("--area <area>", "Search only one area's operations (see base44 api catalog)").option("-i, --ignore-case", "Case-insensitive match").option("--limit <n>", "Most operations to return (default 20)").option("--read-only", READ_ONLY_FILTER_HELP).option("--refresh", REFRESH_OPTION_HELP).action(grepAction);
249880
+ }).description("Search the API spec with a regular expression, for when search misses").argument("<pattern>", `Regular expression over each operation's contract as JSON text (quotes and newlines appear escaped), e.g. "runs.*status"`).option("--area <area>", "Search only one area's operations (see base44 api catalog)").option("-i, --ignore-case", "Case-insensitive match").option("--limit <n>", "Most operations to return (default 20)").option("--read-only", READ_ONLY_FILTER_HELP).option("--refresh", REFRESH_OPTION_HELP).action(grepAction);
249760
249881
  }
249761
249882
 
249762
249883
  // src/cli/commands/api/search.ts
@@ -251543,7 +251664,7 @@ function getBuildCommand() {
251543
251664
  }
251544
251665
 
251545
251666
  // src/cli/commands/project/create.ts
251546
- import { basename as basename6, resolve as resolve11 } from "node:path";
251667
+ import { basename as basename7, resolve as resolve11 } from "node:path";
251547
251668
  var import_kebabCase = __toESM(require_kebabCase(), 1);
251548
251669
 
251549
251670
  // src/cli/commands/project/scaffold-shared.ts
@@ -251708,8 +251829,8 @@ async function createInteractive(options, ctx) {
251708
251829
  name: () => {
251709
251830
  return options.name ? Promise.resolve(options.name) : Ze({
251710
251831
  message: "What is the name of your project?",
251711
- placeholder: basename6(process.cwd()),
251712
- initialValue: basename6(process.cwd()),
251832
+ placeholder: basename7(process.cwd()),
251833
+ initialValue: basename7(process.cwd()),
251713
251834
  validate: (value) => {
251714
251835
  if (!value || value.trim().length === 0) {
251715
251836
  return "Every project deserves a name";
@@ -252404,7 +252525,7 @@ function getLogsCommand() {
252404
252525
  }
252405
252526
 
252406
252527
  // src/cli/commands/project/scaffold.ts
252407
- import { basename as basename7, resolve as resolve12 } from "node:path";
252528
+ import { basename as basename8, resolve as resolve12 } from "node:path";
252408
252529
  function resolveAppId(options) {
252409
252530
  const appId = options.appId;
252410
252531
  if (!appId) {
@@ -252421,7 +252542,7 @@ async function scaffoldAction(ctx, name, options, command) {
252421
252542
  const { log, runTask } = ctx;
252422
252543
  const appId = resolveAppId(command.optsWithGlobals());
252423
252544
  const resolvedPath = resolve12("./");
252424
- const projectName = (name ?? basename7(resolvedPath)).trim();
252545
+ const projectName = (name ?? basename8(resolvedPath)).trim();
252425
252546
  const template = await getTemplateById("backend-only");
252426
252547
  log.info(`Scaffolding project at ${resolvedPath}`);
252427
252548
  const { projectId } = await runTask("Setting up your project...", async () => {
@@ -262316,7 +262437,7 @@ var nativeEnumType = ZodNativeEnum.create;
262316
262437
  var promiseType = ZodPromise.create;
262317
262438
  var effectsType = ZodEffects.create;
262318
262439
  var optionalType = ZodOptional2.create;
262319
- var nullableType = ZodNullable2.create;
262440
+ var nullableType2 = ZodNullable2.create;
262320
262441
  var preprocessType = ZodEffects.createWithPreprocess;
262321
262442
  var pipelineType = ZodPipeline.create;
262322
262443
  // ../functions-compiler/src/contracts.ts
@@ -262346,7 +262467,7 @@ var bundleAppRequestSchema = objectType({
262346
262467
  var ACTIVATION_MODE = "pull";
262347
262468
 
262348
262469
  // ../functions-compiler/src/deno-bundle.ts
262349
- import { mkdtemp, rm, writeFile as writeFile2 } from "node:fs/promises";
262470
+ import { mkdtemp, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
262350
262471
  import { tmpdir as tmpdir2 } from "node:os";
262351
262472
  import path21 from "node:path";
262352
262473
 
@@ -262953,7 +263074,7 @@ async function bundleToModule(prepared, nodeModulesDir = "none") {
262953
263074
  warnings: result.warnings.map((w) => w.text)
262954
263075
  };
262955
263076
  } finally {
262956
- await rm(dir, { recursive: true, force: true });
263077
+ await rm2(dir, { recursive: true, force: true });
262957
263078
  }
262958
263079
  }
262959
263080
  function buildFailureErrors(e, dir) {
@@ -267944,7 +268065,7 @@ function createPattern(matcher) {
267944
268065
  }
267945
268066
  return () => false;
267946
268067
  }
267947
- function normalizePath(path) {
268068
+ function normalizePath2(path) {
267948
268069
  if (typeof path !== "string")
267949
268070
  throw new Error("string expected");
267950
268071
  path = sp3.normalize(path);
@@ -267958,7 +268079,7 @@ function normalizePath(path) {
267958
268079
  return path;
267959
268080
  }
267960
268081
  function matchPatterns(patterns, testString, stats) {
267961
- const path = normalizePath(testString);
268082
+ const path = normalizePath2(testString);
267962
268083
  for (let index = 0;index < patterns.length; index++) {
267963
268084
  const pattern = patterns[index];
267964
268085
  if (pattern(path, stats)) {
@@ -273318,4 +273439,4 @@ export {
273318
273439
  runCLI
273319
273440
  };
273320
273441
 
273321
- //# debugId=719BE22CF0CDD92864756E2164756E21
273442
+ //# debugId=4213AA6F0A5C795F64756E2164756E21