@somewhere-tech/cli 0.33.0 → 0.33.1

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.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@somewhere-tech/cli",
3
- "version": "0.33.0",
3
+ "version": "0.33.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@somewhere-tech/cli",
9
- "version": "0.33.0",
9
+ "version": "0.33.1",
10
10
  "bundleDependencies": [
11
11
  "@modelcontextprotocol/sdk",
12
12
  "chokidar",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@somewhere-tech/cli",
3
- "version": "0.33.0",
3
+ "version": "0.33.1",
4
4
  "description": "CLI for somewhere.tech — auth, projects, deploy, pull, promote, db, logs, env, MCP bridge.",
5
5
  "keywords": [
6
6
  "somewhere",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "esbuild": "0.24.0",
3
3
  "source_files": {
4
- "worker/containers/compile/declared-data-contract.cjs": "7a2c20ddddd912ae513f3fff2614829b18c3e19954f9c48314ca96f1ce3143a1",
4
+ "worker/containers/compile/declared-data-contract.cjs": "b91609429f4e54d54a80848db8cf13b5e1965625c45f423f56e51e941c5daf9c",
5
5
  "worker/containers/compile/runtime-types.cjs": "a458232f605ac60202b58c6cae725a813d22479498f436668b8ad553678576ef",
6
- "worker/containers/compile/schema-types.cjs": "c16fa8cabc8f5cff50777623f94f8cb009fee3ac47d53b471894be9c698d614a",
6
+ "worker/containers/compile/schema-types.cjs": "d6985d40a1f4462615eb65e0350fdd23d53de7c4beaa3ff574e45a036fbdec3e",
7
7
  "worker/containers/compile/typed-data.cjs": "57b458e16a373e42d48a0e527317fcdf609299ae368eb539a8b582b9a47abb15",
8
- "worker/src/utils/authority/scope-enforcement.ts": "98c242359240b8918c1b71af73f17b6b4200559a896f47f6032ef771f543201f",
8
+ "worker/src/utils/authority/scope-enforcement.ts": "25c4aa81f0d2dd4b58784c0ada03c041ce48b66728b52cfee3790a841aba766c",
9
9
  "worker/src/utils/db-schema-deploy/client-contract-source.ts": "fc37a8d18affc0d814f40357884e1764eb4bcc9af446626933729b7021fa3c6d",
10
10
  "worker/src/utils/db-schema-deploy/client-permissions.ts": "05b1d68afae151569abfa16b452834a96b3c0a2bf4b04f63d322f05548827222",
11
- "worker/src/utils/db-schema-deploy/extract-schema-ts.ts": "b092afe7c271405126da9e204084fc5528a42025b5583eba25da3907f995a25b",
11
+ "worker/src/utils/db-schema-deploy/extract-schema-ts.ts": "0da51e6884afbbf9b04104f9bf669f0d0163d7cb2b5723a6e9df05b661115a1e",
12
12
  "worker/src/utils/db-schema-deploy/schema-ownership.ts": "e6c811a87bc657707be3518dfe547c6d97718ac073108b126eb7f791c9c08202"
13
13
  },
14
- "sha256": "80171af94d110f5caf717eb7ad6abd95b15ed4125af1cc848151561d67462b8d"
14
+ "sha256": "b9f9cf5b17aa489ddc8ba9937a268a369ec73c91769f90eb20ab38ebc750f4c9"
15
15
  }
@@ -54,9 +54,11 @@ var require_declared_data_contract = __commonJS({
54
54
  if (value.k === "o" && keys === "c,k" && identifier.test(value.c)) return { k: "o", c: value.c };
55
55
  if (value.k === "m" && keys === "k,m") {
56
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] } };
57
+ const { g, m, u, mg, o } = value.m;
58
+ const memberKeys = Object.keys(value.m).sort().join(",");
59
+ if (!["g,m,mg,u", "g,m,mg,o,u"].includes(memberKeys) || !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)) || o !== void 0 && (!Array.isArray(o) || JSON.stringify(o) !== JSON.stringify(["read", "create", "update", "delete"].filter((operation) => o.includes(operation))) || !o.includes("read") && (o.includes("update") || o.includes("delete")))) fail(label);
60
+ const normalizedOperations = o?.length === 4 ? void 0 : o;
61
+ return { k: "m", m: { g: [...g], m, u, mg: [...mg], ...normalizedOperations !== void 0 ? { o: [...normalizedOperations] } : {} } };
60
62
  }
