@somewhere-tech/cli 0.32.4 → 0.33.0
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/commands/cron.js
CHANGED
|
@@ -165,7 +165,8 @@ export function registerCron(program) {
|
|
|
165
165
|
cron
|
|
166
166
|
.command('create <schedule> <handler>')
|
|
167
167
|
.description('Create a scheduled trigger (5-field cron expression, read in UTC unless --timezone says otherwise)')
|
|
168
|
-
.
|
|
168
|
+
.addHelpText('after', '\nHandler is a deployed route such as /api/tick, not a source filename such as api/tick.js.\n')
|
|
169
|
+
.option('-p, --project <project>', 'Project slug or ID; defaults to the linked project')
|
|
169
170
|
.option('--timezone <iana>', 'IANA time zone the schedule is read in, e.g. America/Los_Angeles. Daylight saving is handled for you. Omit for UTC.')
|
|
170
171
|
.option('--name <name>', 'Display name')
|
|
171
172
|
.option('--payload <json>', 'JSON object sent to the handler')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/commands/cron.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EACL,aAAa,EACb,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAqCzF,MAAM,oBAAoB,GAAG,yDAAyD,CAAC;AAEvF,SAAS,YAAY,CAAC,KAAyB;IAC7C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAY,CAAC;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACxG,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACzE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,IAAY,EACZ,IAA6B,EAC7B,IAAyB,EACzB,KAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,IAAI,IAAI;YAAE,SAAS,CAAC,KAAK,CAAC,CAAC;;YACtB,KAAK,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAC9B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;YAC3C,CAAC,CAAC,IAAI,CAAC,KAAK;YACZ,CAAC,CAAC,IAAI,CAAC;IACX,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAClF,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,IAAI,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,EAAE,CAAC;IACnE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;WACZ,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;WAChC,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;WAC/B,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;WAC/B,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,IAAqB,CAAC;AAC/B,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAY;IACtC,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,MAAM,KAAK,GAAG,8BAA8B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAY;IACtC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAClD,IAAI,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,IAAI,KAAK,WAAW,IAAI,CAAC,gCAAgC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY,EAAE,OAAe,EAAE,IAAyB;IACnF,IAAI,IAAI;QAAE,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;;QACnC,KAAK,CAAC,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;IAClC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,OAA2B;IACzE,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC;IAC9C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,gBAAgB,CAAC,WAAW,EAAE,aAAa,CAAC;QACtE,CAAC,YAAY,EAAE,OAAO,CAAC;KACxB,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,4CAA4C,MAAM,cAAc,OAAO,CAAC,CAAC,CAAC,gBAAgB,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAChI,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,+DAA+D,MAAM,qCAAqC,CAAC,CAAC;IAC9H,CAAC;IACD,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IACnF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAY,EAAE,KAAc;IACrD,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ;QACtD,CAAC,CAAC,IAAI,CAAC,EAAE;QACT,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;YAClD,CAAC,CAAC,IAAI,CAAC,OAAO;YACd,CAAC,CAAC,IAAI,CAAC;IACX,OAAO,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,MAAM,IAAI,GAAG,OAAO;SACjB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,2BAA2B,CAAC;SACxC,WAAW,CACV,OAAO,EACP,uFAAuF;UACnF,yGAAyG;UACzG,qEAAqE,CAC1E,CAAC;IAEJ,IAAI;SACD,OAAO,CAAC,MAAM,CAAC;SACf,KAAK,CAAC,IAAI,CAAC;SACX,WAAW,CAAC,yBAAyB,CAAC;SACtC,MAAM,CAAC,yBAAyB,EAAE,kDAAkD,CAAC;SACrF,MAAM,CAAC,QAAQ,EAAE,qCAAqC,CAAC;SACvD,MAAM,CAAC,KAAK,EAAE,IAAoB,EAAE,EAAE;QACrC,MAAM,WAAW,CAAC,WAAW,EAAE,aAAa,CAAC;YAC3C,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC;SAC7B,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YACvB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC,CAAC;gBAC3C,OAAO;YACT,CAAC;YACD,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;gBAC9E,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG;gBACrB,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC1B,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG;gBACrD,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;gBAC7B,GAAG,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK;aACrC,CAAC,CAAC,CAAC;QACN,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,uBAAuB,CAAC;SAChC,WAAW,CAAC,6DAA6D,CAAC;SAC1E,MAAM,CAAC,yBAAyB,EAAE,iDAAiD,CAAC;SACpF,MAAM,CAAC,QAAQ,EAAE,qCAAqC,CAAC;SACvD,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,IAAoB,EAAE,EAAE;QACrD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC5D,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1F,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,SAAS,CAAC,KAAK,CAAC,CAAC;gBACjB,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;YACpC,OAAO,CAAC,uCAAuC,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,cAAc,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5B,mBAAmB,CAAC,wBAAwB,EAAE,oBAAoB,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC/E,OAAO;YACT,CAAC;YACD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;YAClD,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,IAAI;SACD,OAAO,CAAC,6BAA6B,CAAC;SACtC,WAAW,CAAC,oGAAoG,CAAC;SACjH,
|
|
1
|
+
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/commands/cron.ts"],"names":[],"mappings":"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"}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@somewhere-tech/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@somewhere-tech/cli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.33.0",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"@modelcontextprotocol/sdk",
|
|
12
12
|
"chokidar",
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"esbuild": "0.24.0",
|
|
3
3
|
"source_files": {
|
|
4
|
-
"worker/containers/compile/declared-data-contract.cjs": "
|
|
5
|
-
"worker/containers/compile/runtime-types.cjs": "
|
|
6
|
-
"worker/containers/compile/schema-types.cjs": "
|
|
4
|
+
"worker/containers/compile/declared-data-contract.cjs": "7a2c20ddddd912ae513f3fff2614829b18c3e19954f9c48314ca96f1ce3143a1",
|
|
5
|
+
"worker/containers/compile/runtime-types.cjs": "a458232f605ac60202b58c6cae725a813d22479498f436668b8ad553678576ef",
|
|
6
|
+
"worker/containers/compile/schema-types.cjs": "c16fa8cabc8f5cff50777623f94f8cb009fee3ac47d53b471894be9c698d614a",
|
|
7
7
|
"worker/containers/compile/typed-data.cjs": "57b458e16a373e42d48a0e527317fcdf609299ae368eb539a8b582b9a47abb15",
|
|
8
|
-
"worker/src/utils/authority/scope-enforcement.ts": "
|
|
9
|
-
"worker/src/utils/db-schema-deploy/client-contract-source.ts": "
|
|
10
|
-
"worker/src/utils/db-schema-deploy/client-permissions.ts": "
|
|
11
|
-
"worker/src/utils/db-schema-deploy/extract-schema-ts.ts": "
|
|
8
|
+
"worker/src/utils/authority/scope-enforcement.ts": "98c242359240b8918c1b71af73f17b6b4200559a896f47f6032ef771f543201f",
|
|
9
|
+
"worker/src/utils/db-schema-deploy/client-contract-source.ts": "fc37a8d18affc0d814f40357884e1764eb4bcc9af446626933729b7021fa3c6d",
|
|
10
|
+
"worker/src/utils/db-schema-deploy/client-permissions.ts": "05b1d68afae151569abfa16b452834a96b3c0a2bf4b04f63d322f05548827222",
|
|
11
|
+
"worker/src/utils/db-schema-deploy/extract-schema-ts.ts": "b092afe7c271405126da9e204084fc5528a42025b5583eba25da3907f995a25b",
|
|
12
12
|
"worker/src/utils/db-schema-deploy/schema-ownership.ts": "e6c811a87bc657707be3518dfe547c6d97718ac073108b126eb7f791c9c08202"
|
|
13
13
|
},
|
|
14
|
-
"sha256": "
|
|
14
|
+
"sha256": "80171af94d110f5caf717eb7ad6abd95b15ed4125af1cc848151561d67462b8d"
|
|
15
15
|
}
|
|
@@ -48,12 +48,41 @@ var require_declared_data_contract = __commonJS({
|
|
|
48
48
|
if (result.some((field) => typeof field !== "string" || !columns.has(field)) || new Set(result).size !== result.length) fail(name);
|
|
49
49
|
return result.sort();
|
|
50
50
|
}
|
|
51
|
+
function policy(value, label) {
|
|
52
|
+
record(value, label);
|
|
53
|
+
const keys = Object.keys(value).sort().join(",");
|
|
54
|
+
if (value.k === "o" && keys === "c,k" && identifier.test(value.c)) return { k: "o", c: value.c };
|
|
55
|
+
if (value.k === "m" && keys === "k,m") {
|
|
56
|
+
record(value.m, label + ".member");
|
|
57
|
+
const { g, m, u, mg } = value.m;
|
|
58
|
+
if (Object.keys(value.m).sort().join(",") !== "g,m,mg,u" || !identifier.test(m) || !identifier.test(u) || !Array.isArray(g) || !g.length || !Array.isArray(mg) || g.length !== mg.length || [...g, ...mg].some((field) => typeof field !== "string" || !identifier.test(field))) fail(label);
|
|
59
|
+
return { k: "m", m: { g: [...g], m, u, mg: [...mg] } };
|
|
60
|
+
}
|
|
61
|
+
if (value.k === "a" && keys === "k,p" && Array.isArray(value.p) && value.p.length === 2) {
|
|
62
|
+
const left = policy(value.p[0], label), right = policy(value.p[1], label);
|
|
63
|
+
if (left.k !== "o" || right.k !== "m") fail(label);
|
|
64
|
+
return { k: "a", p: [left, right] };
|
|
65
|
+
}
|
|
66
|
+
if (value.k === "p" && keys === "k,p,pk,t,v" && [value.v, value.t, value.pk].every((item) => typeof item === "string" && identifier.test(item))) {
|
|
67
|
+
const inherited = policy(value.p, label);
|
|
68
|
+
if (!["o", "m", "a"].includes(inherited.k)) fail(label);
|
|
69
|
+
return { k: "p", v: value.v, t: value.t, pk: value.pk, p: inherited };
|
|
70
|
+
}
|
|
71
|
+
fail(label);
|
|
72
|
+
}
|
|
73
|
+
function policyMembershipTables(value, out) {
|
|
74
|
+
if (!value || typeof value !== "object") return;
|
|
75
|
+
if (value.k === "m" && value.m) out.add(value.m.m);
|
|
76
|
+
else if (value.k === "a") value.p.forEach((item) => policyMembershipTables(item, out));
|
|
77
|
+
else if (value.k === "p") policyMembershipTables(value.p, out);
|
|
78
|
+
}
|
|
51
79
|
function canonicalize2(schema, intents, scopes) {
|
|
52
80
|
record(schema, "schema");
|
|
53
81
|
record(intents, "intents");
|
|
54
82
|
record(scopes, "scopes");
|
|
55
83
|
const tables = [];
|
|
56
84
|
const membershipTables = new Set(Object.values(schema).filter((table) => table && table.member).map((table) => table.member.m));
|
|
85
|
+
for (const table of Object.values(schema)) if (table?.policy) policyMembershipTables(table.policy, membershipTables);
|
|
57
86
|
for (const name of Object.keys(schema).sort()) {
|
|
58
87
|
const table = schema[name];
|
|
59
88
|
record(table, `table ${name}`);
|
|
@@ -78,14 +107,30 @@ var require_declared_data_contract = __commonJS({
|
|
|
78
107
|
if (Object.keys(input).some((key) => !["read", "publicRead", "create", "update", "delete", "identity"].includes(key))) fail(`${name} permissions`);
|
|
79
108
|
const read = typeof input.read === "boolean" ? input.read : fields(input.read, seen, `${name}.read`);
|
|
80
109
|
if (Array.isArray(read) && !read.includes(pk)) fail(`${name}.read must include the primary key`);
|
|
81
|
-
|
|
110
|
+
let publicRead = input.publicRead;
|
|
111
|
+
if (typeof publicRead !== "boolean") {
|
|
112
|
+
record(publicRead, `${name} public read`);
|
|
113
|
+
if (Object.keys(publicRead).some((key) => key !== "where")) fail(`${name} public read`);
|
|
114
|
+
record(publicRead.where, `${name} public read predicate`);
|
|
115
|
+
const entries = Object.entries(publicRead.where);
|
|
116
|
+
if (!entries.length || entries.length > 8) fail(`${name} public read predicate`);
|
|
117
|
+
const where = {};
|
|
118
|
+
for (const [field, value] of entries.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
119
|
+
const column = columns.find((column2) => column2.n === field);
|
|
120
|
+
if (!column || ["json", "blob"].includes(column.t) || !(value === null || typeof value === "string" && value.length <= 256 || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value))) fail(`${name} public read predicate`);
|
|
121
|
+
if (value === null ? column.nul !== 1 : column.t === "boolean" ? typeof value !== "boolean" : ["integer", "number"].includes(column.t) ? typeof value !== "number" : ["text", "timestamp"].includes(column.t) ? typeof value !== "string" : true) fail(`${name} public read predicate type`);
|
|
122
|
+
where[field] = value;
|
|
123
|
+
}
|
|
124
|
+
publicRead = { where };
|
|
125
|
+
}
|
|
126
|
+
if (typeof input.delete !== "boolean" || !["authenticated", "visitor"].includes(input.identity)) fail(`${name} permissions`);
|
|
82
127
|
const create = input.create === null ? null : fields(input.create, seen, `${name}.create`, true);
|
|
83
128
|
const update = input.update === null ? null : fields(input.update, seen, `${name}.update`);
|
|
84
129
|
if (membershipTables.has(name) && (create !== null || update !== null || input.delete)) fail(`${name} membership authority writes`);
|
|
85
130
|
if ([...create || [], ...update || []].some((field) => columns.find((column) => column.n === field).t === "blob")) fail(`${name} blob browser writes require a server function`);
|
|
86
131
|
const intent = intents[name];
|
|
87
|
-
if (!["scoped", "shared", "member"].includes(intent)) fail(`${name} intent`);
|
|
88
|
-
|
|
132
|
+
if (!["scoped", "shared", "member", "policy"].includes(intent)) fail(`${name} intent`);
|
|
133
|
+
let owner = intent === "scoped" ? scopes[name] || null : null;
|
|
89
134
|
if (intent === "scoped" && (typeof owner !== "string" || !identifier.test(owner))) fail(`${name} owner`);
|
|
90
135
|
if (table.visitors !== void 0 && table.visitors !== true) fail(`${name} visitors`);
|
|
91
136
|
const visitors = table.visitors === true;
|
|
@@ -95,7 +140,8 @@ var require_declared_data_contract = __commonJS({
|
|
|
95
140
|
if (intent === "shared" && (author !== "_sw_author_id" || !columns.some((column) => column.n === author && column.t === "text" && column.nul === 1))) fail(`${name} author`);
|
|
96
141
|
if (Array.isArray(read) && read.includes(author)) fail(`${name} internal author projection`);
|
|
97
142
|
if (input.identity === "visitor" && intent !== "scoped") fail(`${name} visitor identity`);
|
|
98
|
-
if (
|
|
143
|
+
if (publicRead && !Array.isArray(read)) fail(`${name} public read`);
|
|
144
|
+
if (typeof publicRead === "object" && [...create || [], ...update || []].some((field) => Object.prototype.hasOwnProperty.call(publicRead.where, field))) fail(`${name} publication writes require a server function`);
|
|
99
145
|
if ([...create || [], ...update || []].some((field) => field === pk || field === owner || field === author)) fail(`${name} identity writes`);
|
|
100
146
|
let member = null;
|
|
101
147
|
if (intent === "member") {
|
|
@@ -107,7 +153,49 @@ var require_declared_data_contract = __commonJS({
|
|
|
107
153
|
if ((update || []).some((field) => g.includes(field))) fail(`${name} membership writes`);
|
|
108
154
|
member = { g: [...g], m, u, mg: [...mg] };
|
|
109
155
|
}
|
|
110
|
-
|
|
156
|
+
const normalizedPolicy = intent === "policy" ? policy(table.policy, `${name}.policy`) : null;
|
|
157
|
+
if (normalizedPolicy && !["a", "p"].includes(normalizedPolicy.k)) fail(`${name}.policy root`);
|
|
158
|
+
if (normalizedPolicy?.k === "a") {
|
|
159
|
+
owner = normalizedPolicy.p[0].c;
|
|
160
|
+
if ([...create || [], ...update || []].includes(owner) || (update || []).some((field) => normalizedPolicy.p[1].m.g.includes(field))) fail(`${name}.policy identity writes`);
|
|
161
|
+
}
|
|
162
|
+
if (normalizedPolicy?.k === "p" && create !== null && !create.includes(normalizedPolicy.v)) fail(`${name}.policy parent create`);
|
|
163
|
+
tables.push({
|
|
164
|
+
name,
|
|
165
|
+
columns,
|
|
166
|
+
client: { identity: input.identity, read, publicRead, create, update, delete: input.delete },
|
|
167
|
+
primaryKey: pk,
|
|
168
|
+
intent,
|
|
169
|
+
owner,
|
|
170
|
+
author,
|
|
171
|
+
visitors,
|
|
172
|
+
member,
|
|
173
|
+
...normalizedPolicy ? { policy: normalizedPolicy } : {}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
const tableByName = new Map(tables.map((table) => [table.name, table]));
|
|
177
|
+
for (const table of tables) {
|
|
178
|
+
const declared = schema[table.name].relations;
|
|
179
|
+
if (declared === void 0) continue;
|
|
180
|
+
if (!Array.isArray(declared)) fail(`${table.name}.relations`);
|
|
181
|
+
const relations = [];
|
|
182
|
+
const names = /* @__PURE__ */ new Set();
|
|
183
|
+
for (const relation of declared) {
|
|
184
|
+
record(relation, `${table.name}.relation`);
|
|
185
|
+
if (Object.keys(relation).some((key) => !["name", "table", "fk", "parentKey"].includes(key))) fail(`${table.name}.relation`);
|
|
186
|
+
const { name, table: childName, fk, parentKey } = relation;
|
|
187
|
+
if (![name, childName, fk, parentKey].every((value) => typeof value === "string" && identifier.test(value)) || names.has(name)) {
|
|
188
|
+
fail(`${table.name}.relation`);
|
|
189
|
+
}
|
|
190
|
+
names.add(name);
|
|
191
|
+
const declaredChild = schema[childName];
|
|
192
|
+
if (!declaredChild || !Array.isArray(declaredChild.columns) || parentKey !== table.primaryKey || !declaredChild.columns.some((column) => column && column.n === fk)) fail(`${table.name}.relation`);
|
|
193
|
+
const child = tableByName.get(childName);
|
|
194
|
+
if (table.client.read !== false && child && child.client.read !== false) {
|
|
195
|
+
relations.push({ name, table: childName, fk, parentKey });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (relations.length) table.relations = relations.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
111
199
|
}
|
|
112
200
|
return JSON.stringify({ version: 1, tables });
|
|
113
201
|
}
|
|
@@ -115,10 +203,10 @@ var require_declared_data_contract = __commonJS({
|
|
|
115
203
|
const type = { integer: "number | string", number: "number", text: "string", timestamp: "string", boolean: "boolean", json: "Json", blob: "number[]" }[column.t];
|
|
116
204
|
return column.nul ? `${type} | null` : type;
|
|
117
205
|
}
|
|
118
|
-
function shape(columns, names, mode) {
|
|
206
|
+
function shape(columns, names, mode, required = /* @__PURE__ */ new Set()) {
|
|
119
207
|
const values = names.map((name) => {
|
|
120
208
|
const column = columns.find((candidate) => candidate.n === name);
|
|
121
|
-
const optional = mode === "update" || mode === "create" && (column.d || column.nul);
|
|
209
|
+
const optional = mode === "update" || mode === "create" && !required.has(name) && (column.d || column.nul);
|
|
122
210
|
return `${JSON.stringify(name)}${optional ? "?" : ""}: ${columnType(column)}`;
|
|
123
211
|
});
|
|
124
212
|
return values.length ? `{ ${values.join("; ")} }` : "Record<string, never>";
|
|
@@ -143,7 +231,8 @@ var require_declared_data_contract = __commonJS({
|
|
|
143
231
|
}
|
|
144
232
|
const mutation = client.read === false ? "{ count: number; changes: number }" : `{ data: ${row} | null; count: number; changes: number }`;
|
|
145
233
|
if (client.create !== null) {
|
|
146
|
-
|
|
234
|
+
const required = table.policy?.k === "p" ? /* @__PURE__ */ new Set([table.policy.v]) : /* @__PURE__ */ new Set();
|
|
235
|
+
operations.push(`create(values: ${shape(columns, client.create, "create", required)}): Promise<${mutation}>`);
|
|
147
236
|
methods.push("create");
|
|
148
237
|
}
|
|
149
238
|
if (client.update !== null) {
|
|
@@ -154,8 +243,21 @@ var require_declared_data_contract = __commonJS({
|
|
|
154
243
|
operations.push(`delete(id: ${id}): Promise<${mutation}>`);
|
|
155
244
|
methods.push("delete");
|
|
156
245
|
}
|
|
246
|
+
const relationDeclarations = [];
|
|
247
|
+
const relationNames = [];
|
|
248
|
+
for (const relation of table.relations || []) {
|
|
249
|
+
const child = tables.find((candidate) => candidate.name === relation.table);
|
|
250
|
+
const childReadable = child.client.read === true ? child.columns.filter((column) => column.n !== child.author).map((column) => column.n) : child.client.read;
|
|
251
|
+
const childRow = shape(child.columns, childReadable, "read");
|
|
252
|
+
const childId = columnType(child.columns.find((column) => column.n === child.primaryKey));
|
|
253
|
+
const parentId = columnType(columns.find((column) => column.n === primaryKey));
|
|
254
|
+
const equality = childReadable.filter((field) => !["json", "blob"].includes(child.columns.find((column) => column.n === field).t));
|
|
255
|
+
relationDeclarations.push(`${JSON.stringify(relation.name)}: { list(parentId: ${parentId}, options?: { limit?: number; after?: ${childId}; where?: ${shape(child.columns, equality, "update")} }): Promise<{ data: ${childRow}[]; next: ${childId} | null; has_more: boolean }> }`);
|
|
256
|
+
relationNames.push(relation.name);
|
|
257
|
+
}
|
|
258
|
+
if (relationDeclarations.length) operations.push(`relations: { ${relationDeclarations.join("; ")} }`);
|
|
157
259
|
declarations.push(`${JSON.stringify(name)}: { ${operations.join("; ")} }`);
|
|
158
|
-
runtimeTables.push([name, methods]);
|
|
260
|
+
runtimeTables.push([name, methods, relationNames]);
|
|
159
261
|
}
|
|
160
262
|
const declaration = `declare module ${JSON.stringify("somewhere:data")} {
|
|
161
263
|
export type Json = string | number | boolean | null | Json[] | { [key: string]: Json };
|
|
@@ -166,7 +268,7 @@ var require_declared_data_contract = __commonJS({
|
|
|
166
268
|
const runtime = `const contract=${JSON.stringify(contract_digest)};
|
|
167
269
|
class DataError extends Error{constructor(status,payload){super(payload&&typeof payload.message==="string"?payload.message:payload&&typeof payload.error==="string"?payload.error:"Data operation failed");this.name="DataError";this.status=status;this.code=payload&&typeof payload.error==="string"?payload.error:null}}
|
|
168
270
|
const invoke=async(table,operation,input)=>{const response=await fetch("/__sw/data",{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json"},body:JSON.stringify({...input,contract,table,operation})});const payload=await response.json().catch(()=>{throw new DataError(response.status,{error:"INVALID_DATA_RESPONSE",message:"Data operation returned an invalid response"})});if(!response.ok)throw new DataError(response.status,payload);return payload};
|
|
169
|
-
const data=Object.freeze(Object.fromEntries(${JSON.stringify(runtimeTables)}.map(([table,operations])=>
|
|
271
|
+
const data=Object.freeze(Object.fromEntries(${JSON.stringify(runtimeTables)}.map(([table,operations,relations])=>{const entries=operations.map(operation=>[operation,operation==="list"?(options={})=>invoke(table,operation,options):operation==="create"?values=>invoke(table,operation,{values}):operation==="update"?(id,values)=>invoke(table,operation,{id,values}):id=>invoke(table,operation,{id})]);if(relations.length)entries.push(["relations",Object.freeze(Object.fromEntries(relations.map(relation=>[relation,Object.freeze({list:(parent_id,options={})=>invoke(table,"relation_list",{...options,relation,parent_id})})])))]);return[table,Object.freeze(Object.fromEntries(entries))];})));
|
|
170
272
|
export{data,DataError};
|
|
171
273
|
`;
|
|
172
274
|
return { contract_digest, declaration, runtime, manifest: { version: 1, contract_digest, tables, declaration } };
|
|
@@ -216,12 +318,13 @@ var require_schema_types = __commonJS({
|
|
|
216
318
|
interface Relation { readonly __somewhereRelation: unique symbol }
|
|
217
319
|
interface Scope { readonly __somewhereScope: unique symbol }
|
|
218
320
|
interface OwnerScope extends Scope { readonly __somewhereOwner: unique symbol }
|
|
321
|
+
interface MemberScope extends Scope { readonly __somewhereMember: unique symbol }
|
|
219
322
|
interface ColumnOptions<Default> {
|
|
220
323
|
nullable?: boolean; default?: Default | null; unique?: boolean;
|
|
221
324
|
references?: string; onDelete?: 'cascade' | 'restrict'; renamedFrom?: string;
|
|
222
325
|
}
|
|
223
326
|
interface ClientPermissions<Field extends string> {
|
|
224
|
-
identity?: 'authenticated' | 'visitor'; read?: boolean | Field[]; publicRead?: boolean;
|
|
327
|
+
identity?: 'authenticated' | 'visitor'; read?: boolean | Field[]; publicRead?: boolean | { where: Partial<Record<Field, string | number | boolean | null>> };
|
|
225
328
|
create?: Field[] | false | null; update?: Field[] | false | null; delete?: boolean;
|
|
226
329
|
}
|
|
227
330
|
interface TableOptions<Field extends string> {
|
|
@@ -243,14 +346,18 @@ function blob(options?: SomewhereSchemaDeclaration.ColumnOptions<never>): Somewh
|
|
|
243
346
|
function owner(options?: { column?: string; visitors?: boolean }): SomewhereSchemaDeclaration.OwnerScope;
|
|
244
347
|
function shared(): SomewhereSchemaDeclaration.Scope;
|
|
245
348
|
function serverOnly(): SomewhereSchemaDeclaration.Scope;
|
|
246
|
-
function member(options: { group: string | string[]; membership: string; member_user: string; member_group: string | string[] }): SomewhereSchemaDeclaration.
|
|
349
|
+
function member(options: { group: string | string[]; membership: string; member_user: string; member_group: string | string[] }): SomewhereSchemaDeclaration.MemberScope;
|
|
247
350
|
function hasMany(table: string, foreignKey: string): SomewhereSchemaDeclaration.Relation;
|
|
248
351
|
function removed(): SomewhereSchemaDeclaration.ColumnMarker;
|
|
249
352
|
function removedTable(): SomewhereSchemaDeclaration.TableMarker;
|
|
250
353
|
function exported(): SomewhereSchemaDeclaration.TableMarker;
|
|
354
|
+
`;
|
|
355
|
+
var policySignatures = `function anyOf(owner: SomewhereSchemaDeclaration.OwnerScope, member: SomewhereSchemaDeclaration.MemberScope): SomewhereSchemaDeclaration.Scope;
|
|
356
|
+
function anyOf(member: SomewhereSchemaDeclaration.MemberScope, owner: SomewhereSchemaDeclaration.OwnerScope): SomewhereSchemaDeclaration.Scope;
|
|
357
|
+
function parent(options: { via: string }): SomewhereSchemaDeclaration.Scope;
|
|
251
358
|
`;
|
|
252
359
|
var SCHEMA_DECLARATION2 = types + signatures.replace(/^function /gm, "declare function ") + `declare module 'somewhere/db' {
|
|
253
|
-
${signatures.replace(/^function /gm, " export function ")}}
|
|
360
|
+
${(signatures + policySignatures).replace(/^function /gm, " export function ")}}
|
|
254
361
|
`;
|
|
255
362
|
module2.exports = { SCHEMA_DECLARATION: SCHEMA_DECLARATION2 };
|
|
256
363
|
}
|
|
@@ -276,7 +383,10 @@ type __SomewhereDbOperator = {
|
|
|
276
383
|
& Partial<Record<Exclude<keyof __SomewhereDbOperators, K>, never>>
|
|
277
384
|
}[keyof __SomewhereDbOperators];
|
|
278
385
|
type SomewhereDbCondition = SomewhereDbScalar | __SomewhereDbOperator;
|
|
279
|
-
type
|
|
386
|
+
type SomewhereDbOrBranch = Readonly<Record<string, SomewhereDbCondition>>;
|
|
387
|
+
type SomewhereDbWhere = Readonly<{
|
|
388
|
+
$or?: readonly SomewhereDbOrBranch[];
|
|
389
|
+
} & Record<string, SomewhereDbCondition | readonly SomewhereDbCondition[] | readonly SomewhereDbOrBranch[] | undefined>>;
|
|
280
390
|
type SomewhereDbOrder = string | readonly [string, ('asc' | 'desc')?]
|
|
281
391
|
| readonly (readonly [string, ('asc' | 'desc')?])[];
|
|
282
392
|
interface SomewhereDbReadOptions {
|
|
@@ -291,14 +401,17 @@ interface SomewhereDbReadOptions {
|
|
|
291
401
|
interface SomewhereDbCountOptions { where?: SomewhereDbWhere | null }
|
|
292
402
|
interface SomewhereDbInsertOptions { onConflict?: 'ignore' | 'update' | null }
|
|
293
403
|
type SomewhereDbValues = Readonly<Record<string, __SomewhereJson>>;
|
|
294
|
-
|
|
295
|
-
|
|
404
|
+
type SomewhereDbIncrement = Readonly<Record<string, number>>;
|
|
405
|
+
type SomewhereDbUpdate = {
|
|
296
406
|
where?: SomewhereDbWhere | null;
|
|
297
|
-
}
|
|
407
|
+
} & (
|
|
408
|
+
| { set: SomewhereDbValues; increment?: SomewhereDbIncrement | null }
|
|
409
|
+
| { set?: never; increment: SomewhereDbIncrement }
|
|
410
|
+
);
|
|
298
411
|
interface SomewhereDbRemove { where?: SomewhereDbWhere | null }
|
|
299
412
|
type SomewhereDbWriteIntent =
|
|
300
413
|
| { op: 'insert'; table: string; values: SomewhereDbValues; options?: SomewhereDbInsertOptions | null }
|
|
301
|
-
| { op: 'update'; table: string
|
|
414
|
+
| ({ op: 'update'; table: string } & SomewhereDbUpdate)
|
|
302
415
|
| { op: 'remove'; table: string; where?: SomewhereDbWhere | null };
|
|
303
416
|
interface SomewhereDbResult {
|
|
304
417
|
// Composed results preserve the database representation. They do not use
|
|
@@ -364,7 +477,7 @@ function parseClientPermissions(value, columns, scope) {
|
|
|
364
477
|
return fail(`Unknown client permission "${key}".`);
|
|
365
478
|
}
|
|
366
479
|
}
|
|
367
|
-
if (!["owner", "member", "shared"].includes(scope.kind)) {
|
|
480
|
+
if (!["owner", "member", "shared", "policy"].includes(scope.kind)) {
|
|
368
481
|
return fail("serverOnly() tables cannot declare client access. Use a server function for business logic.");
|
|
369
482
|
}
|
|
370
483
|
const identity = scope.kind === "owner" && scope.visitors === true ? "visitor" : "authenticated";
|
|
@@ -375,8 +488,33 @@ function parseClientPermissions(value, columns, scope) {
|
|
|
375
488
|
return fail("client.identity must match the owner() declaration. Visitor access requires owner({ visitors: true }); owner() and other scopes require authenticated identity.");
|
|
376
489
|
}
|
|
377
490
|
let read = input.read ?? false;
|
|
378
|
-
const
|
|
379
|
-
|
|
491
|
+
const rawPublicRead = input.publicRead ?? false;
|
|
492
|
+
let publicRead;
|
|
493
|
+
if (typeof rawPublicRead === "boolean") publicRead = rawPublicRead;
|
|
494
|
+
else {
|
|
495
|
+
if (!rawPublicRead || typeof rawPublicRead !== "object" || Array.isArray(rawPublicRead) || Object.keys(rawPublicRead).some((key) => key !== "where")) {
|
|
496
|
+
return fail("client.publicRead must be true, false, or { where: { field: literal } }.");
|
|
497
|
+
}
|
|
498
|
+
const where = rawPublicRead.where;
|
|
499
|
+
if (!where || typeof where !== "object" || Array.isArray(where)) return fail("client.publicRead.where must be a non-empty object.");
|
|
500
|
+
const entries = Object.entries(where);
|
|
501
|
+
if (entries.length === 0 || entries.length > 8) return fail("client.publicRead.where must declare 1 to 8 equality fields.");
|
|
502
|
+
const normalized = {};
|
|
503
|
+
for (const [rawName, literal] of entries) {
|
|
504
|
+
const name = rawName.toLowerCase();
|
|
505
|
+
const column = columns.find((candidate) => candidate.name === name);
|
|
506
|
+
if (Object.prototype.hasOwnProperty.call(normalized, name)) return fail(`client.publicRead.where declares field "${rawName}" twice.`);
|
|
507
|
+
if (!column || ["json", "blob"].includes(column.helper)) return fail(`client.publicRead.where names undeclared or non-scalar field "${rawName}".`);
|
|
508
|
+
if (!(literal === null || typeof literal === "boolean" || typeof literal === "string" || typeof literal === "number" && Number.isFinite(literal))) return fail(`client.publicRead.where field "${rawName}" must equal a literal scalar.`);
|
|
509
|
+
if (typeof literal === "string" && literal.length > 256) return fail(`client.publicRead.where field "${rawName}" exceeds 256 characters.`);
|
|
510
|
+
if (literal === null && !column.nullable) return fail(`client.publicRead.where field "${rawName}" cannot equal null because it is required.`);
|
|
511
|
+
if (literal !== null && (column.helper === "boolean" && typeof literal !== "boolean" || ["integer", "number"].includes(column.helper) && typeof literal !== "number" || ["text", "timestamp"].includes(column.helper) && typeof literal !== "string")) {
|
|
512
|
+
return fail(`client.publicRead.where field "${rawName}" must match its declared type.`);
|
|
513
|
+
}
|
|
514
|
+
normalized[name] = literal;
|
|
515
|
+
}
|
|
516
|
+
publicRead = { where: Object.fromEntries(Object.entries(normalized).sort(([a], [b]) => a.localeCompare(b))) };
|
|
517
|
+
}
|
|
380
518
|
if (read !== true && read !== false && !Array.isArray(read)) return fail("client.read must list readable fields, or be true or false.");
|
|
381
519
|
if (publicRead && (!Array.isArray(read) || read.length === 0)) {
|
|
382
520
|
return fail("client.publicRead requires an explicit non-empty client.read field list.");
|
|
@@ -406,11 +544,14 @@ function parseClientPermissions(value, columns, scope) {
|
|
|
406
544
|
const name = entry.toLowerCase();
|
|
407
545
|
const column = fields.get(name);
|
|
408
546
|
if (!column) return fail(`client.${operation} names undeclared or platform-managed field "${entry}".`);
|
|
409
|
-
if (column.helper === "id" || scope.kind === "owner" && name === scope.column) {
|
|
547
|
+
if (column.helper === "id" || (scope.kind === "owner" || scope.kind === "policy") && name === scope.column) {
|
|
410
548
|
return fail(`client.${operation} cannot write the platform-assigned identity field "${entry}".`);
|
|
411
549
|
}
|
|
412
550
|
if (column.helper === "blob") return fail(`client.${operation} cannot write binary field "${entry}". Use project files for uploads.`);
|
|
413
|
-
if (
|
|
551
|
+
if (typeof publicRead === "object" && Object.prototype.hasOwnProperty.call(publicRead.where, name)) {
|
|
552
|
+
return fail(`client.${operation} cannot write publication field "${entry}". Use a server operation to publish rows.`);
|
|
553
|
+
}
|
|
554
|
+
if (operation === "update" && (scope.kind === "member" || scope.kind === "policy") && scope.group?.includes(name)) {
|
|
414
555
|
return fail(`client.update cannot move a row between membership groups by changing "${entry}".`);
|
|
415
556
|
}
|
|
416
557
|
if (allowed.includes(name)) return fail(`client.${operation} declares field "${entry}" twice.`);
|
|
@@ -420,6 +561,18 @@ function parseClientPermissions(value, columns, scope) {
|
|
|
420
561
|
writes[operation] = allowed.sort();
|
|
421
562
|
}
|
|
422
563
|
if (writes.create !== null) {
|
|
564
|
+
if (typeof publicRead === "object") {
|
|
565
|
+
let guaranteedPrivate = false;
|
|
566
|
+
for (const [name, publishedValue] of Object.entries(publicRead.where)) {
|
|
567
|
+
const column = fields.get(name);
|
|
568
|
+
if (column.autoNow) continue;
|
|
569
|
+
const defaultValue = column.hasDefault ? column.default : column.nullable ? null : void 0;
|
|
570
|
+
if (defaultValue !== void 0 && defaultValue !== publishedValue) guaranteedPrivate = true;
|
|
571
|
+
}
|
|
572
|
+
if (!guaranteedPrivate) {
|
|
573
|
+
return fail("client.create has no fixed publication-field default that guarantees a private row. Give at least one predicate field a private default, or use a server operation.");
|
|
574
|
+
}
|
|
575
|
+
}
|
|
423
576
|
for (const column of columns) {
|
|
424
577
|
if (column.helper !== "id" && !column.nullable && !column.hasDefault && !column.autoNow && !writes.create.includes(column.name)) {
|
|
425
578
|
return fail(`client.create must include required field "${column.name}", or that field needs a declared default.`);
|
|
@@ -463,7 +616,14 @@ function bakedTableSchemaFromDeclared(declaredJson) {
|
|
|
463
616
|
if (col.nullable === true) entry.nul = 1;
|
|
464
617
|
if (implicitDefault || col.hasDefault === true || col.autoNow === true) entry.d = 1;
|
|
465
618
|
out.push(entry);
|
|
466
|
-
permissionColumns.push({
|
|
619
|
+
permissionColumns.push({
|
|
620
|
+
name: entry.n,
|
|
621
|
+
helper: col.helper,
|
|
622
|
+
nullable: col.nullable === true,
|
|
623
|
+
hasDefault: col.hasDefault === true,
|
|
624
|
+
autoNow: col.autoNow === true,
|
|
625
|
+
...col.default === null || ["string", "number", "boolean"].includes(typeof col.default) ? { default: col.default } : {}
|
|
626
|
+
});
|
|
467
627
|
if (col.helper === "id") primaryKeys.push(entry.n);
|
|
468
628
|
}
|
|
469
629
|
const relations = bakedRelationsFromDeclared(shape.relations);
|
|
@@ -479,11 +639,13 @@ function bakedTableSchemaFromDeclared(declaredJson) {
|
|
|
479
639
|
if (!scope || typeof scope !== "object" || primaryKeys.length !== 1) return null;
|
|
480
640
|
const sc = scope;
|
|
481
641
|
if (typeof sc.kind !== "string") return null;
|
|
642
|
+
const policyIdentity = sc.kind === "policy" ? declaredPolicyIdentity(sc.policy) : {};
|
|
482
643
|
const parsed = parseClientPermissions(shape.client, permissionColumns, {
|
|
483
644
|
kind: sc.kind,
|
|
484
645
|
...visitors ? { visitors: true } : {},
|
|
485
646
|
...typeof sc.column === "string" ? { column: sc.column } : {},
|
|
486
|
-
...Array.isArray(sc.group) && sc.group.every((value) => typeof value === "string") ? { group: sc.group } : {}
|
|
647
|
+
...Array.isArray(sc.group) && sc.group.every((value) => typeof value === "string") ? { group: sc.group } : {},
|
|
648
|
+
...policyIdentity
|
|
487
649
|
});
|
|
488
650
|
if (!parsed.ok) return null;
|
|
489
651
|
if (Array.isArray(parsed.permissions.read) && !parsed.permissions.read.includes(primaryKeys[0])) return null;
|
|
@@ -494,10 +656,48 @@ function bakedTableSchemaFromDeclared(declaredJson) {
|
|
|
494
656
|
if (!member) return null;
|
|
495
657
|
return relations ? { columns: out, member, relations, ...browser } : { columns: out, member, ...browser };
|
|
496
658
|
}
|
|
659
|
+
if (scope !== null && typeof scope === "object" && scope.kind === "policy") {
|
|
660
|
+
const policy = bakedPolicyFromDeclared(scope.policy);
|
|
661
|
+
if (!policy || policy.k !== "a" && policy.k !== "p") return null;
|
|
662
|
+
return relations ? { columns: out, policy, relations, ...browser } : { columns: out, policy, ...browser };
|
|
663
|
+
}
|
|
497
664
|
const author = scope !== null && typeof scope === "object" && scope.kind === "shared" ? SHARED_AUTHOR_COLUMN : void 0;
|
|
498
665
|
if (author) out.push({ n: author, t: "text", nul: 1 });
|
|
499
666
|
return { columns: out, ...relations ? { relations } : {}, ...author ? { author } : {}, ...visitors ? { visitors } : {}, ...browser };
|
|
500
667
|
}
|
|
668
|
+
function declaredPolicyIdentity(value) {
|
|
669
|
+
if (!value || typeof value !== "object") return {};
|
|
670
|
+
const node = value;
|
|
671
|
+
if (node.kind === "owner" && typeof node.column === "string") return { column: node.column };
|
|
672
|
+
if (node.kind === "member" && Array.isArray(node.group) && node.group.every((entry) => typeof entry === "string")) return { group: node.group };
|
|
673
|
+
if (node.kind === "any_of" && Array.isArray(node.policies)) {
|
|
674
|
+
return node.policies.reduce((all, child) => ({ ...all, ...declaredPolicyIdentity(child) }), {});
|
|
675
|
+
}
|
|
676
|
+
return {};
|
|
677
|
+
}
|
|
678
|
+
function bakedPolicyFromDeclared(value, depth = 0) {
|
|
679
|
+
if (depth > 2 || value === null || typeof value !== "object") return null;
|
|
680
|
+
const node = value;
|
|
681
|
+
if (node.kind === "owner") {
|
|
682
|
+
return typeof node.column === "string" && SAFE_SCOPE_IDENTIFIER.test(node.column) ? { k: "o", c: node.column.toLowerCase() } : null;
|
|
683
|
+
}
|
|
684
|
+
if (node.kind === "member") {
|
|
685
|
+
const member = bakedMemberFromDeclared(node);
|
|
686
|
+
return member ? { k: "m", m: member } : null;
|
|
687
|
+
}
|
|
688
|
+
if (node.kind === "any_of" && Array.isArray(node.policies) && node.policies.length === 2) {
|
|
689
|
+
if (depth > 1 || node.policies[0]?.kind !== "owner" || node.policies[1]?.kind !== "member") return null;
|
|
690
|
+
const left = bakedPolicyFromDeclared(node.policies[0], depth + 1);
|
|
691
|
+
const right = bakedPolicyFromDeclared(node.policies[1], depth + 1);
|
|
692
|
+
return left && right ? { k: "a", p: [left, right] } : null;
|
|
693
|
+
}
|
|
694
|
+
if (node.kind === "parent" && typeof node.via === "string" && SAFE_SCOPE_IDENTIFIER.test(node.via) && typeof node.table === "string" && SAFE_SCOPE_IDENTIFIER.test(node.table) && typeof node.parentKey === "string" && SAFE_SCOPE_IDENTIFIER.test(node.parentKey)) {
|
|
695
|
+
if (depth !== 0 || node.policy?.kind === "parent") return null;
|
|
696
|
+
const inherited = bakedPolicyFromDeclared(node.policy, depth + 1);
|
|
697
|
+
return inherited ? { k: "p", v: node.via.toLowerCase(), t: node.table.toLowerCase(), pk: node.parentKey.toLowerCase(), p: inherited } : null;
|
|
698
|
+
}
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
501
701
|
function bakedRelationsFromDeclared(value) {
|
|
502
702
|
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
503
703
|
const out = [];
|
|
@@ -536,6 +736,20 @@ function bakedMemberFromDeclared(scope) {
|
|
|
536
736
|
}
|
|
537
737
|
|
|
538
738
|
// worker/src/utils/db-schema-deploy/extract-schema-ts.ts
|
|
739
|
+
function policyOwner(policy) {
|
|
740
|
+
if (policy.kind === "owner") return policy;
|
|
741
|
+
if (policy.kind === "any_of") return policy.policies.map(policyOwner).find((value) => value !== null) ?? null;
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
function scopeOwner(scope) {
|
|
745
|
+
return scope.kind === "owner" ? scope : scope.kind === "policy" ? policyOwner(scope.policy) : null;
|
|
746
|
+
}
|
|
747
|
+
function declaredOwnerColumn(scope) {
|
|
748
|
+
return scopeOwner(scope)?.column ?? null;
|
|
749
|
+
}
|
|
750
|
+
function declaredScopeIntent(scope) {
|
|
751
|
+
return scope.kind === "owner" ? "scoped" : scope.kind;
|
|
752
|
+
}
|
|
539
753
|
var COLUMN_HELPERS = /* @__PURE__ */ new Set([
|
|
540
754
|
"id",
|
|
541
755
|
"text",
|
|
@@ -546,7 +760,7 @@ var COLUMN_HELPERS = /* @__PURE__ */ new Set([
|
|
|
546
760
|
"json",
|
|
547
761
|
"blob"
|
|
548
762
|
]);
|
|
549
|
-
var SCOPE_HELPERS = /* @__PURE__ */ new Set(["owner", "shared", "serverOnly", "member"]);
|
|
763
|
+
var SCOPE_HELPERS = /* @__PURE__ */ new Set(["owner", "shared", "serverOnly", "member", "anyOf", "parent"]);
|
|
550
764
|
var TABLE_MARKER_HELPERS = /* @__PURE__ */ new Set(["removedTable", "exported"]);
|
|
551
765
|
var SAFE_IDENT = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
552
766
|
var RESERVED_PREFIXES = ["app_", "sqlite_", "d1_", "__sw_"];
|
|
@@ -738,7 +952,15 @@ function readClientPermissions(r) {
|
|
|
738
952
|
const key = r.readKey("for a client permission");
|
|
739
953
|
if (Object.prototype.hasOwnProperty.call(out, key.name)) r.fail(`client declares "${key.name}" twice.`);
|
|
740
954
|
r.expectPunct(":", `after client permission "${key.name}"`);
|
|
741
|
-
if (r.
|
|
955
|
+
if (key.name === "publicRead" && r.peek().kind === "punct" && r.peek().value === "{") {
|
|
956
|
+
r.expectPunct("{", "opening client.publicRead");
|
|
957
|
+
const whereKey = r.readKey("in client.publicRead");
|
|
958
|
+
if (whereKey.name !== "where") r.fail("client.publicRead only accepts a where object.");
|
|
959
|
+
r.expectPunct(":", "after client.publicRead.where");
|
|
960
|
+
out[key.name] = { where: Object.fromEntries([...r.readLiteralObject("for client.publicRead.where")].map(([name, item]) => [name, item.value])) };
|
|
961
|
+
r.tryPunct(",");
|
|
962
|
+
r.expectPunct("}", "closing client.publicRead");
|
|
963
|
+
} else if (r.tryPunct("[")) {
|
|
742
964
|
const fields = [];
|
|
743
965
|
while (!r.tryPunct("]")) {
|
|
744
966
|
fields.push(r.readLiteral(`in client.${key.name}`));
|
|
@@ -995,6 +1217,65 @@ function readMemberScope(r, tableName, scopeLine) {
|
|
|
995
1217
|
}
|
|
996
1218
|
return { kind: "member", group, membership, memberUser, memberGroup };
|
|
997
1219
|
}
|
|
1220
|
+
function readPolicyOwner(r, tableName, line) {
|
|
1221
|
+
r.expectPunct("(", 'after "owner"');
|
|
1222
|
+
let column = DEFAULT_OWNER_COLUMN;
|
|
1223
|
+
if (!r.tryPunct(")")) {
|
|
1224
|
+
const opts = r.readLiteralObject(`in owner() inside anyOf() for table "${tableName}"`);
|
|
1225
|
+
for (const [key, { value, line: optionLine }] of opts) {
|
|
1226
|
+
if (key !== "column") {
|
|
1227
|
+
throw new SchemaTsError(`line ${optionLine}: owner() inside anyOf() only allows column. Visitor ownership is not part of policy scope.`);
|
|
1228
|
+
}
|
|
1229
|
+
if (typeof value !== "string" || !SAFE_IDENT.test(value) || value.length > MAX_NAME_LENGTH) {
|
|
1230
|
+
throw new SchemaTsError(`line ${optionLine}: owner({ column }) on table "${tableName}" must be a valid column name.`);
|
|
1231
|
+
}
|
|
1232
|
+
column = value.toLowerCase();
|
|
1233
|
+
}
|
|
1234
|
+
r.tryPunct(",");
|
|
1235
|
+
r.expectPunct(")", "closing owner()");
|
|
1236
|
+
}
|
|
1237
|
+
return { kind: "owner", column };
|
|
1238
|
+
}
|
|
1239
|
+
function readPolicyMember(r, tableName, line) {
|
|
1240
|
+
r.expectPunct("(", 'after "member"');
|
|
1241
|
+
const scope = readMemberScope(r, tableName, line);
|
|
1242
|
+
r.tryPunct(",");
|
|
1243
|
+
r.expectPunct(")", "closing member()");
|
|
1244
|
+
if (scope.kind !== "member") throw new SchemaTsError(`line ${line}: invalid member() policy.`);
|
|
1245
|
+
return scope;
|
|
1246
|
+
}
|
|
1247
|
+
function readAnyOfScope(r, tableName, line) {
|
|
1248
|
+
r.expectPunct("(", 'after "anyOf"');
|
|
1249
|
+
const first = r.expectIdent(`for the first policy in anyOf() on table "${tableName}"`);
|
|
1250
|
+
const firstPolicy = first.value === "owner" ? readPolicyOwner(r, tableName, first.line) : first.value === "member" ? readPolicyMember(r, tableName, first.line) : null;
|
|
1251
|
+
if (!firstPolicy) throw new SchemaTsError(`line ${first.line}: anyOf() only accepts owner() and member().`);
|
|
1252
|
+
r.expectPunct(",", `between the two policies in anyOf() on table "${tableName}"`);
|
|
1253
|
+
const second = r.expectIdent(`for the second policy in anyOf() on table "${tableName}"`);
|
|
1254
|
+
const secondPolicy = second.value === "owner" ? readPolicyOwner(r, tableName, second.line) : second.value === "member" ? readPolicyMember(r, tableName, second.line) : null;
|
|
1255
|
+
if (!secondPolicy) throw new SchemaTsError(`line ${second.line}: anyOf() only accepts owner() and member().`);
|
|
1256
|
+
r.tryPunct(",");
|
|
1257
|
+
r.expectPunct(")", "closing anyOf()");
|
|
1258
|
+
if (firstPolicy.kind === secondPolicy.kind) {
|
|
1259
|
+
throw new SchemaTsError(`line ${line}: anyOf() requires exactly one owner() and one member() policy.`);
|
|
1260
|
+
}
|
|
1261
|
+
const owner = firstPolicy.kind === "owner" ? firstPolicy : secondPolicy;
|
|
1262
|
+
const member = firstPolicy.kind === "member" ? firstPolicy : secondPolicy;
|
|
1263
|
+
return { kind: "policy", policy: { kind: "any_of", policies: [owner, member] } };
|
|
1264
|
+
}
|
|
1265
|
+
function readParentScope(r, tableName, line) {
|
|
1266
|
+
r.expectPunct("(", 'after "parent"');
|
|
1267
|
+
const opts = r.readLiteralObject(`in parent() options for table "${tableName}"`);
|
|
1268
|
+
r.tryPunct(",");
|
|
1269
|
+
r.expectPunct(")", "closing parent()");
|
|
1270
|
+
if (opts.size !== 1 || !opts.has("via")) {
|
|
1271
|
+
throw new SchemaTsError(`line ${line}: parent() on table "${tableName}" needs exactly { via: 'foreign_key' }.`);
|
|
1272
|
+
}
|
|
1273
|
+
const value = opts.get("via").value;
|
|
1274
|
+
if (typeof value !== "string" || !SAFE_IDENT.test(value) || value.length > MAX_NAME_LENGTH) {
|
|
1275
|
+
throw new SchemaTsError(`line ${opts.get("via").line}: parent({ via }) on table "${tableName}" must name a valid declared foreign-key column.`);
|
|
1276
|
+
}
|
|
1277
|
+
return { kind: "policy", policy: { kind: "parent", via: value.toLowerCase(), table: "", parentKey: "", policy: { kind: "owner", column: "" } } };
|
|
1278
|
+
}
|
|
998
1279
|
function readRelations(r, tableName) {
|
|
999
1280
|
r.expectPunct("{", `for "relations" of table "${tableName}"`);
|
|
1000
1281
|
const out = [];
|
|
@@ -1041,9 +1322,11 @@ function readScope(r, tableName) {
|
|
|
1041
1322
|
const tok = r.expectIdent(`for the scope of table "${tableName}"`);
|
|
1042
1323
|
if (!SCOPE_HELPERS.has(tok.value)) {
|
|
1043
1324
|
throw new SchemaTsError(
|
|
1044
|
-
`line ${tok.line}: unknown scope "${tok.value}" on table "${tableName}". Use owner(), member(), shared(), or serverOnly().`
|
|
1325
|
+
`line ${tok.line}: unknown scope "${tok.value}" on table "${tableName}". Use owner(), member(), anyOf(), parent(), shared(), or serverOnly().`
|
|
1045
1326
|
);
|
|
1046
1327
|
}
|
|
1328
|
+
if (tok.value === "anyOf") return readAnyOfScope(r, tableName, tok.line);
|
|
1329
|
+
if (tok.value === "parent") return readParentScope(r, tableName, tok.line);
|
|
1047
1330
|
if (tok.value === "member") {
|
|
1048
1331
|
r.expectPunct("(", 'after "member"');
|
|
1049
1332
|
const scope = readMemberScope(r, tableName, tok.line);
|
|
@@ -1215,19 +1498,20 @@ function readTable(r, rawName, nameLine) {
|
|
|
1215
1498
|
r.tryPunct(",");
|
|
1216
1499
|
r.expectPunct(")", `closing table("${rawName}")`);
|
|
1217
1500
|
scope ??= { kind: "owner", column: DEFAULT_OWNER_COLUMN };
|
|
1218
|
-
|
|
1501
|
+
const managedOwner = scopeOwner(scope);
|
|
1502
|
+
if (columns.some((column) => column.name === SHARED_AUTHOR_COLUMN || column.renamedFrom === SHARED_AUTHOR_COLUMN) || removedColumns.includes(SHARED_AUTHOR_COLUMN) || managedOwner?.column === SHARED_AUTHOR_COLUMN) {
|
|
1219
1503
|
throw new SchemaTsError(`line ${nameLine}: "${SHARED_AUTHOR_COLUMN}" is platform-maintained row authorship and cannot be declared, removed, renamed, or used as a custom owner column.`);
|
|
1220
1504
|
}
|
|
1221
|
-
if (
|
|
1222
|
-
const clash = columns.find((c) => c.name ===
|
|
1505
|
+
if (managedOwner) {
|
|
1506
|
+
const clash = columns.find((c) => c.name === managedOwner.column);
|
|
1223
1507
|
if (clash) {
|
|
1224
1508
|
throw new SchemaTsError(
|
|
1225
|
-
`line ${nameLine}: table "${rawName}" declares the column "${
|
|
1509
|
+
`line ${nameLine}: table "${rawName}" declares the column "${managedOwner.column}", but that is its owner column, which the platform creates and manages. Remove it from the columns \u2014 or pick a different owner column with owner({ column: '\u2026' }).`
|
|
1226
1510
|
);
|
|
1227
1511
|
}
|
|
1228
|
-
if (removedColumns.includes(
|
|
1512
|
+
if (removedColumns.includes(managedOwner.column)) {
|
|
1229
1513
|
throw new SchemaTsError(
|
|
1230
|
-
`line ${nameLine}: table "${rawName}" marks its owner column "${
|
|
1514
|
+
`line ${nameLine}: table "${rawName}" marks its owner column "${managedOwner.column}" as removed(). The owner column carries the table's private scope and cannot be removed while owner() is part of that scope.`
|
|
1231
1515
|
);
|
|
1232
1516
|
}
|
|
1233
1517
|
}
|
|
@@ -1240,7 +1524,7 @@ function readTable(r, rawName, nameLine) {
|
|
|
1240
1524
|
`line ${nameLine}: "${rawName}"."${c.name}" declares renamedFrom: '${c.renamedFrom}', but "${c.renamedFrom}" is still declared as a column. A rename replaces the old column \u2014 remove its declaration.`
|
|
1241
1525
|
);
|
|
1242
1526
|
}
|
|
1243
|
-
if (
|
|
1527
|
+
if (managedOwner && c.renamedFrom === managedOwner.column) {
|
|
1244
1528
|
throw new SchemaTsError(
|
|
1245
1529
|
`line ${nameLine}: "${rawName}"."${c.name}" declares renamedFrom: '${c.renamedFrom}', but that is the table's owner column, which the platform creates and manages \u2014 it cannot be renamed into an ordinary column.`
|
|
1246
1530
|
);
|
|
@@ -1258,7 +1542,7 @@ function readTable(r, rawName, nameLine) {
|
|
|
1258
1542
|
renameSources.add(c.renamedFrom);
|
|
1259
1543
|
}
|
|
1260
1544
|
const known = new Set(columns.map((c) => c.name));
|
|
1261
|
-
if (
|
|
1545
|
+
if (managedOwner) known.add(managedOwner.column);
|
|
1262
1546
|
const indexes = [];
|
|
1263
1547
|
for (const entry of indexEntries ?? []) {
|
|
1264
1548
|
for (const colName of entry.columns) {
|
|
@@ -1291,7 +1575,12 @@ function readTable(r, rawName, nameLine) {
|
|
|
1291
1575
|
if (columns.filter((column) => column.helper === "id").length !== 1) {
|
|
1292
1576
|
throw new SchemaTsError(`line ${nameLine}: table "${rawName}": client operations require exactly one id() column.`);
|
|
1293
1577
|
}
|
|
1294
|
-
const
|
|
1578
|
+
const policyMember = scope.kind === "policy" && scope.policy.kind === "any_of" ? scope.policy.policies.find((node) => node.kind === "member") : void 0;
|
|
1579
|
+
const parsed = parseClientPermissions(clientInput, columns, {
|
|
1580
|
+
...scope,
|
|
1581
|
+
...managedOwner ? { column: managedOwner.column } : {},
|
|
1582
|
+
...policyMember ? { group: policyMember.group } : {}
|
|
1583
|
+
});
|
|
1295
1584
|
if (!parsed.ok) throw new SchemaTsError(`line ${nameLine}: table "${rawName}": ${parsed.message}`);
|
|
1296
1585
|
const key = columns.find((column) => column.helper === "id").name;
|
|
1297
1586
|
if (Array.isArray(parsed.permissions.read) && !parsed.permissions.read.includes(key)) {
|
|
@@ -1394,41 +1683,83 @@ function extractSchemaTs(source) {
|
|
|
1394
1683
|
const tableByName = new Map(tables.map((t) => [t.name, t]));
|
|
1395
1684
|
const knownColumns = (t) => {
|
|
1396
1685
|
const s = new Set(t.columns.map((c) => c.name));
|
|
1397
|
-
|
|
1686
|
+
const owner = scopeOwner(t.scope);
|
|
1687
|
+
if (owner) s.add(owner.column);
|
|
1398
1688
|
return s;
|
|
1399
1689
|
};
|
|
1690
|
+
const privatePolicy = (scope) => {
|
|
1691
|
+
if (scope.kind === "owner" && !scope.visitors) return { kind: "owner", column: scope.column };
|
|
1692
|
+
if (scope.kind === "member") return scope;
|
|
1693
|
+
if (scope.kind === "policy" && scope.policy.kind !== "parent") return scope.policy;
|
|
1694
|
+
return null;
|
|
1695
|
+
};
|
|
1400
1696
|
for (const t of tables) {
|
|
1401
|
-
if (t.scope.kind !== "
|
|
1402
|
-
const
|
|
1403
|
-
const
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
`Table "${t.name}" is member-scoped on "${g}", but "${g}" is not one of its declared columns. The group-key columns must be real columns on the table.`
|
|
1408
|
-
);
|
|
1409
|
-
}
|
|
1697
|
+
if (t.scope.kind !== "policy" || t.scope.policy.kind !== "parent") continue;
|
|
1698
|
+
const pending = t.scope.policy;
|
|
1699
|
+
const fk = t.columns.find((column) => column.name === pending.via);
|
|
1700
|
+
if (!fk || !fk.references) {
|
|
1701
|
+
errors.push(`Table "${t.name}" uses parent({ via: '${pending.via}' }), but "${pending.via}" is not a declared scalar foreign key.`);
|
|
1702
|
+
continue;
|
|
1410
1703
|
}
|
|
1411
|
-
const
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
);
|
|
1704
|
+
const parentTable = tableByName.get(fk.references);
|
|
1705
|
+
const parentKey = parentTable?.columns.find((column) => column.helper === "id");
|
|
1706
|
+
const inherited = parentTable ? privatePolicy(parentTable.scope) : null;
|
|
1707
|
+
if (!parentTable || !parentKey || !inherited) {
|
|
1708
|
+
errors.push(`Table "${t.name}" uses parent({ via: '${pending.via}' }), but its referenced parent must have one id() key and a direct owner(), member(), or anyOf(owner(), member()) private scope.`);
|
|
1416
1709
|
continue;
|
|
1417
1710
|
}
|
|
1418
|
-
const
|
|
1419
|
-
if (
|
|
1420
|
-
errors.push(`Table "${
|
|
1711
|
+
const compatible = fk.helper === parentKey.helper && (fk.helper !== "id" || fk.uuid === parentKey.uuid) || fk.helper === "integer" && parentKey.helper === "id" && !parentKey.uuid || fk.helper === "text" && parentKey.helper === "id" && parentKey.uuid;
|
|
1712
|
+
if (!compatible) {
|
|
1713
|
+
errors.push(`Table "${t.name}" uses parent({ via: '${pending.via}' }), but that foreign key does not have the same scalar type as "${parentTable.name}"."${parentKey.name}".`);
|
|
1714
|
+
continue;
|
|
1421
1715
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
);
|
|
1716
|
+
t.scope.policy = { kind: "parent", via: pending.via, table: parentTable.name, parentKey: parentKey.name, policy: inherited };
|
|
1717
|
+
if (t.client?.create && !t.client.create.includes(pending.via)) {
|
|
1718
|
+
errors.push(`Table "${t.name}" uses parent({ via: '${pending.via}' }) and client.create must include "${pending.via}" so every new row explicitly selects an authorized parent.`);
|
|
1426
1719
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1720
|
+
}
|
|
1721
|
+
const memberPolicies = (scope) => {
|
|
1722
|
+
const out = [];
|
|
1723
|
+
const visit = (node) => {
|
|
1724
|
+
if (node.kind === "member") out.push(node);
|
|
1725
|
+
else if (node.kind === "any_of") node.policies.forEach(visit);
|
|
1726
|
+
};
|
|
1727
|
+
if (scope.kind === "member") out.push(scope);
|
|
1728
|
+
else if (scope.kind === "policy") visit(scope.policy);
|
|
1729
|
+
return out;
|
|
1730
|
+
};
|
|
1731
|
+
for (const t of tables) {
|
|
1732
|
+
for (const sc of memberPolicies(t.scope)) {
|
|
1733
|
+
const own = knownColumns(t);
|
|
1734
|
+
for (const g of sc.group) {
|
|
1735
|
+
if (!own.has(g)) {
|
|
1736
|
+
errors.push(
|
|
1737
|
+
`Table "${t.name}" is member-scoped on "${g}", but "${g}" is not one of its declared columns. The group-key columns must be real columns on the table.`
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
const membershipTable = tableByName.get(sc.membership);
|
|
1742
|
+
if (!membershipTable) {
|
|
1429
1743
|
errors.push(
|
|
1430
|
-
`
|
|
1744
|
+
`Table "${t.name}" is member-scoped through the membership table "${sc.membership}", which is not declared in db/schema.ts. The membership table must be a managed table in the same schema.`
|
|
1431
1745
|
);
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
const membershipCols = knownColumns(membershipTable);
|
|
1749
|
+
if (membershipTable.client && (membershipTable.client.create !== null || membershipTable.client.update !== null || membershipTable.client.delete)) {
|
|
1750
|
+
errors.push(`Table "${sc.membership}" grants membership access to "${t.name}" and cannot allow client writes. Change membership through a server function.`);
|
|
1751
|
+
}
|
|
1752
|
+
if (!membershipCols.has(sc.memberUser)) {
|
|
1753
|
+
errors.push(
|
|
1754
|
+
`The membership table "${sc.membership}" (for member-scoped "${t.name}") has no "${sc.memberUser}" column named in member({ member_user }).`
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
for (const mg of sc.memberGroup) {
|
|
1758
|
+
if (!membershipCols.has(mg)) {
|
|
1759
|
+
errors.push(
|
|
1760
|
+
`The membership table "${sc.membership}" (for member-scoped "${t.name}") has no "${mg}" column named in member({ member_group }).`
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1432
1763
|
}
|
|
1433
1764
|
}
|
|
1434
1765
|
}
|
|
@@ -1549,8 +1880,9 @@ function schemaAuthorityFromSource(files) {
|
|
|
1549
1880
|
const baked = bakedTableSchemaFromDeclared(JSON.stringify(canonicalTableShape(table)));
|
|
1550
1881
|
if (!baked) throw new Error(`Could not derive the client contract for declared table "${table.name}".`);
|
|
1551
1882
|
declareEntry(schema, table.name, baked);
|
|
1552
|
-
declareEntry(intents, table.name, table.scope
|
|
1553
|
-
|
|
1883
|
+
declareEntry(intents, table.name, declaredScopeIntent(table.scope));
|
|
1884
|
+
const ownerColumn = declaredOwnerColumn(table.scope);
|
|
1885
|
+
if (declaredScopeIntent(table.scope) === "scoped" && ownerColumn) declareEntry(scopes, table.name, ownerColumn);
|
|
1554
1886
|
}
|
|
1555
1887
|
return { schema, intents, scopes };
|
|
1556
1888
|
}
|