@blotout/qinto-cli 0.1.22 → 0.1.24

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.
@@ -20184,6 +20184,14 @@ var lifecycleEventSchema = external_exports.discriminatedUnion("type", [
20184
20184
  }).strict(),
20185
20185
  external_exports.object({ ...lifecycleBase, type: external_exports.literal("uninstalled"), scope: external_exports.enum(["app", "install"]) }).strict()
20186
20186
  ]);
20187
+ var deleteRequestSchema = external_exports.object({
20188
+ eventId: external_exports.string().min(1).max(64),
20189
+ occurredAt: external_exports.number().int().nonnegative().safe(),
20190
+ install: installSchema2,
20191
+ personId: external_exports.string().min(1).nullable(),
20192
+ anonIds: external_exports.array(external_exports.string().min(1)),
20193
+ identifiers: external_exports.array(external_exports.object({ type: external_exports.enum(["email", "phone", "custom"]), value: external_exports.string().min(1) }).strict()).optional()
20194
+ }).strict();
20187
20195
  var migrationSchema = external_exports.discriminatedUnion("phase", [
20188
20196
  external_exports.object({
20189
20197
  resource: external_exports.string().min(1).max(64),
@@ -20246,10 +20254,11 @@ var toolSchema2 = external_exports.object({
20246
20254
  }, "Tool arguments exceed the size cap");
20247
20255
  var controlEnvelopeSchema = external_exports.object({
20248
20256
  v: external_exports.literal(CONTROL_ENVELOPE_VERSION),
20249
- kind: external_exports.enum(["noop", "lifecycle", "migration", "schedule", "tool"]),
20257
+ kind: external_exports.enum(["noop", "lifecycle", "deleteRequest", "migration", "schedule", "tool"]),
20250
20258
  app: external_exports.object({ slug: external_exports.string().min(1).max(30), version: external_exports.string().min(1).max(32) }).strict(),
20251
20259
  install: external_exports.object({ id: external_exports.string().min(1).max(64), ord: external_exports.number().int().min(1), label: external_exports.string().max(100) }).strict().optional(),
20252
20260
  lifecycle: lifecycleEventSchema.optional(),
20261
+ deleteRequest: deleteRequestSchema.optional(),
20253
20262
  migration: migrationSchema.optional(),
20254
20263
  schedule: scheduleSchema2.optional(),
20255
20264
  tool: toolSchema2.optional()
@@ -20257,6 +20266,9 @@ var controlEnvelopeSchema = external_exports.object({
20257
20266
  if (envelope.kind === "lifecycle" !== (envelope.lifecycle !== void 0)) {
20258
20267
  ctx.addIssue({ code: "custom", path: ["lifecycle"], message: "Lifecycle payload must match the control kind" });
20259
20268
  }
20269
+ if (envelope.kind === "deleteRequest" !== (envelope.deleteRequest !== void 0)) {
20270
+ ctx.addIssue({ code: "custom", path: ["deleteRequest"], message: "Delete request payload must match the control kind" });
20271
+ }
20260
20272
  if (envelope.kind === "migration" !== (envelope.migration !== void 0)) {
20261
20273
  ctx.addIssue({ code: "custom", path: ["migration"], message: "Migration payload must match the control kind" });
20262
20274
  }
@@ -20266,8 +20278,8 @@ var controlEnvelopeSchema = external_exports.object({
20266
20278
  if (envelope.kind === "tool" !== (envelope.tool !== void 0)) {
20267
20279
  ctx.addIssue({ code: "custom", path: ["tool"], message: "Tool payload must match the control kind" });
20268
20280
  }
20269
- if (envelope.kind === "lifecycle" && envelope.install === void 0) {
20270
- ctx.addIssue({ code: "custom", path: ["install"], message: "Lifecycle control needs an install" });
20281
+ if ((envelope.kind === "lifecycle" || envelope.kind === "deleteRequest") && envelope.install === void 0) {
20282
+ ctx.addIssue({ code: "custom", path: ["install"], message: `${envelope.kind} control needs an install` });
20271
20283
  }
20272
20284
  if (envelope.kind === "tool" && envelope.install === void 0) {
20273
20285
  ctx.addIssue({ code: "custom", path: ["install"], message: "Tool control needs an install" });
@@ -20306,6 +20318,8 @@ var toolResultSchema = external_exports.object({
20306
20318
  var controlResponseSchema = external_exports.object({
20307
20319
  v: external_exports.literal(CONTROL_ENVELOPE_VERSION),
20308
20320
  ok: external_exports.boolean(),
20321
+ // The bundle has no delete request handler, so a retry cannot succeed.
20322
+ skipped: external_exports.literal(true).optional(),
20309
20323
  tool: toolResultSchema.optional(),
20310
20324
  migration: external_exports.union([
20311
20325
  external_exports.object({ done: external_exports.literal(true) }).strict(),
@@ -20483,6 +20497,21 @@ var parseUiContext = (raw) => {
20483
20497
  return parsed.success ? parsed.data : null;
20484
20498
  };
20485
20499
 
20500
+ // src/app-env.ts
20501
+ var PLATFORM_BINDINGS = new Set(
20502
+ PLATFORM_APP_BINDINGS.filter((name) => {
20503
+ return name !== PLATFORM_APP_BINDING_NAMES.installToken;
20504
+ })
20505
+ );
20506
+ var PLATFORM_PREFIXES = ["VAR_", "APPVAR_", PLATFORM_APP_OAUTH_BINDING_PREFIX];
20507
+ var appEnv = (env) => {
20508
+ return Object.fromEntries(
20509
+ Object.entries(env).filter(([name]) => {
20510
+ return !PLATFORM_BINDINGS.has(name) && !PLATFORM_PREFIXES.some((prefix) => name.startsWith(prefix));
20511
+ })
20512
+ );
20513
+ };
20514
+
20486
20515
  // ../../../libs/tail-trace/src/validate.ts
20487
20516
  var TRACE_ID = /^[0-9a-f]{32}$/;
20488
20517
  var SPAN_ID = /^[0-9a-f]{16}$/;
@@ -21071,6 +21100,15 @@ var brokerIdentityRead = async (env, identifier, options = {}) => {
21071
21100
  });
21072
21101
  return { globalId: answer.globalId ?? null, anonIds: answer.anonIds ?? [], nextCursor: answer.nextCursor ?? null };
21073
21102
  };
21103
+ var brokerCurrencyRates = async (env) => {
21104
+ const answer = await askBroker(env, "apps.currency rates", {
21105
+ v: ENVELOPE_VERSION,
21106
+ kind: "currencyRates",
21107
+ slug: callerSlug(env),
21108
+ key: env.QINTO_INTERNAL_KEY
21109
+ });
21110
+ return { rates: answer.rates, fetchedAt: answer.fetchedAt };
21111
+ };
21074
21112
  var buildIdentity = (env) => {
21075
21113
  return {
21076
21114
  resolve: (identifier, options) => {
@@ -21609,7 +21647,7 @@ var buildWorkflowContext = (env, stamp) => {
21609
21647
  depth: 0
21610
21648
  });
21611
21649
  return {
21612
- env,
21650
+ env: appEnv(env),
21613
21651
  install,
21614
21652
  startWorkflow: buildStartWorkflow(stamp),
21615
21653
  oauth: buildOauth(env, stamp.ord),
@@ -21682,12 +21720,13 @@ var buildContext = ({
21682
21720
  emits,
21683
21721
  pushes,
21684
21722
  captures,
21685
- context
21723
+ context,
21724
+ currency
21686
21725
  }) => {
21687
21726
  const { variables, secrets } = projectInstallValues(maps, kinds, install.ord);
21688
21727
  const subject = envelope.user?.anonId ?? null;
21689
21728
  return {
21690
- env,
21729
+ env: appEnv(env),
21691
21730
  site,
21692
21731
  install: { id: install.id, label: install.label },
21693
21732
  startWorkflow: buildStartWorkflow({
@@ -21751,10 +21790,45 @@ var buildContext = ({
21751
21790
  },
21752
21791
  observability: buildObservability(install.ord),
21753
21792
  identity: buildIdentity(env),
21793
+ currency,
21754
21794
  apps: buildAppsClient(env, envelope)
21755
21795
  };
21756
21796
  };
21757
21797
 
21798
+ // src/currency.ts
21799
+ var rateBetween = (rates, from, to) => {
21800
+ const source = from.trim().toUpperCase();
21801
+ const target = to.trim().toUpperCase();
21802
+ if (!Object.hasOwn(rates, source) || !Object.hasOwn(rates, target)) {
21803
+ return null;
21804
+ }
21805
+ return rates[source] / rates[target];
21806
+ };
21807
+ var buildCurrency = (env) => {
21808
+ let snapshot;
21809
+ const load = () => {
21810
+ snapshot ??= brokerCurrencyRates(env).catch((error61) => {
21811
+ throw new Error("the platform currency rates are not available", { cause: error61 });
21812
+ });
21813
+ return snapshot;
21814
+ };
21815
+ return {
21816
+ convert: async (amount, from, to) => {
21817
+ const rate = rateBetween((await load()).rates, from, to);
21818
+ return rate === null ? null : amount * rate;
21819
+ },
21820
+ rate: async (from, to) => {
21821
+ return rateBetween((await load()).rates, from, to);
21822
+ },
21823
+ list: async () => {
21824
+ return Object.keys((await load()).rates).sort();
21825
+ },
21826
+ updatedAt: async () => {
21827
+ return new Date((await load()).fetchedAt);
21828
+ }
21829
+ };
21830
+ };
21831
+
21758
21832
  // src/install-span.ts
21759
21833
  var installSpan = (install, run) => {
21760
21834
  return traced(
@@ -21851,10 +21925,11 @@ var dispatchSchedule = async (envelope, bundle, env) => {
21851
21925
  const appScope = projectAppValues(env, appVariableKinds);
21852
21926
  const event = { id: schedule.id, cron: schedule.cron, scheduledTime: new Date(schedule.scheduledTime) };
21853
21927
  const failed = [];
21928
+ const currency = buildCurrency(env);
21854
21929
  for (const install of schedule.installs) {
21855
21930
  const { variables, secrets } = projectInstallValues(maps, variableKinds, install.ord);
21856
21931
  const context = {
21857
- env,
21932
+ env: appEnv(env),
21858
21933
  site,
21859
21934
  install: { id: install.id, label: install.label },
21860
21935
  startWorkflow: buildStartWorkflow({
@@ -21870,6 +21945,7 @@ var dispatchSchedule = async (envelope, bundle, env) => {
21870
21945
  appSecrets: appScope.appSecrets,
21871
21946
  observability: buildObservability(install.ord),
21872
21947
  identity: buildIdentity(env),
21948
+ currency,
21873
21949
  apps: buildAppsClient(env, { depth: 0 }),
21874
21950
  query: buildQuery({ env, ticket: install.queryTicket, installId: install.id }),
21875
21951
  push: async (userId, message) => {
@@ -21986,7 +22062,7 @@ var buildContext2 = (envelope, env) => {
21986
22062
  const appScope = projectAppValues(env, appVariableKinds);
21987
22063
  const { variables, secrets } = projectInstallValues(maps, variableKinds, install.ord);
21988
22064
  return {
21989
- env,
22065
+ env: appEnv(env),
21990
22066
  site,
21991
22067
  install: { id: install.id, label: install.label },
21992
22068
  // An envelope from a portal that predates the role, or one carrying `full`, has no `appRole`.
@@ -22000,6 +22076,7 @@ var buildContext2 = (envelope, env) => {
22000
22076
  appSecrets: appScope.appSecrets,
22001
22077
  observability: buildObservability(install.ord),
22002
22078
  identity: buildIdentity(env),
22079
+ currency: buildCurrency(env),
22003
22080
  apps: buildAppsClient(env, { depth: 0 }),
22004
22081
  query: buildQuery({ env, ticket: envelope.tool.queryTicket, installId: install.id }),
22005
22082
  track: async (userId, event) => {
@@ -22063,12 +22140,11 @@ var controlContextValues = (env) => {
22063
22140
  appScope: projectAppValues(env, appVariableKinds)
22064
22141
  };
22065
22142
  };
22066
- var lifecycleContext = (env, envelope) => {
22067
- const install = envelope.install;
22143
+ var lifecycleContext = (env, install) => {
22068
22144
  const { maps, variableKinds, site, appScope } = controlContextValues(env);
22069
22145
  const { variables, secrets } = projectInstallValues(maps, variableKinds, install.ord);
22070
22146
  return {
22071
- env,
22147
+ env: appEnv(env),
22072
22148
  site,
22073
22149
  install: { id: install.id, label: install.label },
22074
22150
  variables,
@@ -22081,19 +22157,21 @@ var lifecycleContext = (env, envelope) => {
22081
22157
  installLabel: install.label,
22082
22158
  anonId: null
22083
22159
  }),
22084
- observability: buildObservability(install.ord)
22160
+ observability: buildObservability(install.ord),
22161
+ currency: buildCurrency(env)
22085
22162
  };
22086
22163
  };
22087
22164
  var migrationContext = (env) => {
22088
22165
  const { site, appScope } = controlContextValues(env);
22089
22166
  return {
22090
- env,
22167
+ env: appEnv(env),
22091
22168
  site,
22092
22169
  appVariables: appScope.appVariables,
22093
22170
  appSecrets: appScope.appSecrets,
22094
22171
  // A migration runs once for the site, not once per install, so it has no install to attribute
22095
22172
  // an event or a saved error to.
22096
- observability: buildObservability(null)
22173
+ observability: buildObservability(null),
22174
+ currency: buildCurrency(env)
22097
22175
  };
22098
22176
  };
22099
22177
  var runStatementMigration = async (env, migration) => {
@@ -22114,22 +22192,43 @@ var runStatementMigration = async (env, migration) => {
22114
22192
  const ledger = database.prepare("INSERT INTO _qinto_migrations (id, checksum, phase, applied_at) VALUES (?, ?, ?, ?)").bind(migration.id, migration.checksum, migration.phase, (/* @__PURE__ */ new Date()).toISOString());
22115
22193
  await database.batch([...migration.statements.map((statement) => database.prepare(statement)), ledger]);
22116
22194
  };
22117
- var dispatchLifecycle = async (envelope, bundle, env) => {
22118
- const event = envelope.lifecycle;
22119
- const handler = bundle.onLifecycle?.[event.type];
22120
- if (typeof handler !== "function") {
22121
- return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: true });
22122
- }
22123
- const install = envelope.install;
22195
+ var runInstallHook = async (install, run, env) => {
22124
22196
  try {
22125
22197
  await installSpan(install, () => {
22126
- return handler(event, lifecycleContext(env, envelope));
22198
+ return run(lifecycleContext(env, install));
22127
22199
  });
22128
22200
  return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: true });
22129
22201
  } catch (error61) {
22130
22202
  return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: false, error: { message: failureMessage(error61) } });
22131
22203
  }
22132
22204
  };
22205
+ var dispatchLifecycle = async (envelope, bundle, env) => {
22206
+ const event = envelope.lifecycle;
22207
+ const handler = bundle.onLifecycle?.[event.type];
22208
+ if (typeof handler !== "function") {
22209
+ return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: true });
22210
+ }
22211
+ return runInstallHook(
22212
+ envelope.install,
22213
+ (context) => {
22214
+ return handler(event, context);
22215
+ },
22216
+ env
22217
+ );
22218
+ };
22219
+ var dispatchDeleteRequest = async (envelope, bundle, env) => {
22220
+ const handler = bundle.onDeleteRequest;
22221
+ if (typeof handler !== "function") {
22222
+ return json2(200, { v: CONTROL_ENVELOPE_VERSION, ok: false, skipped: true });
22223
+ }
22224
+ return runInstallHook(
22225
+ envelope.install,
22226
+ (context) => {
22227
+ return handler(envelope.deleteRequest, context);
22228
+ },
22229
+ env
22230
+ );
22231
+ };
22133
22232
  var dispatchMigration = async (envelope, bundle, env) => {
22134
22233
  const migration = envelope.migration;
22135
22234
  if (linkedDatabasesOf(env).includes(migration.resource)) {
@@ -22184,6 +22283,9 @@ var dispatchControl = async ({ request, env, versions }) => {
22184
22283
  case "lifecycle": {
22185
22284
  return dispatchLifecycle(envelope, bundle, env);
22186
22285
  }
22286
+ case "deleteRequest": {
22287
+ return dispatchDeleteRequest(envelope, bundle, env);
22288
+ }
22187
22289
  case "migration": {
22188
22290
  return dispatchMigration(envelope, bundle, env);
22189
22291
  }
@@ -22234,11 +22336,12 @@ var buildContext3 = ({
22234
22336
  site,
22235
22337
  geo,
22236
22338
  params,
22237
- user
22339
+ user,
22340
+ currency
22238
22341
  }) => {
22239
22342
  const { variables, secrets } = projectInstallValues(maps, kinds, install.ord);
22240
22343
  return {
22241
- env,
22344
+ env: appEnv(env),
22242
22345
  site,
22243
22346
  geo,
22244
22347
  params,
@@ -22257,6 +22360,7 @@ var buildContext3 = ({
22257
22360
  appSecrets: appScope.appSecrets,
22258
22361
  observability: buildObservability(install.ord),
22259
22362
  identity: buildIdentity(env),
22363
+ currency,
22260
22364
  apps: buildAppsClient(env, { depth: 0 }),
22261
22365
  push: async (userId, message) => {
22262
22366
  await brokerPush(env, {
@@ -22349,6 +22453,7 @@ var dispatchEndpoint = async ({ request, env, versions }) => {
22349
22453
  const maps = parseVariableMaps(env);
22350
22454
  const { variableKinds, appVariableKinds, site } = appContextOf(env);
22351
22455
  const appScope = projectAppValues(env, appVariableKinds);
22456
+ const currency = buildCurrency(env);
22352
22457
  const invoke = async (install) => {
22353
22458
  return installSpan(install, () => {
22354
22459
  return handler(
@@ -22362,7 +22467,8 @@ var dispatchEndpoint = async ({ request, env, versions }) => {
22362
22467
  site,
22363
22468
  geo: context.geo,
22364
22469
  params: context.params,
22365
- user: context.user
22470
+ user: context.user,
22471
+ currency
22366
22472
  })
22367
22473
  );
22368
22474
  });
@@ -22505,6 +22611,69 @@ var checkAgainst = async (schema, value) => {
22505
22611
  return { ok: false, message };
22506
22612
  };
22507
22613
 
22614
+ // src/rpc.ts
22615
+ var dispatchRpc = async (envelope, bundle, env) => {
22616
+ if (envelope.installs.length > 1) {
22617
+ return jsonError(400, "rpc envelope carries more than one install");
22618
+ }
22619
+ const rpc = envelope.rpc;
22620
+ if (rpc === void 0) {
22621
+ return jsonError(400, "rpc envelope carries no rpc call");
22622
+ }
22623
+ const definition = rpcDefinitionOf(bundle?.rpc?.[rpc.fn]);
22624
+ if (definition === void 0) {
22625
+ return jsonError(200, `missing handler: rpc.${rpc.fn}`);
22626
+ }
22627
+ const args = await checkAgainst(definition.input, rpc.args);
22628
+ if (!args.ok) {
22629
+ return json2(200, { v: ENVELOPE_VERSION, error: { code: "invalid_arguments", message: failureMessage(args.message) } });
22630
+ }
22631
+ const install = envelope.installs[0];
22632
+ const emits = [];
22633
+ const pushes = [];
22634
+ const captures = [];
22635
+ const context = contextCollector();
22636
+ const maps = parseVariableMaps(env);
22637
+ const { variableKinds: kinds, appVariableKinds, site } = appContextOf(env);
22638
+ const appScope = projectAppValues(env, appVariableKinds);
22639
+ const currency = buildCurrency(env);
22640
+ const subject = envelope.user?.anonId ?? null;
22641
+ const ctx = {
22642
+ ...buildContext({ env, envelope, site, install, maps, kinds, appScope, emits, pushes, captures, context, currency }),
22643
+ context: fetchedContext(
22644
+ env,
22645
+ (what) => {
22646
+ return requireSubject(subject, what);
22647
+ },
22648
+ context
22649
+ )
22650
+ };
22651
+ let value;
22652
+ try {
22653
+ value = await installSpan(install, () => {
22654
+ return definition.handler(args.value, ctx);
22655
+ });
22656
+ } catch (error61) {
22657
+ const message = failureMessage(error61);
22658
+ console.warn(`rpc failed for install ${install.id}: ${message}`);
22659
+ return json2(200, { v: ENVELOPE_VERSION, error: { message } });
22660
+ }
22661
+ const checked = await checkAgainst(definition.output, value);
22662
+ if (!checked.ok) {
22663
+ return jsonError(200, failureMessage(`rpc result does not match the output schema: ${checked.message}`));
22664
+ }
22665
+ const result = checked.value === void 0 ? null : checked.value;
22666
+ if (new TextEncoder().encode(JSON.stringify(result)).length > MAX_RPC_BYTES) {
22667
+ return jsonError(200, "rpc result exceeds the size cap");
22668
+ }
22669
+ const body = { v: ENVELOPE_VERSION, rpc: { result } };
22670
+ const contextWrites = context.writes();
22671
+ if (emits.length > 0 || pushes.length > 0 || captures.length > 0 || contextWrites.length > 0) {
22672
+ body.effects = [{ ord: install.ord, emits, pushes, captures, context: contextWrites }];
22673
+ }
22674
+ return json2(200, body);
22675
+ };
22676
+
22508
22677
  // src/traced-fetch.ts
22509
22678
  var MAX_BODY_CHARS = 65536;
22510
22679
  var TOO_LARGE = `[not recorded: the body is over ${MAX_BODY_CHARS} characters]`;
@@ -22656,7 +22825,7 @@ var dispatchUi = async ({ request, env, versions }) => {
22656
22825
  const { variables, secrets } = projectInstallValues(maps, variableKinds, context.install.ord);
22657
22826
  const { appVariables, appSecrets } = projectAppValues(env, appVariableKinds);
22658
22827
  const ctx = {
22659
- env,
22828
+ env: appEnv(env),
22660
22829
  site,
22661
22830
  install: { id: context.install.id, label: context.install.label },
22662
22831
  installs: projectAllInstallValues(maps, variableKinds, context.installs),
@@ -22716,7 +22885,7 @@ var withAppDurableObject = (Base, migrations) => {
22716
22885
  return class extends Base {
22717
22886
  constructor(ctx, env) {
22718
22887
  applyDurableObjectMigrations(ctx.storage, migrations);
22719
- super(ctx, env);
22888
+ super(ctx, appEnv(env));
22720
22889
  }
22721
22890
  };
22722
22891
  };
@@ -22742,12 +22911,13 @@ var runInstalls = async (envelope, env, invoke) => {
22742
22911
  const appScope = projectAppValues(env, appVariableKinds);
22743
22912
  const groups = [];
22744
22913
  let failure2;
22914
+ const currency = buildCurrency(env);
22745
22915
  for (const install of envelope.installs) {
22746
22916
  const emits = [];
22747
22917
  const pushes = [];
22748
22918
  const captures = [];
22749
22919
  const context = contextCollector();
22750
- const ctx = buildContext({ env, envelope, site, install, maps, kinds, appScope, emits, pushes, captures, context });
22920
+ const ctx = buildContext({ env, envelope, site, install, maps, kinds, appScope, emits, pushes, captures, context, currency });
22751
22921
  try {
22752
22922
  await installSpan(install, () => {
22753
22923
  return invoke(ctx, install);
@@ -22808,7 +22978,8 @@ var runTransform = async (envelope, env, action, handler) => {
22808
22978
  emits,
22809
22979
  pushes,
22810
22980
  captures,
22811
- context
22981
+ context,
22982
+ currency: buildCurrency(env)
22812
22983
  });
22813
22984
  const ctx = {
22814
22985
  ...withoutApps,
@@ -22860,66 +23031,6 @@ var dispatchTransform = async (envelope, bundle, env) => {
22860
23031
  }
22861
23032
  return runTransform(envelope, env, action, handler);
22862
23033
  };
22863
- var dispatchRpc = async (envelope, bundle, env) => {
22864
- if (envelope.installs.length > 1) {
22865
- return jsonError(400, "rpc envelope carries more than one install");
22866
- }
22867
- const rpc = envelope.rpc;
22868
- if (rpc === void 0) {
22869
- return jsonError(400, "rpc envelope carries no rpc call");
22870
- }
22871
- const definition = rpcDefinitionOf(bundle?.rpc?.[rpc.fn]);
22872
- if (definition === void 0) {
22873
- return jsonError(200, `missing handler: rpc.${rpc.fn}`);
22874
- }
22875
- const args = await checkAgainst(definition.input, rpc.args);
22876
- if (!args.ok) {
22877
- return json2(200, { v: ENVELOPE_VERSION, error: { code: "invalid_arguments", message: failureMessage(args.message) } });
22878
- }
22879
- const install = envelope.installs[0];
22880
- const emits = [];
22881
- const pushes = [];
22882
- const captures = [];
22883
- const context = contextCollector();
22884
- const maps = parseVariableMaps(env);
22885
- const { variableKinds: kinds, appVariableKinds, site } = appContextOf(env);
22886
- const appScope = projectAppValues(env, appVariableKinds);
22887
- const subject = envelope.user?.anonId ?? null;
22888
- const ctx = {
22889
- ...buildContext({ env, envelope, site, install, maps, kinds, appScope, emits, pushes, captures, context }),
22890
- context: fetchedContext(
22891
- env,
22892
- (what) => {
22893
- return requireSubject(subject, what);
22894
- },
22895
- context
22896
- )
22897
- };
22898
- let value;
22899
- try {
22900
- value = await installSpan(install, () => {
22901
- return definition.handler(args.value, ctx);
22902
- });
22903
- } catch (error61) {
22904
- const message = failureMessage(error61);
22905
- console.warn(`rpc failed for install ${install.id}: ${message}`);
22906
- return json2(200, { v: ENVELOPE_VERSION, error: { message } });
22907
- }
22908
- const checked = await checkAgainst(definition.output, value);
22909
- if (!checked.ok) {
22910
- return jsonError(200, failureMessage(`rpc result does not match the output schema: ${checked.message}`));
22911
- }
22912
- const result = checked.value === void 0 ? null : checked.value;
22913
- if (new TextEncoder().encode(JSON.stringify(result)).length > MAX_RPC_BYTES) {
22914
- return jsonError(200, "rpc result exceeds the size cap");
22915
- }
22916
- const body = { v: ENVELOPE_VERSION, rpc: { result } };
22917
- const contextWrites = context.writes();
22918
- if (emits.length > 0 || pushes.length > 0 || captures.length > 0 || contextWrites.length > 0) {
22919
- body.effects = [{ ord: install.ord, emits, pushes, captures, context: contextWrites }];
22920
- }
22921
- return json2(200, body);
22922
- };
22923
23034
  var dispatch = async (envelope, versions, env) => {
22924
23035
  if (!(envelope.app.version in versions)) {
22925
23036
  return jsonError(200, "unknown version");
@@ -0,0 +1 @@
1
+ (function(){var e=()=>{},t=t=>{t.config?.autoCapture?.pageLoad===!0&&t.q.push([`track`,[`PageView`],e,e])},n=class extends Error{constructor(e){super(e),this.name=`QintoError`}},r=class extends n{constructor(e){super(e),this.name=`QintoTimeoutError`}},i=(e=Date.now())=>`evt_${globalThis.crypto.randomUUID()}_${e}`,a=(e,t)=>{if(e!==void 0)return e;let n=t?.orderId;return typeof n==`string`&&n!==``?n:i()},o=[`necessary`,`functional`,`analytics`,`marketing`,`salesOfData`],s=[`then`,`ready`,`config`],c=()=>{},l=(e,t)=>(...n)=>{let r=new Promise((r,i)=>{e.q.push([t,n,r,i])});return r.catch(c),r},u=e=>{let t=e;return t._q=1,t.q=[],new Proxy(t,{get:(e,n)=>typeof n!=`string`||s.includes(n)||n in t?Reflect.get(t,n):(t[n]=l(t,n),t[n])})},d=()=>{let e=document.currentScript?.dataset.qintoGlobal;return typeof e==`string`&&e!==``?e:`qinto`},f=e=>typeof e==`object`&&!!e&&e._q===1,p=(e=`qinto`)=>{let t=globalThis;if(e in t){let n=t[e];if(!f(n))return null;let r=Array.isArray(n.q)?n:u(n);return r!==n&&Object.defineProperty(t,e,{value:r,writable:!0,enumerable:!0,configurable:!0}),r}let n=u({});return Object.defineProperty(t,e,{value:n,writable:!0,enumerable:!0,configurable:!0}),n},m=(e,t)=>async(r,i)=>{let a=Reflect.get(e,r);if(typeof a!=`function`)throw new n(`[Qinto] ${t}.${r} is not a method of this tag.`);return a.apply(e,Array.from(i))},h=e=>()=>Promise.reject(e),g=(e,t)=>{let[n,r,i,a]=t;e(n,r).then(i,a)},ee=e=>{let t=[];return t.push=(...t)=>{for(let n of t)g(e,n);return 0},t},_=(e,t,n=`window.qinto`)=>{let r=e.ready;if(r!==void 0)return r;let i=e,a=e=>{let t=i.q;i.q=ee(e);for(let n of t)g(e,n)},o=t().then(e=>{a(m(e,n))},e=>{throw a(h(e)),e});return o.catch(c),i.ready=o,o},v=(e,t,n)=>n===void 0||!o.some(e=>e===n)?!1:n===`necessary`||e!==null&&e.categories[n]===!0&&e.apps[t]!==!1,y=()=>{},te=100,ne=1024,re=32768,b=(e,t,n)=>e.target.config?.consentConfiguration?.enabled!==!0||v(n,t,e.target.config?.apps?.[t]?.category),x=e=>Object.values(e.config?.apps??{}).some(e=>e.category!==`necessary`||e.capture!==void 0),S=(e,t)=>{let{app:n,install:r,topic:i,to:a}=e;return typeof n!=`string`||typeof i!=`string`||r!==void 0&&typeof r!=`string`||t&&!(Array.isArray(a)&&a.every(e=>typeof e==`string`))},ie=(e,t,n,r)=>r===null?e.slug===t&&(n===void 0||n===e.installId):r.has(e.slug),C=(e,t)=>{console.error(`[Qinto] A handler of app "${e.slug}" (install "${e.installId}") failed.`,t)},w=(e,t,n)=>{for(let r of t)try{let t=r(n);t instanceof Promise&&t.catch(t=>{C(e,t)})}catch(t){C(e,t)}},ae=(e,t,n,r)=>{e.activated.add(r.id);let i={slug:t,installId:r.id,eventHandlers:[],consentHandlers:[],pushHandlers:[],sharedContextHandlers:[]};e.instances.push(i);let a={variables:r.variables,install:{id:r.id,label:r.label},getSession:async()=>{let n=await e.client.getSession();return e.target.config?.apps?.[t]?.geo===!0?n:{...n,ip:null,geo:null}},track:(t,n)=>{e.client.track(t,n).catch(y)},onEvent:e=>{i.eventHandlers.push(e)},onConsent:e=>{i.consentHandlers.push(e)},onPush:e=>{i.pushHandlers.push(e)},fetch:async(n,i)=>{let a=new URL(`${e.client.destination}/app/${t}${n}`);if(!n.startsWith(`/`)||!a.pathname.startsWith(`/app/${t}/`))throw TypeError(`[Qinto] App "${t}" fetched "${n}"; the path must be absolute and stay inside the app.`);let o=new Headers(i?.headers);return o.set(`x-qinto-install`,r.id),fetch(a,{...i,headers:o,credentials:`include`})},onSharedContext:n=>{if(e.target.config?.apps?.[t]?.sharedContext!==!0){console.error(`[Qinto] App "${t}" reads shared context without declaring the sharedContext capability.`);return}i.sharedContextHandlers.push(n),e.sharedContext!==null&&oe(e,t)&&w(i,[n],e.sharedContext)}};try{n(a)}catch(e){console.error(`[Qinto] App "${t}" failed to activate install "${r.id}".`,e)}},T=(e,t,n)=>{if(!b(e,t,e.consent))return;let r=((`_appInstalls`in e.target?e.target._appInstalls:void 0)?.[t]??[]).filter(t=>!e.activated.has(t.id));if(r.length===0)return;let i=n.factory;if(i===void 0){try{i=n.loader()}catch(e){i=null,console.error(`[Qinto] App "${t}" failed to load.`,e)}e.apps.set(t,{...n,factory:i})}if(i!==null)for(let n of r)ae(e,t,i,n)},oe=(e,t)=>b(e,t,e.consent)?e.sharedContextConsent===void 0||b(e,t,e.sharedContextConsent):!1,se=e=>{let{categories:t,apps:n}=e;if(typeof t==`object`&&t&&typeof n==`object`&&n)return{categories:t,apps:n}},ce=(e,t)=>{let n=e;n.consent=t,n.sharedContextConsent=void 0;for(let n of e.instances)w(n,n.consentHandlers,t);e.onConsentSettled?.(t);for(let[t,n]of e.apps)T(e,t,n)},E=async e=>{try{ce(e,await e.client.getConsent())}catch(e){console.error(`[Qinto] The apps consent read failed.`,e)}},D=e=>{let t;try{t=JSON.stringify(e)}catch{return}if(!(t===void 0||new TextEncoder().encode(t).length>ne))return JSON.parse(t)},O=e=>{let t={};for(let n of e){if(n.settled!==n.total||n.values.length!==1)continue;let e=D(n.values[0]);if(e===void 0)continue;let r={...t[n.instance.slug]??{},[n.instance.installId]:e},i={...t,[n.instance.slug]:r};new TextEncoder().encode(JSON.stringify(i)).length<=re&&(t[n.instance.slug]=r)}return Object.keys(t).length===0?void 0:t},k=async(e,t)=>{e.onPageEvent?.(e.consent);let n=[],r=[];for(let i of e.instances){if(!b(e,i.slug,e.consent))continue;if(e.target.config?.apps?.[i.slug]?.eventResults?.includes(t.type)!==!0){w(i,i.eventHandlers,t);continue}let a={instance:i,total:i.eventHandlers.length,settled:0,values:[]};n.push(a);for(let e of i.eventHandlers){let n;try{n=e(t)}catch(e){a.settled+=1,C(i,e);continue}r.push(Promise.resolve(n).then(e=>{a.settled+=1,e!==void 0&&a.values.push(e)},e=>{a.settled+=1,C(i,e)}))}}if(r.length>0){let e=y,t=new Promise(t=>{e=t}),n=setTimeout(e,te);await Promise.race([Promise.all(r),t]),clearTimeout(n)}return O(n)},A=e=>{let{client:t}=e;return{track:async(n,r,i)=>{let o=a(i?.eventId,r),s=t.actionContext(),c=await k(e,{type:`track`,event:n,eventId:o,properties:r??{}});return await t.submitTrack({event:n,eventId:o,properties:r,context:s,appResults:c}),{eventId:o}},identify:async(n,r)=>{let i=t.actionContext(),a=await k(e,{type:`identify`,event:`identify`,properties:n??{}});return t.submitIdentify({traits:n,id:r,context:i,appResults:a})},alias:t.alias,consent:async n=>{if(await t.submitConsent(n,t.actionContext()),e.onConsentMutation?.(),x(e.target)){let t=e;t.consent=null,await E(e)}},getAnonId:t.getAnonId,getConsent:t.getConsent,getSession:t.getSession,close:t.close}},j=(e,t)=>{let n=new Map,r=null,i=null,a=e=>{let[t,i]=e;if(n.has(t))return;let a={loader:i};n.set(t,a),r?.(t,a)},o=e,s=`_apps`in o?o._apps:void 0,c=Array.isArray(s)?s:[];c.push=(...e)=>{for(let t of e)a(t);return 0},o._apps=c;for(let e of c.splice(0))a(e);return{sharedContext:e=>{let{context:t}=e;if(typeof t!=`object`||!t||Array.isArray(t)){console.error(`[Qinto] The server sent a malformed shared context.`,e);return}let n=i;if(n===null)return;n.sharedContext=t;let r=se(e),a=r??n.consent;n.sharedContextConsent=r;for(let e of n.instances)b(n,e.slug,a)&&w(e,e.sharedContextHandlers,n.sharedContext)},push:e=>{let{app:t,install:n,topic:r,scope:a,to:o}=e,s=a===`site`;if(S(e,s)){console.error(`[Qinto] The server sent a malformed app push.`,e);return}let c=i;if(c===null)return;let l=s?new Set(o):null,u={topic:r,data:e.data,from:t};for(let e of c.instances)ie(e,t,n,l)&&b(c,e.slug,c.consent)&&w(e,e.pushHandlers,u)},connect:async a=>{let o={target:e,client:a,apps:n,instances:[],activated:new Set,consent:null,sharedContext:i?.sharedContext??null,sharedContextConsent:i?.sharedContextConsent,onConsentSettled:t?.onConsentSettled,onConsentMutation:t?.onConsentMutation,onPageEvent:t?.onPageEvent};i=o,r=(e,t)=>{T(o,e,t),o.consent===null&&o.target.config?.consentConfiguration?.enabled===!0&&o.target.config?.apps?.[e]?.category!==`necessary`&&E(o)};for(let[e,t]of n)T(o,e,t);return e.config?.consentConfiguration?.enabled===!0&&x(e)?await E(o):o.onConsentSettled?.(o.consent),A(o)}}},M=512,N=864e5,P=e=>Object.fromEntries(Object.entries(e).map(([e,t])=>[e,Object.fromEntries(Object.entries(t).map(([e,t])=>[e,t.value]))])),F=(e,t)=>`qinto:cap:${encodeURIComponent(e)}:${encodeURIComponent(t)}`,I=e=>{try{let t=window.sessionStorage.getItem(e);if(t===null)return null;let n=JSON.parse(t);return typeof n.at!=`number`||typeof n.anonId!=`string`||typeof n.apps!=`object`||n.apps===null||Date.now()-n.at>N?null:{at:n.at,anonId:n.anonId,apps:n.apps}}catch{return null}},L=(e,t)=>{try{window.sessionStorage.setItem(e,JSON.stringify(t))}catch{}},R=e=>{try{window.sessionStorage.removeItem(e)}catch{}},z=e=>{try{return decodeURIComponent(e)}catch{return e}},B=()=>{let e={};for(let t of document.cookie.split(`;`)){let n=t.indexOf(`=`);n>0&&(e[t.slice(0,n).trim()]=z(t.slice(n+1)))}return e},V=(e,t)=>{let n={};for(let r of e.queryParams){let e=t.params.get(r);e!==null&&e.length<=M&&(n[r]={value:e,source:`query`})}for(let r of e.cookies){let e=t.cookies[r];e!==void 0&&e.length<=M&&(n[r]={value:e,source:`cookie`})}return n},H=(e,t)=>{let n={params:new URLSearchParams(window.location.search),cookies:B()},r={};for(let[i,a]of Object.entries(e.config?.apps??{})){if(a.capture===void 0||!v(t,i,a.category))continue;let e=V(a.capture,n);Object.keys(e).length>0&&(r[i]=e)}return r},U=(e,t)=>{let n={};for(let[r,i]of Object.entries(e)){let e=t[r]??{},a={};for(let[t,n]of Object.entries(i))(e[t]?.value!==n.value||e[t]?.source!==n.source)&&(a[t]=n);Object.keys(a).length>0&&(n[r]=a)}return n},W=(e,t,n,r)=>{let i=F(r,n),a=I(i),o=a?.apps??{},s=a?.anonId??null,c=a===null,l={},u=0,d=(e,t)=>{s!==null&&s!==e.anonId&&(o={}),s=e.anonId;let n=new Set(Object.entries(e.accepted).flatMap(([e,t])=>t.map(t=>`${e}\u0000${t}`)));for(let[e,r]of Object.entries(t))for(let[t,i]of Object.entries(r))n.has(`${e}\u0000${t}`)&&(o[e]={...o[e],[t]:i});L(i,{at:Date.now(),anonId:s,apps:o})},f=null,p=()=>{u+=1,o={},l={},R(i)},m=n=>{if(f=n,!c)return;let r=U(H(e,n),{...o,...l});if(Object.keys(r).length===0)return;let i=u;l={...l,...r},t(P(r)).then(e=>{i===u&&d(e,r)}).catch(()=>{}).finally(()=>{l={}})};return{settled:e=>{m(e)},needsAnon:()=>!c,observedAnon:e=>{(e===null||s!==null&&s!==e)&&p(),s=e,c=!0,m(f)},clear:p}},G=Symbol.for(`qinto.connectGate`),K=()=>{},le=e=>{let t=globalThis,n=(t[G]??Promise.resolve()).then(e);return t[G]=n.then(K,K),n},q=class extends n{constructor(e){super(e),this.name=`QintoProtocolError`}},ue=e=>{if(typeof e!=`string`)return null;let t;try{t=JSON.parse(e)}catch{return null}if(typeof t!=`object`||!t)return null;let n=t;return n.t===`ack`||n.t===`error`||n.t===`consentState`||n.t===`session`||n.t===`captureAck`||n.t===`appPush`||n.t===`sharedContext`?t:null},de=e=>{if(e.t===`consentState`)return{categories:e.categories,apps:e.apps};if(e.t===`session`)return{anonId:e.anonId,globalId:e.globalId,ip:e.ip,geo:e.geo};if(e.t===`captureAck`)return{anonId:e.anonId,accepted:e.accepted}},fe=e=>{let t=new Map,n=e=>{let n=t.get(e);if(n!==void 0)return clearTimeout(n.timer),t.delete(e),n};return{submit:n=>new Promise((i,a)=>{let o={frame:n,sent:e.send(n),resolve:i,reject:a,timer:setTimeout(()=>{t.delete(n.ref),a(new r(`The Qinto request timed out.`))},e.requestTimeoutMs)};t.set(n.ref,o)}),flush:()=>{for(let n of t.values())n.sent||=e.send(n.frame)},deliver:e=>{let t=e.ref===void 0?void 0:n(e.ref);if(t!==void 0){if(e.t===`error`){t.reject(new q(e.message));return}t.resolve(de(e))}},rejectAll:e=>{let n=[...t.values()];t.clear();for(let t of n)clearTimeout(t.timer),t.reject(e)}}},J=()=>{let e=document.referrer;return{url:window.location.href,...e===``?{}:{referrer:e}}},pe=e=>new WebSocket(e),me=500,he=3e4,Y=4096,ge=8192,_e=(e,t)=>{if(t.url.length>Y)return e;let n=new URLSearchParams({url:t.url});t.referrer!==void 0&&t.referrer.length<=Y&&n.set(`referrer`,t.referrer);let r=`${e}?${n}`;return r.length<=ge?r:e},ve=`session not initialized`,ye=(e,t)=>{let n=Math.min(me*2**e,he);return Math.round(n/2+n/2*t)},be=(e,t)=>e.t===`appPush`?(t.onAppPush?.(e),!0):e.t===`sharedContext`&&(t.onSharedContext?.(e),!0),xe=e=>{let t=`${e.destination.replace(/\/+$/,``)}/open`,n=null,r=null,i=0,a=null,o=fe({send:e=>n===null||n.readyState!==1?!1:(n.send(JSON.stringify(e)),!0),requestTimeoutMs:e.requestTimeoutMs}),s=t=>{e.onError?.(t)},c=()=>{if(r!==null&&(clearTimeout(r),r=null),n!==null){let e=n;n=null,e.close()}o.rejectAll(new q(`connection closed`))},l=()=>{let u=pe(_e(t,J()));n=u,u.onopen=()=>{i=0;let e=a;a=null,o.flush(),e?.resolve()},u.onmessage=t=>{let n=ue(t.data);if(n===null){s(new q(`invalid message`));return}be(n,e)||(`anonId`in n&&typeof n.anonId==`string`&&e.onAnonId?.(n.anonId),o.deliver(n),n.t===`error`&&n.message===ve&&u.close())},u.onerror=()=>{s(new q(`connection closed`))},u.onclose=()=>{if(n!==u)return;n=null;let e=a;if(e!==null){a=null,c(),e.reject(new q(`connection closed`));return}o.rejectAll(new q(`connection closed`)),r=setTimeout(()=>{r=null,l()},ye(i,Math.random())),i+=1}};return{connect:()=>new Promise((e,t)=>{a={resolve:e,reject:t},l()}),submit:e=>o.submit(e),close:()=>{c()}}},Se=1e4,Ce=()=>{let e=0;return()=>(e+=1,`q${e}`)},X=(e,t,n)=>{let r=null,i=()=>({occurredAt:Date.now(),page:J()}),o=async()=>{let n=await e.submit({t:`getSession`,ref:t()});return r=n.anonId,n};return{destination:n.replace(/\/+$/,``),actionContext:i,submitIdentify:async({traits:n,id:r,context:i,appResults:a})=>{await e.submit({t:`identify`,ref:t(),traits:n,id:r,context:i,appResults:a})},submitTrack:async({event:n,eventId:r,properties:i,context:a,appResults:o})=>{await e.submit({t:`track`,ref:t(),event:n,eventId:r,properties:i,context:a,appResults:o})},submitConsent:async(n,r)=>{await e.submit({t:`consent`,ref:t(),categories:n.categories,apps:n.apps,context:r})},track:async(n,r,o)=>{let s=a(o?.eventId,r);return await e.submit({t:`track`,ref:t(),event:n,eventId:s,properties:r,context:i()}),{eventId:s}},alias:async n=>{await e.submit({t:`alias`,ref:t(),alias:n})},getConsent:async()=>await e.submit({t:`getConsent`,ref:t()}),getSession:o,getAnonId:async()=>(r===null&&await o(),r),close:async()=>{e.close()}}},we=async e=>{let t=xe({destination:e.destination,requestTimeoutMs:e.requestTimeoutMs??Se,onError:e.onError,onAppPush:e.onAppPush,onSharedContext:e.onSharedContext,onAnonId:e.onAnonId});await le(()=>t.connect());let n=Ce();return{client:X(t,n,e.destination),submitCapture:async e=>await t.submit({t:`capture`,ref:n(),apps:e})}},Te=(e,t)=>{let n=null,r=j(e,{onConsentSettled:e=>{n?.settled(e)},onConsentMutation:()=>{n?.clear()},onPageEvent:e=>{n?.settled(e)}});return{connect:async i=>{let a=await we({...i,onAppPush:r.push,onSharedContext:r.sharedContext,onAnonId:e=>{n?.observedAnon(e)}});n=W(e,a.submitCapture,i.destination,t);let o=await r.connect(a.client);return n.needsAnon()&&n.observedAnon((await a.client.getSession()).anonId),o}}},Z=d(),Q=p(Z),$=`window.${Z}`;if(Q!==null&&Q.ready===void 0){let e=Te(Q,Z);t(Q),_(Q,async()=>{let t=Q.config?.destination;if(typeof t!=`string`||t===``)throw Error(`[Qinto] ${$}.config.destination is missing.`);return e.connect({destination:t})},$)}})();