61
63
  if (value.k === "a" && keys === "k,p" && Array.isArray(value.p) && value.p.length === 2) {
62
64
  const left = policy(value.p[0], label), right = policy(value.p[1], label);
@@ -146,12 +148,14 @@ var require_declared_data_contract = __commonJS({
146
148
  let member = null;
147
149
  if (intent === "member") {
148
150
  record(table.member, `${name}.member`);
149
- const { g, m, u, mg } = table.member;
151
+ const { g, m, u, mg, o } = table.member;
150
152
  if (typeof m !== "string" || typeof u !== "string" || !identifier.test(m) || !identifier.test(u) || !Array.isArray(mg) || mg.some((field) => typeof field !== "string" || !identifier.test(field))) fail(`${name}.member`);
151
153
  fields(g, seen, `${name}.member.g`);
152
154
  if (g.length !== mg.length || new Set(mg).size !== mg.length) fail(`${name}.member`);
153
155
  if ((update || []).some((field) => g.includes(field))) fail(`${name} membership writes`);
154
- member = { g: [...g], m, u, mg: [...mg] };
156
+ if (o !== void 0 && (!Array.isArray(o) || JSON.stringify(o) !== JSON.stringify(["read", "create", "update", "delete"].filter((operation) => o.includes(operation))) || !o.includes("read") && (o.includes("update") || o.includes("delete")))) fail(`${name}.member operations`);
157
+ const normalizedOperations = o?.length === 4 ? void 0 : o;
158
+ member = { g: [...g], m, u, mg: [...mg], ...normalizedOperations !== void 0 ? { o: [...normalizedOperations] } : {} };
155
159
  }
156
160
  const normalizedPolicy = intent === "policy" ? policy(table.policy, `${name}.policy`) : null;
157
161
  if (normalizedPolicy && !["a", "p"].includes(normalizedPolicy.k)) fail(`${name}.policy root`);
@@ -346,7 +350,7 @@ function blob(options?: SomewhereSchemaDeclaration.ColumnOptions<never>): Somewh
346
350
  function owner(options?: { column?: string; visitors?: boolean }): SomewhereSchemaDeclaration.OwnerScope;
347
351
  function shared(): SomewhereSchemaDeclaration.Scope;
348
352
  function serverOnly(): SomewhereSchemaDeclaration.Scope;
349
- function member(options: { group: string | string[]; membership: string; member_user: string; member_group: string | string[] }): SomewhereSchemaDeclaration.MemberScope;
353
+ function member(options: { group: string | string[]; membership: string; member_user: string; member_group: string | string[]; operations?: Array<'read' | 'create' | 'update' | 'delete'> }): SomewhereSchemaDeclaration.MemberScope;
350
354
  function hasMany(table: string, foreignKey: string): SomewhereSchemaDeclaration.Relation;
351
355
  function removed(): SomewhereSchemaDeclaration.ColumnMarker;
352
356
  function removedTable(): SomewhereSchemaDeclaration.TableMarker;
@@ -732,7 +736,15 @@ function bakedMemberFromDeclared(scope) {
732
736
  if (!g || !mg || g.length !== mg.length) return null;
733
737
  if (typeof s.membership !== "string" || !SAFE_SCOPE_IDENTIFIER.test(s.membership)) return null;
734
738
  if (typeof s.memberUser !== "string" || !SAFE_SCOPE_IDENTIFIER.test(s.memberUser)) return null;
735
- return { g, m: s.membership.toLowerCase(), u: s.memberUser.toLowerCase(), mg };
739
+ let o;
740
+ if (s.operations !== void 0) {
741
+ if (!Array.isArray(s.operations)) return null;
742
+ const operations = s.operations;
743
+ const allowed = ["read", "create", "update", "delete"];
744
+ if (JSON.stringify(operations) !== JSON.stringify(allowed.filter((operation) => operations.includes(operation))) || !operations.includes("read") && (operations.includes("update") || operations.includes("delete"))) return null;
745
+ o = operations.length === allowed.length ? void 0 : operations;
746
+ }
747
+ return { g, m: s.membership.toLowerCase(), u: s.memberUser.toLowerCase(), mg, ...o !== void 0 ? { o } : {} };
736
748
  }
737
749
 
738
750
  // worker/src/utils/db-schema-deploy/extract-schema-ts.ts
@@ -771,6 +783,7 @@ var MAX_INDEX_COLUMNS = 8;
771
783
  var MAX_NAME_LENGTH = 64;
772
784
  var MAX_SOURCE_BYTES = 256 * 1024;
773
785
  var DEFAULT_OWNER_COLUMN = "user_id";
786
+ var MEMBER_OPERATIONS = ["read", "create", "update", "delete"];
774
787
  var SchemaTsError = class extends Error {
775
788
  };
776
789
  function tokenize(source) {
@@ -1166,12 +1179,33 @@ function validateMemberColumns(names, optionLabel, tableName, line) {
1166
1179
  seen.add(name);
1167
1180
  }
1168
1181
  }
1182
+ function readMemberOperations(r, tableName) {
1183
+ r.expectPunct("[", `for member({ operations }) on table "${tableName}"`);
1184
+ const seen = /* @__PURE__ */ new Set();
1185
+ while (!r.tryPunct("]")) {
1186
+ const token = r.next();
1187
+ if (token.kind !== "string" || !MEMBER_OPERATIONS.includes(token.value)) {
1188
+ throw new SchemaTsError(`line ${token.line}: member({ operations }) on table "${tableName}" only accepts 'read', 'create', 'update', and 'delete'.`);
1189
+ }
1190
+ seen.add(token.value);
1191
+ if (!r.tryPunct(",")) {
1192
+ r.expectPunct("]", `closing member({ operations }) on table "${tableName}"`);
1193
+ break;
1194
+ }
1195
+ }
1196
+ const normalized = MEMBER_OPERATIONS.filter((operation) => seen.has(operation));
1197
+ if (!seen.has("read") && (seen.has("update") || seen.has("delete"))) {
1198
+ throw new SchemaTsError(`member({ operations }) on table "${tableName}" must include 'read' when it includes 'update' or 'delete', because mutations return the affected row.`);
1199
+ }
1200
+ return normalized.length === MEMBER_OPERATIONS.length ? void 0 : normalized;
1201
+ }
1169
1202
  function readMemberScope(r, tableName, scopeLine) {
1170
1203
  r.expectPunct("{", `in member() options for table "${tableName}" \u2014 member() needs { group, membership, member_user, member_group }`);
1171
1204
  let group = null;
1172
1205
  let membership = null;
1173
1206
  let memberUser = null;
1174
1207
  let memberGroup = null;
1208
+ let operations, operationsSeen = false;
1175
1209
  while (!r.tryPunct("}")) {
1176
1210
  const key = r.readKey(`for a member() option of table "${tableName}"`);
1177
1211
  r.expectPunct(":", `after "${key.name}" in member() for table "${tableName}"`);
@@ -1183,9 +1217,13 @@ function readMemberScope(r, tableName, scopeLine) {
1183
1217
  memberUser = readSingleColumn(r, `member({ member_user }) on table "${tableName}"`);
1184
1218
  } else if (key.name === "member_group") {
1185
1219
  memberGroup = readColumnOrColumnList(r, `member({ member_group }) on table "${tableName}"`);
1220
+ } else if (key.name === "operations") {
1221
+ if (operationsSeen) throw new SchemaTsError(`line ${key.line}: member() on table "${tableName}" declares operations more than once.`);
1222
+ operationsSeen = true;
1223
+ operations = readMemberOperations(r, tableName);
1186
1224
  } else {
1187
1225
  throw new SchemaTsError(
1188
- `line ${key.line}: member() on table "${tableName}" has an unknown option "${key.name}". Allowed: group, membership, member_user, member_group.`
1226
+ `line ${key.line}: member() on table "${tableName}" has an unknown option "${key.name}". Allowed: group, membership, member_user, member_group, operations.`
1189
1227
  );
1190
1228
  }
1191
1229
  if (!r.tryPunct(",")) {
@@ -1215,7 +1253,7 @@ function readMemberScope(r, tableName, scopeLine) {
1215
1253
  `line ${scopeLine}: member() on table "${tableName}" has a ${group.length}-column group but a ${memberGroup.length}-column member_group. The group key must have the same number of columns on both sides.`
1216
1254
  );
1217
1255
  }
1218
- return { kind: "member", group, membership, memberUser, memberGroup };
1256
+ return { kind: "member", group, membership, memberUser, memberGroup, ...operations !== void 0 ? { operations } : {} };
1219
1257
  }
1220
1258
  function readPolicyOwner(r, tableName, line) {
1221
1259
  r.expectPunct("(", 'after "owner"');