@blotout/qinto-cli 0.1.5 → 0.1.7
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 +49 -16
- package/dist/index.js +4218 -3652
- package/dist/index.js.map +1 -1
- package/dist/worker-artifact/app-runtime/index.js +75 -31
- package/dist/worker-artifact/apps-ui/index.js +6 -0
- package/dist/worker-artifact/main/index.js +111 -54
- package/dist/worker-artifact/version.json +6 -6
- package/package.json +1 -1
- package/templates/app/full/qinto.app.jsonc +4 -1
|
@@ -14584,7 +14584,7 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
14584
14584
|
globalId: external_exports.string().min(1).max(64).nullable(),
|
|
14585
14585
|
properties: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
14586
14586
|
geo: external_exports.record(external_exports.string(), external_exports.string()).optional()
|
|
14587
|
-
}).strict(),
|
|
14587
|
+
}).strict().optional(),
|
|
14588
14588
|
consent: external_exports.object({ enabled: external_exports.boolean(), categories: external_exports.record(external_exports.string(), external_exports.boolean()), apps: external_exports.record(external_exports.string(), external_exports.boolean()) }).strict().optional(),
|
|
14589
14589
|
geo: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
|
|
14590
14590
|
// Only what this app captured or wrote for this visitor; `updatedAt` is when the current value
|
|
@@ -14608,7 +14608,11 @@ var envelopeRequestSchema = external_exports.object({
|
|
|
14608
14608
|
browser: external_exports.record(external_exports.string().min(1).max(64), external_exports.unknown()).optional(),
|
|
14609
14609
|
origin: external_exports.object({ app: external_exports.string().min(1).max(30), install: external_exports.number().int().min(1), depth: external_exports.number().int().min(0) }).strict().optional()
|
|
14610
14610
|
}).strict().optional()
|
|
14611
|
-
}).strict()
|
|
14611
|
+
}).strict().superRefine((envelope, ctx) => {
|
|
14612
|
+
if (envelope.kind !== "rpc" && envelope.user === void 0) {
|
|
14613
|
+
ctx.addIssue({ code: "custom", path: ["user"], message: `A ${envelope.kind} envelope needs a user` });
|
|
14614
|
+
}
|
|
14615
|
+
});
|
|
14612
14616
|
var envelopeResponseSchema = external_exports.object({
|
|
14613
14617
|
v: external_exports.literal(ENVELOPE_VERSION),
|
|
14614
14618
|
// Transform invocations only: pass the action through, drop it, or replace its payload (§5.4).
|
|
@@ -15361,6 +15365,7 @@ var brokerActive = async (env, input) => {
|
|
|
15361
15365
|
};
|
|
15362
15366
|
var buildAppsClient = (env, envelope) => {
|
|
15363
15367
|
const caller = callerSlug(env);
|
|
15368
|
+
const subject = envelope.user?.anonId;
|
|
15364
15369
|
return {
|
|
15365
15370
|
call: async (callee, fn, args, options) => {
|
|
15366
15371
|
const answer = await askBroker(env, `apps.call ${callee}.${fn}`, {
|
|
@@ -15368,7 +15373,9 @@ var buildAppsClient = (env, envelope) => {
|
|
|
15368
15373
|
kind: "rpc",
|
|
15369
15374
|
slug: caller,
|
|
15370
15375
|
key: env.QINTO_INTERNAL_KEY,
|
|
15371
|
-
|
|
15376
|
+
// An endpoint, schedule or panel calls with no visitor; the broker then reaches the callee
|
|
15377
|
+
// with no user block and its ctx.user is null.
|
|
15378
|
+
...subject === void 0 ? {} : { userId: subject },
|
|
15372
15379
|
callee,
|
|
15373
15380
|
fn,
|
|
15374
15381
|
args,
|
|
@@ -15430,6 +15437,14 @@ var buildObservability = () => {
|
|
|
15430
15437
|
};
|
|
15431
15438
|
};
|
|
15432
15439
|
|
|
15440
|
+
// src/subject.ts
|
|
15441
|
+
var requireSubject = (anonId, what) => {
|
|
15442
|
+
if (anonId === null) {
|
|
15443
|
+
throw new Error(`${what} needs a visitor, and this invocation has none.`);
|
|
15444
|
+
}
|
|
15445
|
+
return anonId;
|
|
15446
|
+
};
|
|
15447
|
+
|
|
15433
15448
|
// src/variables.ts
|
|
15434
15449
|
var parseVariableMaps = (env) => {
|
|
15435
15450
|
const maps = /* @__PURE__ */ new Map();
|
|
@@ -15565,9 +15580,9 @@ var readOnce = (env, subject) => {
|
|
|
15565
15580
|
return pending;
|
|
15566
15581
|
};
|
|
15567
15582
|
};
|
|
15568
|
-
var fetchedContext = (env,
|
|
15583
|
+
var fetchedContext = (env, subject, collector) => {
|
|
15569
15584
|
const read = readOnce(env, () => {
|
|
15570
|
-
return
|
|
15585
|
+
return subject("context.load");
|
|
15571
15586
|
});
|
|
15572
15587
|
return {
|
|
15573
15588
|
private: {
|
|
@@ -15575,6 +15590,7 @@ var fetchedContext = (env, anonId, collector) => {
|
|
|
15575
15590
|
return (await read()).private;
|
|
15576
15591
|
},
|
|
15577
15592
|
set: (patch) => {
|
|
15593
|
+
subject("context.set");
|
|
15578
15594
|
collector.set("private", patch);
|
|
15579
15595
|
}
|
|
15580
15596
|
},
|
|
@@ -15583,6 +15599,7 @@ var fetchedContext = (env, anonId, collector) => {
|
|
|
15583
15599
|
return (await read()).shared;
|
|
15584
15600
|
},
|
|
15585
15601
|
set: (patch) => {
|
|
15602
|
+
subject("context.set");
|
|
15586
15603
|
collector.set("shared", patch);
|
|
15587
15604
|
}
|
|
15588
15605
|
}
|
|
@@ -15654,20 +15671,15 @@ var readStamp = (payload) => {
|
|
|
15654
15671
|
}
|
|
15655
15672
|
return { q: stamp, payload: params?.payload };
|
|
15656
15673
|
};
|
|
15657
|
-
var requireSubject = (anonId, what) => {
|
|
15658
|
-
if (anonId === null) {
|
|
15659
|
-
throw new Error(`${what} needs a visitor, and this workflow was started without one.`);
|
|
15660
|
-
}
|
|
15661
|
-
return anonId;
|
|
15662
|
-
};
|
|
15663
15674
|
var buildWorkflowContext = (env, stamp) => {
|
|
15664
15675
|
const { variableKinds, appVariableKinds } = contextKindsOf(env);
|
|
15665
15676
|
const { variables, secrets } = projectInstallValues(parseVariableMaps(env), variableKinds, stamp.ord);
|
|
15666
15677
|
const { appVariables, appSecrets } = projectAppValues(env, appVariableKinds);
|
|
15667
15678
|
const install = { id: stamp.installId, label: stamp.installLabel };
|
|
15668
|
-
const
|
|
15669
|
-
|
|
15670
|
-
|
|
15679
|
+
const apps = buildAppsClient(env, {
|
|
15680
|
+
...stamp.anonId === null ? {} : { user: { anonId: stamp.anonId, globalId: null, properties: {} } },
|
|
15681
|
+
depth: 0
|
|
15682
|
+
});
|
|
15671
15683
|
return {
|
|
15672
15684
|
env,
|
|
15673
15685
|
install,
|
|
@@ -15690,15 +15702,7 @@ var buildWorkflowContext = (env, stamp) => {
|
|
|
15690
15702
|
broadcast: message.broadcast
|
|
15691
15703
|
});
|
|
15692
15704
|
},
|
|
15693
|
-
apps
|
|
15694
|
-
call: async (slug, fn, args, options) => {
|
|
15695
|
-
return client(requireSubject(stamp.anonId, "apps.call")).call(slug, fn, args, options);
|
|
15696
|
-
},
|
|
15697
|
-
// The broker's installs body carries no subject, so this keeps working without one.
|
|
15698
|
-
installs: async (slug) => {
|
|
15699
|
-
return client(stamp.anonId ?? "").installs(slug);
|
|
15700
|
-
}
|
|
15701
|
-
}
|
|
15705
|
+
apps
|
|
15702
15706
|
};
|
|
15703
15707
|
};
|
|
15704
15708
|
var handlerFor = (bundle, className) => {
|
|
@@ -15747,6 +15751,7 @@ var buildContext = ({
|
|
|
15747
15751
|
context
|
|
15748
15752
|
}) => {
|
|
15749
15753
|
const { variables, secrets } = projectInstallValues(maps, kinds, install.ord);
|
|
15754
|
+
const subject = envelope.user?.anonId ?? null;
|
|
15750
15755
|
return {
|
|
15751
15756
|
env,
|
|
15752
15757
|
site: envelope.site,
|
|
@@ -15755,18 +15760,19 @@ var buildContext = ({
|
|
|
15755
15760
|
ord: install.ord,
|
|
15756
15761
|
installId: install.id,
|
|
15757
15762
|
installLabel: install.label,
|
|
15758
|
-
anonId:
|
|
15763
|
+
anonId: subject
|
|
15759
15764
|
}),
|
|
15760
15765
|
variables,
|
|
15761
15766
|
secrets,
|
|
15762
15767
|
appVariables: appScope.appVariables,
|
|
15763
15768
|
appSecrets: appScope.appSecrets,
|
|
15764
|
-
user: envelope.user,
|
|
15769
|
+
user: envelope.user ?? null,
|
|
15765
15770
|
consent: envelope.consent,
|
|
15766
15771
|
geo: envelope.geo,
|
|
15767
15772
|
// Collect one entry past the per-install cap: the platform rejects an over-limit group whole,
|
|
15768
15773
|
// and the extra entry keeps it visibly over while bounding memory.
|
|
15769
15774
|
emit: (event) => {
|
|
15775
|
+
requireSubject(subject, "emit");
|
|
15770
15776
|
if (event.type !== "track") {
|
|
15771
15777
|
throw new Error("only track events may be emitted");
|
|
15772
15778
|
}
|
|
@@ -15779,6 +15785,7 @@ var buildContext = ({
|
|
|
15779
15785
|
}
|
|
15780
15786
|
},
|
|
15781
15787
|
push: (message) => {
|
|
15788
|
+
requireSubject(subject, "push");
|
|
15782
15789
|
if (pushes.length <= MAX_PUSHES_PER_INSTALL) {
|
|
15783
15790
|
pushes.push(message);
|
|
15784
15791
|
}
|
|
@@ -15786,6 +15793,7 @@ var buildContext = ({
|
|
|
15786
15793
|
attribution: {
|
|
15787
15794
|
values: envelope.attribution ?? {},
|
|
15788
15795
|
set: (key, value) => {
|
|
15796
|
+
requireSubject(subject, "attribution.set");
|
|
15789
15797
|
if (attribution.length <= MAX_ATTRIBUTION_WRITES_PER_INSTALL) {
|
|
15790
15798
|
attribution.push({ key, value });
|
|
15791
15799
|
}
|
|
@@ -15793,19 +15801,21 @@ var buildContext = ({
|
|
|
15793
15801
|
},
|
|
15794
15802
|
facts: {
|
|
15795
15803
|
list: (input) => {
|
|
15796
|
-
return brokerFactsRead(env,
|
|
15804
|
+
return brokerFactsRead(env, requireSubject(subject, "facts.list"), input);
|
|
15797
15805
|
}
|
|
15798
15806
|
},
|
|
15799
15807
|
context: {
|
|
15800
15808
|
private: {
|
|
15801
15809
|
entry: envelope.context?.private ?? null,
|
|
15802
15810
|
set: (patch) => {
|
|
15811
|
+
requireSubject(subject, "context.set");
|
|
15803
15812
|
context.set("private", patch);
|
|
15804
15813
|
}
|
|
15805
15814
|
},
|
|
15806
15815
|
shared: {
|
|
15807
15816
|
byApp: envelope.context?.shared ?? {},
|
|
15808
15817
|
set: (patch) => {
|
|
15818
|
+
requireSubject(subject, "context.set");
|
|
15809
15819
|
context.set("shared", patch);
|
|
15810
15820
|
}
|
|
15811
15821
|
}
|
|
@@ -15870,6 +15880,7 @@ var dispatchSchedule = async (envelope, bundle, env) => {
|
|
|
15870
15880
|
appVariables: appScope.appVariables,
|
|
15871
15881
|
appSecrets: appScope.appSecrets,
|
|
15872
15882
|
observability: buildObservability(),
|
|
15883
|
+
apps: buildAppsClient(env, { depth: 0 }),
|
|
15873
15884
|
push: async (userId, message) => {
|
|
15874
15885
|
await brokerPush(env, {
|
|
15875
15886
|
userId,
|
|
@@ -16073,14 +16084,15 @@ var buildContext2 = ({ env, maps, kinds, appScope, install }) => {
|
|
|
16073
16084
|
return {
|
|
16074
16085
|
env,
|
|
16075
16086
|
install: { id: install.id, label: install.label },
|
|
16076
|
-
// An endpoint has no visitor, so a workflow it starts cannot push
|
|
16077
|
-
//
|
|
16087
|
+
// An endpoint has no visitor, so a workflow it starts cannot push. `push` below is the
|
|
16088
|
+
// exception: it names its subject rather than inheriting one.
|
|
16078
16089
|
startWorkflow: buildStartWorkflow({ ord: install.ord, installId: install.id, installLabel: install.label, anonId: null }),
|
|
16079
16090
|
variables,
|
|
16080
16091
|
secrets,
|
|
16081
16092
|
appVariables: appScope.appVariables,
|
|
16082
16093
|
appSecrets: appScope.appSecrets,
|
|
16083
16094
|
observability: buildObservability(),
|
|
16095
|
+
apps: buildAppsClient(env, { depth: 0 }),
|
|
16084
16096
|
push: async (userId, message) => {
|
|
16085
16097
|
await brokerPush(env, {
|
|
16086
16098
|
userId,
|
|
@@ -16361,7 +16373,8 @@ var dispatchUi = async ({ request, env, versions }) => {
|
|
|
16361
16373
|
appVariables,
|
|
16362
16374
|
appSecrets,
|
|
16363
16375
|
viewer: context.viewer,
|
|
16364
|
-
observability: buildObservability()
|
|
16376
|
+
observability: buildObservability(),
|
|
16377
|
+
apps: buildAppsClient(env, { depth: 0 })
|
|
16365
16378
|
};
|
|
16366
16379
|
let response;
|
|
16367
16380
|
try {
|
|
@@ -16382,6 +16395,29 @@ var dispatchUi = async ({ request, env, versions }) => {
|
|
|
16382
16395
|
return response;
|
|
16383
16396
|
};
|
|
16384
16397
|
|
|
16398
|
+
// src/durable-object.ts
|
|
16399
|
+
var VERSION_KEY = "qintoSchemaVersion";
|
|
16400
|
+
var applyDurableObjectMigrations = (storage, migrations) => {
|
|
16401
|
+
const applied = storage.kv.get(VERSION_KEY) ?? 0;
|
|
16402
|
+
for (const [index, migration] of migrations.entries()) {
|
|
16403
|
+
if (index < applied) {
|
|
16404
|
+
continue;
|
|
16405
|
+
}
|
|
16406
|
+
for (const statement of migration.statements) {
|
|
16407
|
+
storage.sql.exec(statement);
|
|
16408
|
+
}
|
|
16409
|
+
storage.kv.put(VERSION_KEY, index + 1);
|
|
16410
|
+
}
|
|
16411
|
+
};
|
|
16412
|
+
var withAppDurableObject = (Base, migrations) => {
|
|
16413
|
+
return class extends Base {
|
|
16414
|
+
constructor(ctx, env) {
|
|
16415
|
+
applyDurableObjectMigrations(ctx.storage, migrations);
|
|
16416
|
+
super(ctx, env);
|
|
16417
|
+
}
|
|
16418
|
+
};
|
|
16419
|
+
};
|
|
16420
|
+
|
|
16385
16421
|
// src/index.ts
|
|
16386
16422
|
installTracedFetch();
|
|
16387
16423
|
var toHandler = (candidate) => {
|
|
@@ -16528,9 +16564,16 @@ var dispatchRpc = async (envelope, bundle, env) => {
|
|
|
16528
16564
|
const maps = parseVariableMaps(env);
|
|
16529
16565
|
const { variableKinds: kinds, appVariableKinds } = contextKindsOf(env);
|
|
16530
16566
|
const appScope = projectAppValues(env, appVariableKinds);
|
|
16567
|
+
const subject = envelope.user?.anonId ?? null;
|
|
16531
16568
|
const ctx = {
|
|
16532
16569
|
...buildContext({ env, envelope, install, maps, kinds, appScope, emits, pushes, attribution, context }),
|
|
16533
|
-
context: fetchedContext(
|
|
16570
|
+
context: fetchedContext(
|
|
16571
|
+
env,
|
|
16572
|
+
(what) => {
|
|
16573
|
+
return requireSubject(subject, what);
|
|
16574
|
+
},
|
|
16575
|
+
context
|
|
16576
|
+
)
|
|
16534
16577
|
};
|
|
16535
16578
|
let value;
|
|
16536
16579
|
try {
|
|
@@ -16639,5 +16682,6 @@ var createRuntime = (input) => {
|
|
|
16639
16682
|
};
|
|
16640
16683
|
export {
|
|
16641
16684
|
createRuntime,
|
|
16642
|
-
createWorkflow
|
|
16685
|
+
createWorkflow,
|
|
16686
|
+
withAppDurableObject
|
|
16643
16687
|
};
|
|
@@ -8,8 +8,10 @@ var withSecurityHeaders = /* @__PURE__ */ __name((response, portalOrigin) => {
|
|
|
8
8
|
response.headers.set("content-security-policy", `frame-ancestors ${portalOrigin}`);
|
|
9
9
|
response.headers.set("x-content-type-options", "nosniff");
|
|
10
10
|
response.headers.set("referrer-policy", "no-referrer");
|
|
11
|
+
response.headers.set("vary", "sec-fetch-dest");
|
|
11
12
|
return response;
|
|
12
13
|
}, "withSecurityHeaders");
|
|
14
|
+
var NAVIGATION_DESTINATIONS = /* @__PURE__ */ new Set(["document", "object", "embed"]);
|
|
13
15
|
var assetKeys = /* @__PURE__ */ __name((hostname, pathname) => {
|
|
14
16
|
const dot = hostname.indexOf(".");
|
|
15
17
|
const slug = dot === -1 ? hostname : hostname.slice(0, dot);
|
|
@@ -27,6 +29,10 @@ var serve = /* @__PURE__ */ __name(async (request, env) => {
|
|
|
27
29
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
28
30
|
return new Response("method not allowed", { status: 405 });
|
|
29
31
|
}
|
|
32
|
+
const destination = request.headers.get("sec-fetch-dest");
|
|
33
|
+
if (destination !== null && NAVIGATION_DESTINATIONS.has(destination)) {
|
|
34
|
+
return new Response("unauthorized", { status: 401 });
|
|
35
|
+
}
|
|
30
36
|
const url = new URL(request.url);
|
|
31
37
|
const keys = assetKeys(url.hostname, url.pathname);
|
|
32
38
|
if (keys === null) {
|