@blotout/qinto-cli 0.1.16 → 0.1.17

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.
@@ -18115,10 +18115,10 @@ var stringbool = (...args) => _stringbool({
18115
18115
  String: ZodString
18116
18116
  }, ...args);
18117
18117
  function json(params) {
18118
- const jsonSchema = lazy(() => {
18119
- return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema), record(string2(), jsonSchema)]);
18118
+ const jsonSchema2 = lazy(() => {
18119
+ return union([string2(params), number2(), boolean2(), _null3(), array(jsonSchema2), record(string2(), jsonSchema2)]);
18120
18120
  });
18121
- return jsonSchema;
18121
+ return jsonSchema2;
18122
18122
  }
18123
18123
  function preprocess(fn, schema) {
18124
18124
  return new ZodPreprocess({
@@ -18945,6 +18945,7 @@ function date4(params) {
18945
18945
  var IDENTIFIER = /^[a-z][a-z0-9-]*$/;
18946
18946
  var VARIABLE_NAME = /^[A-Z][A-Z0-9_]*$/;
18947
18947
  var APP_SLUG_PATTERN = /^[a-z][a-z0-9-]{2,29}$/;
18948
+ var FUNCTION_NAME = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
18948
18949
  var identifierField = external_exports.string().min(1).max(64).regex(IDENTIFIER);
18949
18950
  var httpsUrl = (field2) => {
18950
18951
  return external_exports.url().max(2048).refine((value) => value.startsWith("https://"), `${field2} must be an HTTPS URL`);
@@ -19345,7 +19346,6 @@ var MAX_MANIFEST_MIGRATION_BYTES = 1024 * 1024;
19345
19346
  var BINDING_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
19346
19347
  var CLASS_NAME = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
19347
19348
  var MIGRATION_ID = /^[a-z0-9][a-z0-9-]*$/;
19348
- var FUNCTION_NAME = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
19349
19349
  var EVENT_TOPIC = /^[a-z][a-z0-9:_-]*$/;
19350
19350
  var QUERY_PARAM_NAME = /^[A-Za-z0-9_.-]+$/;
19351
19351
  var COOKIE_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
@@ -19383,6 +19383,7 @@ var PLATFORM_APP_RESOURCE_IDENTIFIERS = {
19383
19383
  };
19384
19384
  var PLATFORM_APP_VARIABLE_IDENTIFIER_PREFIX = "var-";
19385
19385
  var PLATFORM_APP_SCOPE_VARIABLE_IDENTIFIER_PREFIX = "appvar-";
19386
+ var STORAGE_LINK_BINDING_PREFIX = "QINTO_LINK_";
19386
19387
  var legalUrl = (field2) => {
19387
19388
  return httpsUrl(field2).refine((value) => {
19388
19389
  const host = new URL(value).hostname;
@@ -19653,7 +19654,7 @@ var validateBindings = (manifest, ctx) => {
19653
19654
  "variable name"
19654
19655
  );
19655
19656
  for (const [index, resource] of manifest.resources.entries()) {
19656
- if (reserved.has(resource.binding) || resource.binding.startsWith("VAR_") || resource.binding.startsWith("APPVAR_") || resource.binding.startsWith(PLATFORM_APP_OAUTH_BINDING_PREFIX)) {
19657
+ if (reserved.has(resource.binding) || resource.binding.startsWith("VAR_") || resource.binding.startsWith("APPVAR_") || resource.binding.startsWith(PLATFORM_APP_OAUTH_BINDING_PREFIX) || resource.binding.startsWith(STORAGE_LINK_BINDING_PREFIX)) {
19657
19658
  ctx.addIssue({ code: "custom", path: ["resources", index, "binding"], message: `Binding ${resource.binding} is reserved` });
19658
19659
  }
19659
19660
  if (reservedIdentifiers.has(resource.identifier) || resource.identifier.startsWith(PLATFORM_APP_VARIABLE_IDENTIFIER_PREFIX) || resource.identifier.startsWith(PLATFORM_APP_SCOPE_VARIABLE_IDENTIFIER_PREFIX)) {
@@ -19956,8 +19957,17 @@ var appManifestSchema = baseSchema.superRefine((manifest, ctx) => {
19956
19957
  var MAX_VENDOR_BODY_BYTES = 64 * 1024;
19957
19958
  var MAX_EXPIRES_IN_SECONDS = 365 * 24 * 60 * 60;
19958
19959
 
19960
+ // ../../../libs/app-manifest/src/rpc-contract.ts
19961
+ var MAX_RPC_CONTRACT_BYTES = 256 * 1024;
19962
+ var jsonSchema = external_exports.record(external_exports.string(), external_exports.unknown());
19963
+ var rpcFunctionContractSchema = external_exports.object({ visibility: external_exports.enum(["public", "private"]), input: jsonSchema, output: jsonSchema }).strict();
19964
+ var appRpcContractSchema = external_exports.record(external_exports.string().regex(FUNCTION_NAME).max(64), rpcFunctionContractSchema).refine((contract) => Object.keys(contract).length <= 32, "An RPC contract carries at most 32 functions").refine((contract) => {
19965
+ return new TextEncoder().encode(JSON.stringify(contract)).length <= MAX_RPC_CONTRACT_BYTES;
19966
+ }, "The RPC contract exceeds the size cap");
19967
+
19959
19968
  // ../../../libs/app-envelope/src/envelope.ts
19960
19969
  var ENVELOPE_VERSION = 2;
19970
+ var ENVELOPE_ERROR_CODES = ["invalid_arguments"];
19961
19971
  var MAX_INSTALLS_PER_APP = 15;
19962
19972
  var MAX_EMITS_PER_INSTALL = 10;
19963
19973
  var MAX_PUSHES_PER_INSTALL = 10;
@@ -20056,13 +20066,14 @@ var envelopeResponseSchema = external_exports.object({
20056
20066
  rpc: external_exports.object({ result: external_exports.unknown() }).strict().refine((rpc) => {
20057
20067
  return new TextEncoder().encode(JSON.stringify(rpc.result ?? null)).length <= MAX_RPC_BYTES;
20058
20068
  }, "RPC result exceeds the size cap").optional(),
20059
- error: external_exports.object({ message: external_exports.string().max(2048) }).strict().optional()
20069
+ error: external_exports.object({ message: external_exports.string().max(2048), code: external_exports.enum(ENVELOPE_ERROR_CODES).optional() }).strict().optional()
20060
20070
  }).strict();
20061
20071
 
20062
20072
  // ../../../libs/app-envelope/src/control.ts
20063
20073
  var CONTROL_ENVELOPE_VERSION = 1;
20064
20074
  var MAX_CONTROL_BYTES = 256 * 1024;
20065
20075
  var MAX_MIGRATION_CHECKPOINT_BYTES = 16 * 1024;
20076
+ var MIGRATION_DATABASE_LINK_CHANGED = "migration_database_link_changed";
20066
20077
  var MAX_TOOL_ARGUMENTS_BYTES = 32 * 1024;
20067
20078
  var MAX_TOOL_RESULT_BYTES = 64 * 1024;
20068
20079
  var MAX_TOOL_DATA_DEPTH = 8;
@@ -20868,6 +20879,10 @@ var siteChannel = (env) => {
20868
20879
  var channelFetch = (env, init) => {
20869
20880
  return siteChannel(env).fetch(BROKER_URL, init);
20870
20881
  };
20882
+ var brokerFailure = (answer, status) => {
20883
+ const error61 = answer.error ?? `the broker answered ${status}`;
20884
+ return answer.message === void 0 ? error61 : `${error61}: ${answer.message}`;
20885
+ };
20871
20886
  var askBroker = async (env, name, body) => {
20872
20887
  return traced(name, {}, async () => {
20873
20888
  const headers = { "content-type": "application/json" };
@@ -20891,7 +20906,7 @@ var askBroker = async (env, name, body) => {
20891
20906
  }
20892
20907
  const answer = parsed;
20893
20908
  if (answer.error !== void 0 || !response.ok) {
20894
- throw new Error(answer.error ?? `the broker answered ${response.status}`);
20909
+ throw new Error(brokerFailure(answer, response.status));
20895
20910
  }
20896
20911
  return answer;
20897
20912
  });
@@ -21273,6 +21288,10 @@ var contextKindsOf = (env) => {
21273
21288
  const context = JSON.parse(env.APP_CONTEXT);
21274
21289
  return { variableKinds: context.variableKinds ?? {}, appVariableKinds: context.appVariableKinds ?? {} };
21275
21290
  };
21291
+ var linkedDatabasesOf = (env) => {
21292
+ const context = JSON.parse(env.APP_CONTEXT);
21293
+ return context.linkedDatabases ?? [];
21294
+ };
21276
21295
  var projectAppValues = (env, kinds) => {
21277
21296
  const appVariables = {};
21278
21297
  const appSecrets = {};
@@ -21759,6 +21778,11 @@ var dispatchSchedule = async (envelope, bundle, env) => {
21759
21778
  data: message.data,
21760
21779
  broadcast: message.broadcast
21761
21780
  });
21781
+ },
21782
+ context: (userId) => {
21783
+ return asyncContext(env, () => {
21784
+ return userId;
21785
+ });
21762
21786
  }
21763
21787
  };
21764
21788
  try {
@@ -21998,6 +22022,9 @@ var dispatchLifecycle = async (envelope, bundle, env) => {
21998
22022
  };
21999
22023
  var dispatchMigration = async (envelope, bundle, env) => {
22000
22024
  const migration = envelope.migration;
22025
+ if (linkedDatabasesOf(env).includes(migration.resource)) {
22026
+ return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: false, error: { message: MIGRATION_DATABASE_LINK_CHANGED } });
22027
+ }
22001
22028
  try {
22002
22029
  if (migration.phase !== "backfill") {
22003
22030
  await runStatementMigration(env, migration);
@@ -22108,8 +22135,8 @@ var buildContext3 = ({
22108
22135
  geo,
22109
22136
  params,
22110
22137
  install: { id: install.id, label: install.label },
22111
- // An endpoint has no visitor, so a workflow it starts cannot push. `push` below is the
22112
- // exception: it names its subject rather than inheriting one.
22138
+ // An endpoint has no visitor, so a workflow it starts cannot push or touch context. `push` and
22139
+ // `context` below are the exceptions: they name their subject rather than inheriting one.
22113
22140
  startWorkflow: buildStartWorkflow({ ord: install.ord, installId: install.id, installLabel: install.label, anonId: null }),
22114
22141
  oauth: buildOauth(env, install.ord),
22115
22142
  variables,
@@ -22128,6 +22155,11 @@ var buildContext3 = ({
22128
22155
  broadcast: message.broadcast
22129
22156
  });
22130
22157
  },
22158
+ context: (userId) => {
22159
+ return asyncContext(env, () => {
22160
+ return userId;
22161
+ });
22162
+ },
22131
22163
  skip: () => {
22132
22164
  return { kind: "skip" };
22133
22165
  }
@@ -22300,6 +22332,42 @@ var withPipelineRetry = (env, bindings) => {
22300
22332
  return wrapped;
22301
22333
  };
22302
22334
 
22335
+ // src/rpc-definition.ts
22336
+ var isSchema = (candidate) => {
22337
+ return typeof candidate === "object" && candidate !== null && "~standard" in candidate;
22338
+ };
22339
+ var rpcDefinitionOf = (candidate) => {
22340
+ if (typeof candidate !== "object" || candidate === null || !("__qintoRpc" in candidate)) {
22341
+ return void 0;
22342
+ }
22343
+ const marker = candidate.__qintoRpc;
22344
+ if (typeof marker !== "object" || marker === null || !("handler" in marker) || typeof marker.handler !== "function") {
22345
+ return void 0;
22346
+ }
22347
+ const definition = { handler: marker.handler };
22348
+ if ("input" in marker && isSchema(marker.input)) {
22349
+ definition.input = marker.input;
22350
+ }
22351
+ if ("output" in marker && isSchema(marker.output)) {
22352
+ definition.output = marker.output;
22353
+ }
22354
+ return definition;
22355
+ };
22356
+ var checkAgainst = async (schema, value) => {
22357
+ if (schema === void 0) {
22358
+ return { ok: true, value };
22359
+ }
22360
+ const result = await schema["~standard"].validate(value);
22361
+ if (result.issues === void 0) {
22362
+ return { ok: true, value: result.value };
22363
+ }
22364
+ const message = result.issues.map((issue2) => {
22365
+ const path = (issue2.path ?? []).map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
22366
+ return path === "" ? issue2.message : `${path}: ${issue2.message}`;
22367
+ }).join("; ");
22368
+ return { ok: false, message };
22369
+ };
22370
+
22303
22371
  // src/traced-fetch.ts
22304
22372
  var MAX_BODY_CHARS = 65536;
22305
22373
  var TOO_LARGE = `[not recorded: the body is over ${MAX_BODY_CHARS} characters]`;
@@ -22660,10 +22728,14 @@ var dispatchRpc = async (envelope, bundle, env) => {
22660
22728
  if (rpc === void 0) {
22661
22729
  return jsonError(400, "rpc envelope carries no rpc call");
22662
22730
  }
22663
- const handler = toHandler(bundle?.rpc?.[rpc.fn]);
22664
- if (handler === void 0) {
22731
+ const definition = rpcDefinitionOf(bundle?.rpc?.[rpc.fn]);
22732
+ if (definition === void 0) {
22665
22733
  return jsonError(200, `missing handler: rpc.${rpc.fn}`);
22666
22734
  }
22735
+ const args = await checkAgainst(definition.input, rpc.args);
22736
+ if (!args.ok) {
22737
+ return json2(200, { v: ENVELOPE_VERSION, error: { code: "invalid_arguments", message: failureMessage(args.message) } });
22738
+ }
22667
22739
  const install = envelope.installs[0];
22668
22740
  const emits = [];
22669
22741
  const pushes = [];
@@ -22686,14 +22758,18 @@ var dispatchRpc = async (envelope, bundle, env) => {
22686
22758
  let value;
22687
22759
  try {
22688
22760
  value = await installSpan(install, () => {
22689
- return handler(rpc.args, ctx);
22761
+ return definition.handler(args.value, ctx);
22690
22762
  });
22691
22763
  } catch (error61) {
22692
22764
  const message = failureMessage(error61);
22693
22765
  console.warn(`rpc failed for install ${install.id}: ${message}`);
22694
22766
  return json2(200, { v: ENVELOPE_VERSION, error: { message } });
22695
22767
  }
22696
- const result = value === void 0 ? null : value;
22768
+ const checked = await checkAgainst(definition.output, value);
22769
+ if (!checked.ok) {
22770
+ return jsonError(200, failureMessage(`rpc result does not match the output schema: ${checked.message}`));
22771
+ }
22772
+ const result = checked.value === void 0 ? null : checked.value;
22697
22773
  if (new TextEncoder().encode(JSON.stringify(result)).length > MAX_RPC_BYTES) {
22698
22774
  return jsonError(200, "rpc result exceeds the size cap");
22699
22775
  }