@somewhere-tech/cli 0.12.7 → 0.12.9

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.
@@ -11,6 +11,15 @@ import { dim, error, green, info, red, success, teal, warn, yellow } from '../li
11
11
  export function resolveTargetDir(dir, cwd = process.cwd()) {
12
12
  return dir ? resolve(cwd, dir) : cwd;
13
13
  }
14
+ // `--prebuilt` (alias `--allow-bundled`) opts the deploy out of the
15
+ // raw-source contract: the worker's bundled-deploy guard keeps pre-built /
16
+ // bundled output as-is instead of rejecting it. We surface it as
17
+ // `allow_bundled: true` in the deploy body (the field the worker already
18
+ // reads). Returns true only when a flag was passed, so a normal deploy never
19
+ // sends the field — raw source stays the default.
20
+ export function prebuiltOptIn(opts) {
21
+ return Boolean(opts.prebuilt || opts.allowBundled);
22
+ }
14
23
  export function registerDeploy(program) {
15
24
  program
16
25
  .command('deploy [dir]')
@@ -19,6 +28,12 @@ export function registerDeploy(program) {
19
28
  .option('--scope <scope>', "Partial deploy: 'functions' (backend only, leaves the site untouched) or 'static' (site only, leaves functions untouched). Default deploys both.")
20
29
  .option('--dry-run', 'Show what would change (added / modified / removed files) without deploying')
21
30
  .option('--replace-functions', 'Delete deployed functions that are not in this directory (repo-as-truth). Default keeps them.')
31
+ .option('--prebuilt', 'Deploy pre-built / bundled output (e.g. a dist/ folder) instead of raw source. ' +
32
+ 'Raw source is the default and recommended path: the platform compiles your TSX/JSX/TS ' +
33
+ 'on deploy, which keeps the app editable end-to-end. Choosing --prebuilt trades that away — ' +
34
+ 'pull/export round-trips, find/replace patches, and visual (source-map) editing are disabled ' +
35
+ 'for the bundled files. Use it only when you need full control of your own build.')
36
+ .option('--allow-bundled', 'Alias for --prebuilt.')
22
37
  .action(async (dir, opts) => {
23
38
  const token = getToken();
24
39
  const client = new ApiClient(token);
@@ -74,6 +89,13 @@ export function registerDeploy(program) {
74
89
  body.replace_functions = true;
75
90
  if (opts.dryRun)
76
91
  body.dry_run = true;
92
+ // --prebuilt (alias --allow-bundled) opts out of the raw-source
93
+ // contract: the deploy keeps pre-built / bundled output as-is instead
94
+ // of being rejected by the bundled-deploy guard. Omitted entirely when
95
+ // not set so a normal deploy stays the raw-source default.
96
+ const prebuilt = prebuiltOptIn(opts);
97
+ if (prebuilt)
98
+ body.allow_bundled = true;
77
99
  if (opts.dryRun) {
78
100
  const plan = await client.call('POST', '/deploy', body, undefined, {
79
101
  timeoutMs: LONG_CALL_TIMEOUT_MS,
@@ -129,6 +151,13 @@ export function registerDeploy(program) {
129
151
  else {
130
152
  success(`Live at ${teal(result.url)}`);
131
153
  }
154
+ // Remind the dev of the round-trip trade-off they opted into. Raw
155
+ // source stays editable end-to-end; bundled files do not — pull/export
156
+ // round-trips, find/replace patches, and visual editing are disabled
157
+ // for them.
158
+ if (prebuilt) {
159
+ warn('Prebuilt deploy — source round-trip (pull / export / patch / visual edit) is disabled for the bundled files.');
160
+ }
132
161
  // Exit non-zero if any function failed to deploy — a CI step that
133
162
  // shells out to `somewhere deploy` should fail, not pass green.
134
163
  if (result.function_errors && result.function_errors.length > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@somewhere-tech/cli",
3
- "version": "0.12.7",
3
+ "version": "0.12.9",
4
4
  "description": "CLI for somewhere.tech — auth, projects, deploy, pull, promote, db, logs, env, MCP bridge.",
5
5
  "keywords": [
6
6
  "somewhere",
@@ -1,4 +1,4 @@
1
- // VENDORED from worker/src/utils/function-bundle.ts (PLATFORM_CONTEXT_JS) @ 58927b3
1
+ // VENDORED from worker/src/runtime/context.ts (PLATFORM_CONTEXT_JS) @ e8da46e
2
2
  // — the exact runtime deployed functions run against. Do not edit by hand;
3
3
  // re-sync with: node scripts/extract-runtime.mjs <monorepo>
4
4
  // Process-wide cache for sw.auth.me — keyed by the JWT signature
@@ -104,6 +104,16 @@ function buildPlatformContext(env, request) {
104
104
  opts = opts || {};
105
105
  const headers = {
106
106
  'Authorization': 'Bearer ' + apiKey,
107
+ // tsk_95ecae23 — tell the platform which execution slot this bundle is
108
+ // running in (prod vs dev), so the REST-fallback data path (/v1/db/query,
109
+ // /v1/db/batch) and the direct `dev --local` client select the DEV data
110
+ // slot instead of silently defaulting to prod. The slot is baked into the
111
+ // bundle at deploy/run time as PROJECT_ENV (see function-bundle.ts /
112
+ // code-bundle.ts); 'dev' for run_code + preview + draft, 'prod' for the
113
+ // promoted production bundle. The server only DIVERTS off prod when the
114
+ // project is enrolled in DEV_SLOT_ENFORCE_PROJECTS, so an unenrolled
115
+ // project sees this header but its binding is unchanged.
116
+ 'X-Sw-Env-Slot': projectEnv,
107
117
  ...(opts.headers || {}),
108
118
  };
109
119
  if (opts.body && !headers['Content-Type'] && !headers['content-type']) {
@@ -173,6 +183,94 @@ function buildPlatformContext(env, request) {
173
183
  __sw_pendingCookies.push(__sw_authCookie('sw_refresh_token', '', 0));
174
184
  }
175
185
 
186
+ // Expected-failure marking for the cookie helpers (tsk_a5f3e7d3). The
187
+ // routing shim's catch-all turns every uncaught throw into an opaque 500
188
+ // FUNCTION_ERROR — right for real bugs, wrong for "wrong password": the
189
+ // doc'd cookie snippet has no try/catch, so every expected auth failure
190
+ // surfaced as a 500 (and fed the 5xx-based uptime alerting). An error
191
+ // flagged here carries a customer-safe message from the platform auth
192
+ // API; the shim returns it as a structured 4xx with the real code +
193
+ // message instead. Platform 5xx and anything unflagged stay opaque.
194
+ function __sw_markExpected(err) {
195
+ if (err && typeof err.status === 'number' && err.status >= 400 && err.status < 500) {
196
+ err.__sw_expose = true;
197
+ }
198
+ return err;
199
+ }
200
+
201
+ // Arg-shape forgiveness for the cookie helpers (tsk_3bc373ea): every other
202
+ // sw.auth method takes one options object, so agents naturally pass one
203
+ // here too — and req is never read by these helpers. Accepted shapes:
204
+ // (req, email, password) — documented positional form
205
+ // (req, { email, password })
206
+ // ({ email, password }) — req omitted
207
+ // Anything else throws a 400 VALIDATION_ERROR naming the supported shapes,
208
+ // surfaced as a structured 4xx via __sw_expose (never INTERNAL_ERROR/500).
209
+ function __sw_cookieCreds(method, req, email, password) {
210
+ if (email !== null && typeof email === 'object') {
211
+ password = email.password;
212
+ email = email.email;
213
+ } else if (req !== null && typeof req === 'object' &&
214
+ !(req.headers && typeof req.headers.get === 'function') &&
215
+ email === undefined && password === undefined) {
216
+ password = req.password;
217
+ email = req.email;
218
+ }
219
+ if (typeof email !== 'string' || !email || typeof password !== 'string' || !password) {
220
+ const err = new Error('sw.auth.' + method + ': email and password are required — call ' +
221
+ method + '(req, email, password) or ' + method + '(req, { email, password }).');
222
+ err.code = 'VALIDATION_ERROR';
223
+ err.status = 400;
224
+ err.__sw_expose = true;
225
+ throw err;
226
+ }
227
+ return { email: email, password: password };
228
+ }
229
+
230
+ // Cross-origin guard for cookie sessions (tsk_1dd4e1b4). SameSite=Lax
231
+ // stops cross-SITE cookie sends, but every *.<platformDomain> project is
232
+ // the SAME site in browser terms, so a sibling project's page can make
233
+ // the browser attach THIS app's cookies — and the platform's permissive
234
+ // /api/* CORS (reflected origin + credentials) would even let it read the
235
+ // response. A cookie-authed request whose cross-origin source names a
236
+ // different host is therefore treated as unauthenticated, unless both
237
+ // hosts share a customer-owned registrable domain (www.shop.com →
238
+ // shop.com) — never across the shared platform domain (sibling projects
239
+ // are different security principals).
240
+ // - Writes (POST/PUT/PATCH/DELETE): checked against Origin, falling
241
+ // back to Referer (CSRF).
242
+ // - Reads (GET/HEAD): checked only when an explicit CORS Origin header
243
+ // is present (credentialed cross-origin fetch). Top-level navigations
244
+ // carry no Origin and stay allowed — that's exactly what Lax intends —
245
+ // and their Referer legitimately names the previous, often foreign,
246
+ // page, so Referer is ignored for reads.
247
+ // - No Origin/Referer at all (curl, server-to-server), same-origin
248
+ // requests, and Bearer-header auth are all unaffected.
249
+ function __sw_cookieCsrfBlocked(req) {
250
+ const method = (req.method || 'GET').toUpperCase();
251
+ const isRead = method === 'GET' || method === 'HEAD' || method === 'OPTIONS';
252
+ let src = req.headers.get('Origin') || req.headers.get('origin');
253
+ if (!src && !isRead) src = req.headers.get('Referer') || req.headers.get('referer');
254
+ if (!src) return false;
255
+ let srcHost, reqHost;
256
+ try {
257
+ // 'null' (opaque origin: sandboxed iframe, data: page) fails to parse
258
+ // and is rejected — a same-site sandboxed page still sends the cookie
259
+ // under Lax, so it must never authenticate.
260
+ srcHost = new URL(src).hostname;
261
+ reqHost = new URL(req.url).hostname;
262
+ } catch (_) {
263
+ return true;
264
+ }
265
+ if (srcHost === reqHost) return false;
266
+ const tail2 = function (h) { return h.split('.').slice(-2).join('.'); };
267
+ const t = tail2(srcHost);
268
+ if (t !== tail2(reqHost)) return true;
269
+ if (t === platformDomain) return true;
270
+ if (reqHost === platformDomain || reqHost.slice(-(platformDomain.length + 1)) === '.' + platformDomain) return true;
271
+ return false;
272
+ }
273
+
176
274
  const sw = {
177
275
  project_id: projectId,
178
276
  subdomain: env.SUBDOMAIN || '',
@@ -363,6 +461,105 @@ function buildPlatformContext(env, request) {
363
461
  catch (_) { return {}; }
364
462
  })();
365
463
 
464
+ // Rule-9 grace for newly-baked scopes (tsk_fa2f41f6). Map of
465
+ // lowercased table name -> epoch-ms after which a scope violation
466
+ // THROWS. Before the deadline a violation logs loudly and the
467
+ // query PROCEEDS, so declaring a scope can never 500 already-
468
+ // deployed, previously-working code on the next deploy (the
469
+ // RailTime outage class). Absent/empty -> no grace, enforcement
470
+ // exactly as before. Deadlines come from loadProjectScopesForBake
471
+ // at deploy time; the check is re-evaluated per query, so a
472
+ // window closes on time without needing a redeploy.
473
+ const SCOPE_GRACE = (function () {
474
+ try { return env.PROJECT_SCOPES_GRACE ? JSON.parse(env.PROJECT_SCOPES_GRACE) : {}; }
475
+ catch (_) { return {}; }
476
+ })();
477
+
478
+ // Owner-shaped-but-UNSCOPED tables baked at deploy (scope-hardening,
479
+ // pfb_02e0d66c1275). Map of lowercased table name -> owner-style column.
480
+ // These are GRANDFATHERED tables: they have a user_id/owner_id-style
481
+ // column but NO declared scope, and predate the deploy hard-block, so the
482
+ // deploy still ships them. The runtime uses this set to close the
483
+ // fails-open hole where passing { user } against such a table was a
484
+ // SILENT NO-OP (the table is not in SCOPES, so __sw_checkScope returns
485
+ // null and the raw, unscoped query ran). New deploys cannot introduce
486
+ // such a table (the deploy is blocked), so for new code this set is
487
+ // empty and the no-op cannot arise. Absent/empty -> unchanged behavior.
488
+ const UNSCOPED_OWNER = (function () {
489
+ try { return env.PROJECT_UNSCOPED_OWNER ? JSON.parse(env.PROJECT_UNSCOPED_OWNER) : {}; }
490
+ catch (_) { return {}; }
491
+ })();
492
+
493
+ // Per-table grace deadline (epoch-ms) for the UNSCOPED_OWNER set. Passing
494
+ // { user } against an unscoped-owner table WARNS loudly before the
495
+ // deadline and THROWS SCOPE_NOT_DECLARED after it — same warn-then-arm
496
+ // shape as SCOPE_GRACE. A table in UNSCOPED_OWNER with no deadline here
497
+ // is treated as already armed (throws).
498
+ const UNSCOPED_OWNER_GRACE = (function () {
499
+ try { return env.PROJECT_UNSCOPED_OWNER_GRACE ? JSON.parse(env.PROJECT_UNSCOPED_OWNER_GRACE) : {}; }
500
+ catch (_) { return {}; }
501
+ })();
502
+
503
+ function __sw_scopeInGrace(table) {
504
+ const until = SCOPE_GRACE[table];
505
+ return typeof until === 'number' && Date.now() < until;
506
+ }
507
+
508
+ function __sw_scopeGraceWarn(v, api) {
509
+ try {
510
+ console.error('[scope-grace] ' + api + ' ran UNSCOPED on user-scoped table "' + v.table + '": ' + v.reason +
511
+ ' Enforcement arms at ' + new Date(SCOPE_GRACE[v.table]).toISOString() +
512
+ ' - after that this exact query THROWS SCOPE_VIOLATION. Fix now: pass { user } (the platform auto-scopes simple queries) or { unscoped: true } if cross-user access is intentional.');
513
+ } catch (_) { /* logging must never break the query */ }
514
+ }
515
+
516
+ // scope-hardening (pfb_02e0d66c1275): the { user } no-op closer. Returns
517
+ // { table, ownerCol } when the query touches a table that is owner-shaped
518
+ // but has no declared scope (so { user } would do nothing), else null.
519
+ function __sw_checkUnscopedOwner(sql) {
520
+ const names = Object.keys(UNSCOPED_OWNER);
521
+ if (names.length === 0) return null;
522
+ const touched = __sw_extractTables(sql);
523
+ for (const table of touched) {
524
+ const ownerCol = UNSCOPED_OWNER[table];
525
+ if (ownerCol) return { table: table, ownerCol: ownerCol };
526
+ }
527
+ return null;
528
+ }
529
+
530
+ function __sw_unscopedOwnerInGrace(table) {
531
+ const until = UNSCOPED_OWNER_GRACE[table];
532
+ return typeof until === 'number' && Date.now() < until;
533
+ }
534
+
535
+ function __sw_unscopedOwnerWarn(u, api) {
536
+ try {
537
+ const until = UNSCOPED_OWNER_GRACE[u.table];
538
+ const when = (typeof until === 'number') ? new Date(until).toISOString() : 'the next deploy';
539
+ console.error('[scope-grace] ' + api + ' was passed { user } on table "' + u.table +
540
+ '" which has an owner-style column "' + u.ownerCol + '" but NO declared per-user scope, ' +
541
+ 'so { user } did NOTHING and the query ran UNSCOPED (every user can see every row). ' +
542
+ 'This throws SCOPE_NOT_DECLARED after ' + when + '. Fix now: declare a scope so the ' +
543
+ 'platform filters by the current user (sw.db.scope("' + u.table + '", { owner_column: "' +
544
+ u.ownerCol + '" }) then redeploy), or mark it sw.db.scope("' + u.table +
545
+ '", { intent: "shared" }) if cross-user access is intended and pass { unscoped: true } here.');
546
+ } catch (_) { /* logging must never break the query */ }
547
+ }
548
+
549
+ function __sw_throwScopeNotDeclared(u, api) {
550
+ const err = new Error(api + ' was passed { user } on table "' + u.table +
551
+ '", which has an owner-style column "' + u.ownerCol + '" but no declared per-user scope. ' +
552
+ 'Passing { user } here does NOT scope the query — without a declared scope every user would ' +
553
+ 'see every row. Declare the scope so the platform filters by the current user: ' +
554
+ 'sw.db.scope("' + u.table + '", { owner_column: "' + u.ownerCol + '" }) then redeploy. ' +
555
+ 'If "' + u.table + '" is intentionally shared across users, mark it sw.db.scope("' + u.table +
556
+ '", { intent: "shared" }) and pass { unscoped: true } here.');
557
+ err.code = 'SCOPE_NOT_DECLARED';
558
+ err.table = u.table;
559
+ err.owner_column = u.ownerCol;
560
+ throw err;
561
+ }
562
+
366
563
  function ensureBinding() {
367
564
  // Always satisfied now — DB is either the native binding or
368
565
  // the REST facade. Kept as a no-op so the call sites below
@@ -370,6 +567,16 @@ function buildPlatformContext(env, request) {
370
567
  }
371
568
 
372
569
  function __sw_stripStringsAndComments(sql) {
570
+ // BLANKS every string-literal and comment character with a SPACE,
571
+ // one-for-one, so the returned string is the SAME LENGTH as the input.
572
+ // __sw_autoScopeRewrite computes a tail-clause anchor offset against
573
+ // the STRIPPED sql and then slices the ORIGINAL sql at that offset to
574
+ // inject the owner predicate — so the two must stay byte-aligned. The
575
+ // earlier version collapsed each literal to a single space, which
576
+ // shifted every offset after the first literal; a query like
577
+ // "... WHERE user_id = ? AND status = 'confirmed' ORDER BY ..." had the
578
+ // predicate spliced INTO the literal, producing SQL that silently
579
+ // matched no rows (pfb_20be20f5ec83 / pfb_60b8d245f609).
373
580
  let out = '', i = 0;
374
581
  const n = sql.length;
375
582
  while (i < n) {
@@ -377,20 +584,20 @@ function buildPlatformContext(env, request) {
377
584
  if (ch === "'") {
378
585
  out += ' '; i++;
379
586
  while (i < n) {
380
- if (sql[i] === "'" && sql[i + 1] === "'") { i += 2; continue; }
381
- if (sql[i] === "'") { i++; break; }
382
- i++;
587
+ if (sql[i] === "'" && sql[i + 1] === "'") { out += ' '; i += 2; continue; }
588
+ if (sql[i] === "'") { out += ' '; i++; break; }
589
+ out += ' '; i++;
383
590
  }
384
591
  continue;
385
592
  }
386
593
  if (ch === '-' && sql[i + 1] === '-') {
387
- while (i < n && sql[i] !== '\n') i++;
594
+ while (i < n && sql[i] !== '\n') { out += ' '; i++; }
388
595
  continue;
389
596
  }
390
597
  if (ch === '/' && sql[i + 1] === '*') {
391
- i += 2;
392
- while (i < n && !(sql[i] === '*' && sql[i + 1] === '/')) i++;
393
- if (i < n) i += 2;
598
+ out += ' '; i += 2;
599
+ while (i < n && !(sql[i] === '*' && sql[i + 1] === '/')) { out += ' '; i++; }
600
+ if (i < n) { out += ' '; i += 2; }
394
601
  continue;
395
602
  }
396
603
  out += ch; i++;
@@ -429,6 +636,227 @@ function buildPlatformContext(env, request) {
429
636
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
430
637
  }
431
638
 
639
+ // ─── Positional owner proof (tsk_6475aa86) ──────────────────────────
640
+ // The original single-table check (the bindRx below) was NON-POSITIONAL:
641
+ // it accepted "<owner> = $current_user" appearing ANYWHERE in the SQL,
642
+ // so an app-user could park the sentinel in the SELECT projection, an
643
+ // ORDER BY, an UPDATE SET, or inside an OR and read/overwrite every
644
+ // user's rows. There is no SQL parser inside the deployed function
645
+ // bundle (this is the byte-escaped runtime mirror of the REST checker in
646
+ // worker/src/utils/scope-enforcement.ts), so the proof is a small
647
+ // structural scan of the (length-preserving-stripped) WHERE clause:
648
+ // '<owner> = $current_user' must be an AND-reachable, top-level conjunct
649
+ // of the WHERE — descent stops at any OR exactly as the REST AST does.
650
+
651
+ // True iff the uppercase keyword 'word' sits at offset i in 'up' with a
652
+ // word boundary on both sides.
653
+ function __sw_kwAt(up, i, word) {
654
+ if (up.substr(i, word.length) !== word) return false;
655
+ const before = up[i - 1];
656
+ const after = up[i + word.length];
657
+ const isWord = function (ch) { return ch != null && /[A-Z0-9_]/.test(ch); };
658
+ return !isWord(before) && !isWord(after);
659
+ }
660
+
661
+ // Index of the ')' matching the '(' at 'open', or -1 if unbalanced.
662
+ function __sw_matchingParen(s, open) {
663
+ let depth = 0;
664
+ for (let i = open; i < s.length; i++) {
665
+ if (s[i] === '(') depth++;
666
+ else if (s[i] === ')') { depth--; if (depth === 0) return i; }
667
+ }
668
+ return -1;
669
+ }
670
+
671
+ // Remove one (or more) fully-enclosing paren pair(s): "((x))" -> "x".
672
+ function __sw_stripWrapParens(s) {
673
+ s = s.trim();
674
+ while (s.length >= 2 && s[0] === '(' && __sw_matchingParen(s, 0) === s.length - 1) {
675
+ s = s.slice(1, -1).trim();
676
+ }
677
+ return s;
678
+ }
679
+
680
+ // Split 'expr' on the depth-0, word-bounded keyword 'kw' ('AND' / 'OR').
681
+ function __sw_splitTopLevel(expr, kw) {
682
+ const up = expr.toUpperCase();
683
+ const parts = [];
684
+ let depth = 0, last = 0, i = 0;
685
+ const n = expr.length;
686
+ while (i < n) {
687
+ const ch = expr[i];
688
+ if (ch === '(') { depth++; i++; continue; }
689
+ if (ch === ')') { if (depth > 0) depth--; i++; continue; }
690
+ if (depth === 0 && __sw_kwAt(up, i, kw)) {
691
+ parts.push(expr.slice(last, i));
692
+ i += kw.length;
693
+ last = i;
694
+ continue;
695
+ }
696
+ i++;
697
+ }
698
+ parts.push(expr.slice(last));
699
+ return parts;
700
+ }
701
+
702
+ // True iff the atomic term is exactly "<owner> = $current_user" (either
703
+ // side). The owner column may be bare, table-qualified, or double-quoted.
704
+ function __sw_isOwnerEqTerm(term, ownerCol) {
705
+ const t = term.trim();
706
+ const oc = __sw_escapeRegex(ownerCol);
707
+ const col = '(?:[A-Za-z_][A-Za-z0-9_]*\\.)?"?' + oc + '"?';
708
+ const fwd = new RegExp('^' + col + '\\s*=\\s*\\$current_user$', 'i');
709
+ const rev = new RegExp('^\\$current_user\\s*=\\s*' + col + '$', 'i');
710
+ return fwd.test(t) || rev.test(t);
711
+ }
712
+
713
+ // True iff "<owner> = $current_user" is AND-reachable from the root of
714
+ // the boolean expression 'expr' (a top-level conjunct). A top-level OR
715
+ // anywhere on the path makes the constraint non-guaranteeing -> false.
716
+ function __sw_andReachesOwner(expr, ownerCol) {
717
+ expr = __sw_stripWrapParens(expr);
718
+ if (__sw_splitTopLevel(expr, 'OR').length > 1) return false;
719
+ const ands = __sw_splitTopLevel(expr, 'AND');
720
+ if (ands.length > 1) {
721
+ for (let i = 0; i < ands.length; i++) {
722
+ if (__sw_andReachesOwner(ands[i], ownerCol)) return true;
723
+ }
724
+ return false;
725
+ }
726
+ return __sw_isOwnerEqTerm(expr, ownerCol);
727
+ }
728
+
729
+ // Extract the depth-0 WHERE clause text (between WHERE and the next
730
+ // top-level tail keyword / ';' / end), or null when there is no
731
+ // top-level WHERE. Anything in a subquery WHERE is at depth>0 and is
732
+ // intentionally ignored.
733
+ function __sw_whereClauseSpan(stripped) {
734
+ const up = stripped.toUpperCase();
735
+ const n = stripped.length;
736
+ let depth = 0, start = -1, i = 0;
737
+ while (i < n) {
738
+ const ch = stripped[i];
739
+ if (ch === '(') { depth++; i++; continue; }
740
+ if (ch === ')') { if (depth > 0) depth--; i++; continue; }
741
+ if (depth === 0) {
742
+ if (start === -1) {
743
+ if (__sw_kwAt(up, i, 'WHERE')) { start = i + 5; i = start; continue; }
744
+ } else {
745
+ if (ch === ';') return stripped.slice(start, i);
746
+ if (__sw_kwAt(up, i, 'GROUP') || __sw_kwAt(up, i, 'ORDER') ||
747
+ __sw_kwAt(up, i, 'LIMIT') || __sw_kwAt(up, i, 'HAVING') ||
748
+ __sw_kwAt(up, i, 'WINDOW') || __sw_kwAt(up, i, 'RETURNING') ||
749
+ __sw_kwAt(up, i, 'OFFSET')) {
750
+ return stripped.slice(start, i);
751
+ }
752
+ }
753
+ }
754
+ i++;
755
+ }
756
+ if (start === -1) return null;
757
+ return stripped.slice(start);
758
+ }
759
+
760
+ // Prove the owner predicate constrains the WHERE clause. 'stripped' is the
761
+ // length-preserving-blanked SQL (string literals/comments already removed)
762
+ // so keyword/paren scanning is never fooled by literal contents.
763
+ function __sw_whereProvesOwner(stripped, ownerCol) {
764
+ const span = __sw_whereClauseSpan(stripped);
765
+ if (span == null || span.trim() === '') return false;
766
+ return __sw_andReachesOwner(span, ownerCol);
767
+ }
768
+
769
+ // Split 's' on depth-0 commas (parens-aware). Used to walk an INSERT
770
+ // column list and each VALUES tuple positionally.
771
+ function __sw_splitTopLevelComma(s) {
772
+ const parts = [];
773
+ let depth = 0, last = 0;
774
+ for (let i = 0; i < s.length; i++) {
775
+ const ch = s[i];
776
+ if (ch === '(') depth++;
777
+ else if (ch === ')') { if (depth > 0) depth--; }
778
+ else if (ch === ',' && depth === 0) { parts.push(s.slice(last, i)); last = i + 1; }
779
+ }
780
+ parts.push(s.slice(last));
781
+ return parts;
782
+ }
783
+
784
+ // ─── Positional INSERT owner proof (tsk_3006e8bc) ───────────────────
785
+ // The original INSERT check only asked whether the owner column APPEARED
786
+ // in the column list and the $current_user sentinel APPEARED somewhere —
787
+ // never whether the owner column's VALUE is the sentinel. An app-user
788
+ // could park the sentinel in a non-owner column and plant a row owned by
789
+ // someone else: INSERT INTO notes (user_id, body) VALUES ('VICTIM', $current_user).
790
+ // 'stripped' is the length-preserving-blanked SQL (string literals
791
+ // removed), so the sentinel survives while any literal in the owner slot
792
+ // is blanked away. Proven only when the owner column's VALUE is
793
+ // $current_user at the owner column's own index in EVERY VALUES tuple, or
794
+ // (INSERT...SELECT) in the matching SELECT projection. Fail-closed.
795
+ function __sw_insertProvesOwner(stripped, ownerCol) {
796
+ const up = stripped.toUpperCase();
797
+ function isSentinelTerm(seg) {
798
+ let s = __sw_stripWrapParens(String(seg).trim());
799
+ s = s.replace(/\s+AS\s+[A-Za-z_][A-Za-z0-9_]*$/i, '').trim();
800
+ return s.toLowerCase() === '$current_user';
801
+ }
802
+ // Locate the explicit column list: first '(' after the INTO keyword.
803
+ let intoEnd = -1;
804
+ for (let i = 0; i < stripped.length; i++) {
805
+ if (__sw_kwAt(up, i, 'INTO')) { intoEnd = i + 4; break; }
806
+ }
807
+ if (intoEnd === -1) return false;
808
+ const open = stripped.indexOf('(', intoEnd);
809
+ if (open === -1) return false;
810
+ const close = __sw_matchingParen(stripped, open);
811
+ if (close === -1) return false;
812
+ const cols = __sw_splitTopLevelComma(stripped.slice(open + 1, close)).map(function (c) {
813
+ return c.trim().replace(/^"|"$/g, '').toLowerCase();
814
+ });
815
+ const ownerIdx = cols.indexOf(ownerCol.toLowerCase());
816
+ if (ownerIdx === -1) return false;
817
+ // After the column list: VALUES (...)[, (...)]... or SELECT ...
818
+ const rest = stripped.slice(close + 1);
819
+ const restUp = rest.toUpperCase();
820
+ let depth = 0, kind = null, kidx = -1;
821
+ for (let i = 0; i < rest.length; i++) {
822
+ const ch = rest[i];
823
+ if (ch === '(') { depth++; continue; }
824
+ if (ch === ')') { if (depth > 0) depth--; continue; }
825
+ if (depth === 0) {
826
+ if (__sw_kwAt(restUp, i, 'VALUES')) { kind = 'values'; kidx = i + 6; break; }
827
+ if (__sw_kwAt(restUp, i, 'SELECT')) { kind = 'select'; kidx = i + 6; break; }
828
+ }
829
+ }
830
+ if (kind === 'values') {
831
+ let j = kidx, tuples = 0;
832
+ while (j < rest.length) {
833
+ while (j < rest.length && /[\s,]/.test(rest[j])) j++;
834
+ if (j >= rest.length || rest[j] !== '(') break; // RETURNING / end
835
+ const tClose = __sw_matchingParen(rest, j);
836
+ if (tClose === -1) return false;
837
+ const parts = __sw_splitTopLevelComma(rest.slice(j + 1, tClose));
838
+ if (ownerIdx >= parts.length) return false;
839
+ if (!isSentinelTerm(parts[ownerIdx])) return false;
840
+ tuples++;
841
+ j = tClose + 1;
842
+ }
843
+ return tuples > 0;
844
+ }
845
+ if (kind === 'select') {
846
+ let d2 = 0, projEnd = rest.length;
847
+ for (let i = kidx; i < rest.length; i++) {
848
+ const ch = rest[i];
849
+ if (ch === '(') { d2++; continue; }
850
+ if (ch === ')') { if (d2 > 0) d2--; continue; }
851
+ if (d2 === 0 && __sw_kwAt(restUp, i, 'FROM')) { projEnd = i; break; }
852
+ }
853
+ const parts = __sw_splitTopLevelComma(rest.slice(kidx, projEnd));
854
+ if (ownerIdx >= parts.length) return false;
855
+ return isSentinelTerm(parts[ownerIdx]);
856
+ }
857
+ return false;
858
+ }
859
+
432
860
  function __sw_checkScope(sql) {
433
861
  const tableNames = Object.keys(SCOPES);
434
862
  if (tableNames.length === 0) return null;
@@ -443,13 +871,19 @@ function buildPlatformContext(env, request) {
443
871
  if (!m) return { table: table, ownerCol: ownerCol, reason: 'INSERT into scoped table `' + table + '` must use the explicit-column form: INSERT INTO ' + table + ' (' + ownerCol + ', …) VALUES ($current_user, …).' };
444
872
  const cols = m[1].split(',').map(function (s) { return s.trim().replace(/^"|"$/g, '').toLowerCase(); });
445
873
  if (cols.indexOf(ownerCol.toLowerCase()) === -1) return { table: table, ownerCol: ownerCol, reason: 'INSERT into scoped table `' + table + '` must include column `' + ownerCol + '` set to $current_user.' };
446
- if (!/\$current_user\b/.test(sql)) return { table: table, ownerCol: ownerCol, reason: 'INSERT into scoped table `' + table + '` must bind `' + ownerCol + '` to $current_user.' };
874
+ // POSITIONAL proof (tsk_3006e8bc): the owner column's VALUE must be
875
+ // $current_user at the owner column's own index — in every VALUES
876
+ // row, or the matching SELECT projection for INSERT...SELECT. A
877
+ // sentinel parked in a different column does not scope the row.
878
+ if (!__sw_insertProvesOwner(stripped, ownerCol)) return { table: table, ownerCol: ownerCol, reason: 'INSERT into scoped table `' + table + '` must bind `' + ownerCol + '` to $current_user at its own position in every VALUES row (or the matching SELECT column for INSERT…SELECT). A $current_user value in a different column does not scope the row to the current user.' };
447
879
  continue;
448
880
  }
449
881
  if (kind === 'select' || kind === 'update' || kind === 'delete') {
450
882
  if (!/\bWHERE\b/i.test(stripped)) return { table: table, ownerCol: ownerCol, reason: kind.toUpperCase() + ' on scoped table `' + table + '` must have a WHERE clause that constrains `' + ownerCol + '` to $current_user.' };
451
- const bindRx = new RegExp('(?:\\b|\\.|")' + __sw_escapeRegex(ownerCol) + '"?\\s*=\\s*\\$current_user\\b', 'i');
452
- if (!bindRx.test(stripped)) return { table: table, ownerCol: ownerCol, reason: kind.toUpperCase() + ' on scoped table `' + table + '` must constrain `' + ownerCol + '` with: ' + ownerCol + ' = $current_user.' };
883
+ // POSITIONAL proof (tsk_6475aa86): the owner predicate must be a
884
+ // top-level AND condition of the WHERE clause, not merely present
885
+ // somewhere in the SQL (projection / ORDER BY / SET / OR).
886
+ if (!__sw_whereProvesOwner(stripped, ownerCol)) return { table: table, ownerCol: ownerCol, reason: kind.toUpperCase() + ' on scoped table `' + table + '` must constrain `' + ownerCol + '` to the current user with `' + ownerCol + ' = $current_user` as a top-level condition in the WHERE clause - AND-ed (not inside an OR), and not only in the SELECT list, ORDER BY, or an UPDATE SET.' };
453
887
  continue;
454
888
  }
455
889
  return { table: table, ownerCol: ownerCol, reason: 'Statement type not recognized for scope enforcement on `' + table + '`. Use SELECT, INSERT, UPDATE, or DELETE.' };
@@ -583,6 +1017,14 @@ function buildPlatformContext(env, request) {
583
1017
  err.code = 'SCOPE_VIOLATION';
584
1018
  err.table = v.table;
585
1019
  err.owner_column = v.ownerCol;
1020
+ // A scope violation is a developer-fixable 4xx, not a platform bug
1021
+ // (tsk_81271d84). Mark it exposed so the function-bundle catch-all
1022
+ // surfaces a clean 403 SCOPE_VIOLATION with the owner-binding message,
1023
+ // instead of swallowing it into an opaque 500 FUNCTION_ERROR. The
1024
+ // message names the table + owner column the developer must bind —
1025
+ // product/SDK terms only, no infra leak.
1026
+ err.status = 403;
1027
+ err.__sw_expose = true;
586
1028
  throw err;
587
1029
  }
588
1030
 
@@ -763,18 +1205,50 @@ function buildPlatformContext(env, request) {
763
1205
  // the actual fix is db_migrate. Logs and dashboard Logs tab
764
1206
  // surface both tags ([SW_SLOW_QUERY] / [SW_SCHEMA_DRIFT]).
765
1207
  const SW_SLOW_QUERY_MS = 100;
1208
+ // Strip engine/infra labels from a raw database error so the developer
1209
+ // sees the actionable cause ("UNIQUE constraint failed: users.email")
1210
+ // without the provider noise ("D1_ERROR", "SQLITE_CONSTRAINT_UNIQUE").
1211
+ // No D1/SQLite/Cloudflare term survives (rule 8 — product language only).
1212
+ function __sw_productizeDbMessage(msg) {
1213
+ return String(msg)
1214
+ .replace(/D1_ERROR:?/gi, '')
1215
+ .replace(/SQLITE_[A-Z_]+/gi, '')
1216
+ .replace(/\bSQLite\b/gi, 'database')
1217
+ .replace(/\bD1\b/gi, 'database')
1218
+ .replace(/\bCloudflare\b/gi, '')
1219
+ .replace(/\s{2,}/g, ' ')
1220
+ .replace(/^[\s:;.,]+|[\s:;.,]+$/g, '')
1221
+ .trim();
1222
+ }
766
1223
  function __sw_decorateError(label, sqlPreview, err) {
767
1224
  const msg = (err && err.message) || String(err);
1225
+ const snippet = String(sqlPreview).replace(/\s+/g, ' ').slice(0, 200);
768
1226
  const m = msg.match(/no such (?:column|table)[^A-Za-z0-9_]*([A-Za-z0-9_.]*)/i);
769
1227
  if (m) {
770
1228
  const ident = m[1] || 'unknown';
771
- const snippet = String(sqlPreview).replace(/\s+/g, ' ').slice(0, 200);
772
1229
  console.warn('[SW_SCHEMA_DRIFT] ' + label + ' — ' + ident + ' missing — ' + snippet);
773
1230
  const better = new Error('Schema drift: ' + msg + '. Your code references "' + ident + '" but the database schema does not. Did you forget to run db_migrate before deploying?');
774
1231
  better.code = 'SCHEMA_DRIFT';
775
1232
  better.original = err;
776
1233
  return better;
777
1234
  }
1235
+ // Any OTHER database error: the native binding / provider passthrough
1236
+ // surfaces raw strings like "D1_ERROR: ... : SQLITE_CONSTRAINT" that
1237
+ // leak the engine and read as noise. Rewrite to product language, but
1238
+ // FAIL LOUD: log the real cause server-side (Logs tab) and keep
1239
+ // .original — never swallow it.
1240
+ if (/D1_ERROR|SQLITE/i.test(msg)) {
1241
+ console.error('[SW_DB_ERROR] ' + label + ' — ' + msg + ' — ' + snippet);
1242
+ const detail = __sw_productizeDbMessage(msg);
1243
+ const wrapped = new Error(
1244
+ (detail ? 'Your database query could not be completed: ' + detail + '.'
1245
+ : 'Your database query could not be completed.') +
1246
+ ' Check the query and your table and column names against your schema; the full database error is in your project Logs.'
1247
+ );
1248
+ wrapped.code = 'DB_QUERY_ERROR';
1249
+ wrapped.original = err;
1250
+ return wrapped;
1251
+ }
778
1252
  return err;
779
1253
  }
780
1254
  // Retry-on-backpressure (founder 2026-06-08): a write that gets
@@ -1150,6 +1624,57 @@ function buildPlatformContext(env, request) {
1150
1624
  }
1151
1625
  }
1152
1626
 
1627
+ // sw.db.scope(table, opts) — declare a table as user-scoped IN-BAND,
1628
+ // so a deployed function / run_code setup script can arm per-user row
1629
+ // scoping without minting a developer key and curl-ing /v1/db/scopes
1630
+ // out of band (the gooddog gap). Routes through the SAME POST
1631
+ // /v1/db/scopes the dashboard + MCP use, with the same owner authority:
1632
+ // the runtime key is the project owner's, pinned to THIS project by the
1633
+ // auth middleware, so a scope can only ever be declared on this project.
1634
+ //
1635
+ // Declaring a scope TIGHTENS access (it arms owner-column enforcement on
1636
+ // app-user queries); it never widens a query result set. Scopes are
1637
+ // baked into the bundle at DEPLOY time (PROJECT_SCOPES), so a scope
1638
+ // declared at runtime takes effect for app-user queries on the NEXT
1639
+ // deploy — this records the declaration; it does NOT hot-swap the
1640
+ // running bundle's SCOPES map. Unlike sw.db.query, failures throw.
1641
+ //
1642
+ // await sw.db.scope('bookings', { owner_column: 'user_id' })
1643
+ // await sw.db.scope('audit', { intent: 'shared' }) // intentionally cross-user
1644
+ // await sw.db.scope.list() // all declared scopes
1645
+ // await sw.db.scope.get('bookings') // one scope, or null
1646
+ async function __sw_scopeDeclare(table, opts) {
1647
+ if (typeof table !== 'string' || !table) {
1648
+ const err = new Error('sw.db.scope(table, opts): table must be a non-empty string.');
1649
+ err.code = 'VALIDATION_ERROR';
1650
+ throw err;
1651
+ }
1652
+ opts = opts || {};
1653
+ const body = { project_id: projectId, table: table };
1654
+ if (opts.owner_column != null) body.owner_column = opts.owner_column;
1655
+ if (opts.sensitive_columns != null) body.sensitive_columns = opts.sensitive_columns;
1656
+ if (opts.intent != null) body.intent = opts.intent;
1657
+ return platformJSON('/v1/db/scopes', { method: 'POST', body: JSON.stringify(body) });
1658
+ }
1659
+ __sw_scopeDeclare.list = async function () {
1660
+ const d = await platformJSON('/v1/db/scopes?project_id=' + encodeURIComponent(projectId));
1661
+ return (d && d.scopes) || [];
1662
+ };
1663
+ __sw_scopeDeclare.get = async function (table) {
1664
+ if (typeof table !== 'string' || !table) {
1665
+ const err = new Error('sw.db.scope.get(table): table must be a non-empty string.');
1666
+ err.code = 'VALIDATION_ERROR';
1667
+ throw err;
1668
+ }
1669
+ const lc = table.toLowerCase();
1670
+ const d = await platformJSON('/v1/db/scopes?project_id=' + encodeURIComponent(projectId));
1671
+ const scopes = (d && d.scopes) || [];
1672
+ for (let i = 0; i < scopes.length; i++) {
1673
+ if (scopes[i] && String(scopes[i].table).toLowerCase() === lc) return scopes[i];
1674
+ }
1675
+ return null;
1676
+ };
1677
+
1153
1678
  return {
1154
1679
  async query(sql, params, options) {
1155
1680
  ensureBinding();
@@ -1190,11 +1715,28 @@ function buildPlatformContext(env, request) {
1190
1715
  changes: (rr.meta && rr.meta.changes) || 0,
1191
1716
  };
1192
1717
  }
1718
+ if (!__sw_scopeInGrace(v.table)) {
1719
+ __sw_throwViolation(v,
1720
+ 'Pass { user } and we will scope your query automatically, but only for single-table SELECT/UPDATE/DELETE/INSERT. This query has a JOIN, UNION, or subquery — use sw.db.scoped(user.id) to write the scoped form explicitly, or { unscoped: true } if cross-user is intentional.');
1721
+ }
1722
+ __sw_scopeGraceWarn(v, 'sw.db.query');
1723
+ } else if (__sw_scopeInGrace(v.table)) {
1724
+ // Grace window: warn loudly, run the query as written.
1725
+ __sw_scopeGraceWarn(v, 'sw.db.query');
1726
+ } else {
1193
1727
  __sw_throwViolation(v,
1194
- 'Pass { user } and we will scope your query automatically, but only for single-table SELECT/UPDATE/DELETE/INSERT. This query has a JOIN, UNION, or subquery — use sw.db.scoped(user.id) to write the scoped form explicitly, or { unscoped: true } if cross-user is intentional.');
1728
+ 'Pass { user } as the third arg sw.db.query(sql, params, { user: sw.auth.fromRequest(req).user }) or { unscoped: true } if cross-user is intentional. The verbose form sw.db.scoped(user.id).query(...) still works.');
1729
+ }
1730
+ } else if (userId) {
1731
+ // No scoped-table violation, but { user } was passed. If the
1732
+ // query touches an owner-shaped table with NO declared scope,
1733
+ // { user } would silently no-op (pfb_02e0d66c1275) — warn during
1734
+ // grace, throw SCOPE_NOT_DECLARED after. Never a silent leak.
1735
+ const u = __sw_checkUnscopedOwner(sql);
1736
+ if (u) {
1737
+ if (__sw_unscopedOwnerInGrace(u.table)) __sw_unscopedOwnerWarn(u, 'sw.db.query');
1738
+ else __sw_throwScopeNotDeclared(u, 'sw.db.query');
1195
1739
  }
1196
- __sw_throwViolation(v,
1197
- 'Pass { user } as the third arg — sw.db.query(sql, params, { user: sw.auth.fromRequest(req).user }) — or { unscoped: true } if cross-user is intentional. The verbose form sw.db.scoped(user.id).query(...) still works.');
1198
1740
  }
1199
1741
  }
1200
1742
  const r = await __sw_timedExec('sw.db.query', sql, function () { return prep(sql, params).all(); });
@@ -1229,8 +1771,13 @@ function buildPlatformContext(env, request) {
1229
1771
  for (const s of statements) {
1230
1772
  const v = __sw_checkScope(s.sql);
1231
1773
  if (v && !userId) {
1232
- __sw_throwViolation(v,
1233
- 'Pass { user } as the second arg to sw.db.batch, or { unscoped: true } if cross-user is intentional. The verbose form sw.db.scoped(user.id).batch(...) still works.');
1774
+ if (__sw_scopeInGrace(v.table)) {
1775
+ // Grace window: warn loudly, let the batch run as written.
1776
+ __sw_scopeGraceWarn(v, 'sw.db.batch');
1777
+ } else {
1778
+ __sw_throwViolation(v,
1779
+ 'Pass { user } as the second arg to sw.db.batch, or { unscoped: true } if cross-user is intentional. The verbose form sw.db.scoped(user.id).batch(...) still works.');
1780
+ }
1234
1781
  }
1235
1782
  }
1236
1783
  }
@@ -1243,8 +1790,20 @@ function buildPlatformContext(env, request) {
1243
1790
  const subst = __sw_substCurrentUser(rewritten, s.params, userId);
1244
1791
  return prep(subst.sql, subst.params);
1245
1792
  }
1246
- __sw_throwViolation(v,
1247
- 'Auto-scope only handles single-table SELECT/UPDATE/DELETE/INSERT. Use sw.db.scoped(user.id).batch(...) for JOIN/UNION/subquery shapes.');
1793
+ if (!__sw_scopeInGrace(v.table)) {
1794
+ __sw_throwViolation(v,
1795
+ 'Auto-scope only handles single-table SELECT/UPDATE/DELETE/INSERT. Use sw.db.scoped(user.id).batch(...) for JOIN/UNION/subquery shapes.');
1796
+ }
1797
+ __sw_scopeGraceWarn(v, 'sw.db.batch');
1798
+ } else {
1799
+ // { user } against an owner-shaped table with no declared scope
1800
+ // would silently no-op (pfb_02e0d66c1275) — warn during grace,
1801
+ // throw SCOPE_NOT_DECLARED after.
1802
+ const u = __sw_checkUnscopedOwner(s.sql);
1803
+ if (u) {
1804
+ if (__sw_unscopedOwnerInGrace(u.table)) __sw_unscopedOwnerWarn(u, 'sw.db.batch');
1805
+ else __sw_throwScopeNotDeclared(u, 'sw.db.batch');
1806
+ }
1248
1807
  }
1249
1808
  }
1250
1809
  return prep(s.sql, s.params);
@@ -1291,6 +1850,7 @@ function buildPlatformContext(env, request) {
1291
1850
  return (r.results || []).map((row) => row.name);
1292
1851
  },
1293
1852
  scoped: scopedClient,
1853
+ scope: __sw_scopeDeclare,
1294
1854
  async dump() {
1295
1855
  // sw.db.dump is developer-only and is NOT callable from a
1296
1856
  // deployed function. A full-database export must run with
@@ -1373,11 +1933,26 @@ function buildPlatformContext(env, request) {
1373
1933
  // fs_write path worked, the in-function shim silently dropped it).
1374
1934
  const headers = { 'Content-Type': ct };
1375
1935
  if (opts.visibility === 'public' || opts.public === true) headers['X-Visibility'] = 'public';
1936
+ // Opt-in conditional write: { if_match: <version> } pins the version
1937
+ // this writer last read (write/stat/versions all return it). If the
1938
+ // file changed since, the platform refuses with FS_VERSION_CONFLICT
1939
+ // instead of silently last-write-wins. Omit for today's behavior.
1940
+ const ifMatch = opts.ifMatch ?? opts.if_match;
1941
+ if (ifMatch !== undefined && ifMatch !== null) headers['If-Match'] = String(ifMatch);
1376
1942
  const r = await platformFetch('/v1/fs/' + projectId + path, {
1377
1943
  method: 'PUT',
1378
1944
  headers,
1379
1945
  body,
1380
1946
  });
1947
+ if (r.status === 412) {
1948
+ const conflict = await r.json().catch(() => null);
1949
+ const err = new Error('FS_VERSION_CONFLICT: ' + (conflict && conflict.message
1950
+ ? conflict.message
1951
+ : 'the file changed since it was read — re-read it and retry with the current version'));
1952
+ err.code = 'FS_VERSION_CONFLICT';
1953
+ if (conflict && conflict.data) err.current_version = conflict.data.current_version;
1954
+ throw err;
1955
+ }
1381
1956
  if (!r.ok) throw new Error('fs.write failed: ' + r.status);
1382
1957
  return r.json();
1383
1958
  },
@@ -2481,14 +3056,20 @@ function buildPlatformContext(env, request) {
2481
3056
  // The cookies ride out on the Response via the shim, so the handler
2482
3057
  // returns a plain user/Response and does zero header work.
2483
3058
  async loginWithCookie(req, email, password) {
2484
- const d = await this.login({ email: email, password: password });
3059
+ const creds = __sw_cookieCreds('loginWithCookie', req, email, password);
3060
+ let d;
3061
+ try { d = await this.login(creds); }
3062
+ catch (err) { throw __sw_markExpected(err); }
2485
3063
  const access = d && (d.token || d.access_token);
2486
3064
  const refresh = d && d.refresh_token;
2487
3065
  if (access && refresh) __sw_setAuthCookies(access, refresh);
2488
3066
  return (d && d.user) || null;
2489
3067
  },
2490
3068
  async signupWithCookie(req, email, password) {
2491
- const d = await this.signup({ email: email, password: password });
3069
+ const creds = __sw_cookieCreds('signupWithCookie', req, email, password);
3070
+ let d;
3071
+ try { d = await this.signup(creds); }
3072
+ catch (err) { throw __sw_markExpected(err); }
2492
3073
  const access = d && (d.token || d.access_token);
2493
3074
  const refresh = d && d.refresh_token;
2494
3075
  if (access && refresh) __sw_setAuthCookies(access, refresh);
@@ -2499,13 +3080,34 @@ function buildPlatformContext(env, request) {
2499
3080
  async googleCallbackWithCookie(req, redirectTo) {
2500
3081
  const u = new URL(req.url);
2501
3082
  const code = u.searchParams.get('code');
2502
- if (!code) { const e = new Error('Missing ?code on the OAuth callback.'); e.code = 'VALIDATION_ERROR'; throw e; }
2503
- const d = await this.googleExchange({ code: code, redirect_uri: u.origin + u.pathname });
3083
+ if (!code) { const e = new Error('Missing ?code on the OAuth callback.'); e.code = 'VALIDATION_ERROR'; e.status = 400; e.__sw_expose = true; throw e; }
3084
+ let d;
3085
+ try { d = await this.googleExchange({ code: code, redirect_uri: u.origin + u.pathname }); }
3086
+ catch (err) { throw __sw_markExpected(err); }
2504
3087
  const access = d && (d.token || d.access_token);
2505
3088
  const refresh = d && d.refresh_token;
2506
3089
  if (access && refresh) __sw_setAuthCookies(access, refresh);
2507
3090
  return new Response(null, { status: 302, headers: { Location: redirectTo || '/' } });
2508
3091
  },
3092
+ // Mint the httpOnly session cookies from an existing token pair —
3093
+ // the primitive the *WithCookie helpers wrap (tsk_1dd4e1b4). For
3094
+ // backends (e.g. @somewhere-tech/auth/server) that already called
3095
+ // login / googleExchange / verifyOtp and want THAT session as
3096
+ // cookies without a second platform call.
3097
+ setSessionCookies(access, refresh) {
3098
+ if (typeof access !== 'string' || !access || typeof refresh !== 'string' || !refresh) {
3099
+ const err = new Error('sw.auth.setSessionCookies: access and refresh must be non-empty strings');
3100
+ err.code = 'VALIDATION_ERROR';
3101
+ throw err;
3102
+ }
3103
+ __sw_setAuthCookies(access, refresh);
3104
+ },
3105
+ // Park the Set-Cookie expirations that end a cookie session (the
3106
+ // clear half of setSessionCookies). logoutWithCookie does this AND
3107
+ // revokes server-side — prefer it when you have the Request.
3108
+ clearSessionCookies() {
3109
+ __sw_clearAuthCookies();
3110
+ },
2509
3111
  // Revokes the session server-side and clears the cookies.
2510
3112
  async logoutWithCookie(req) {
2511
3113
  const ch = (req && req.headers && (req.headers.get('cookie') || req.headers.get('Cookie'))) || '';
@@ -2729,6 +3331,16 @@ function buildPlatformContext(env, request) {
2729
3331
  }
2730
3332
  if (!token) return null;
2731
3333
 
3334
+ // Cross-origin guard (tsk_1dd4e1b4): a cookie-authed request from a
3335
+ // foreign origin is treated as unauthenticated (CSRF on writes,
3336
+ // credentialed cross-origin reads on GET). Loud log so blocked
3337
+ // attempts show in the function logs, never silently.
3338
+ if (fromCookie && __sw_cookieCsrfBlocked(req)) {
3339
+ console.error('[CSRF] blocked cookie-authed ' + req.method + ' ' + (req.url || '') +
3340
+ ' — cross-origin source does not match this app. Browser calls must be same-origin (or use token auth).');
3341
+ return null;
3342
+ }
3343
+
2732
3344
  let refreshToken = null;
2733
3345
  const optOut = req.headers.get('X-No-Auto-Refresh') || req.headers.get('x-no-auto-refresh');
2734
3346
  if (optOut !== '1') {
@@ -2942,6 +3554,39 @@ function buildPlatformContext(env, request) {
2942
3554
  info(msg, data) { return this._send('info', msg, data); },
2943
3555
  warn(msg, data) { return this._send('warn', msg, data); },
2944
3556
  error(msg, data) { return this._send('error', msg, data); },
3557
+ // sw.logs.tail({ limit?, level?, since?, source?, search? }) — read this
3558
+ // project's OWN recent logs back, in one call, so a script can run an
3559
+ // action then read the logs it produced without a second round-trip
3560
+ // through the dashboard. Reads the same owner-scoped app_logs the
3561
+ // GET /v1/logs read path returns; the runtime key is pinned to this
3562
+ // project, so it can only ever see this project's logs. Read-only —
3563
+ // unlike _send (fire-and-forget), failures throw so the caller sees them.
3564
+ //
3565
+ // Read consistency: log writes (sw.logs.* and platform internalLog) are
3566
+ // ingested through a durable queue that batch-inserts, so a line you just
3567
+ // wrote can lag a short window before tail() returns it (pfb
3568
+ // F-LOG-WRITE-READ-GAP). tail() reports only committed rows — it never
3569
+ // invents or waits on an un-ingested write. Poll briefly if you need
3570
+ // read-your-write.
3571
+ //
3572
+ // since: epoch-ms number OR ISO-8601 string — returns logs at/after it.
3573
+ // level: 'debug' | 'info' | 'warn' | 'error'. source: filter by source.
3574
+ // limit: default 50, capped at 500 by the read path.
3575
+ // Returns an array of { id, level, message, data, source, created_at }
3576
+ // (newest first).
3577
+ async tail(opts) {
3578
+ opts = opts || {};
3579
+ const qs = new URLSearchParams({ project_id: projectId });
3580
+ if (opts.limit != null) qs.set('limit', String(opts.limit));
3581
+ if (opts.level) qs.set('level', String(opts.level));
3582
+ if (opts.source) qs.set('source', String(opts.source));
3583
+ if (opts.search) qs.set('search', String(opts.search));
3584
+ if (opts.since != null) {
3585
+ qs.set('after', typeof opts.since === 'number' ? String(opts.since) : String(opts.since));
3586
+ }
3587
+ const d = await platformJSON('/v1/logs?' + qs.toString());
3588
+ return (d && d.logs) || [];
3589
+ },
2945
3590
  },
2946
3591
 
2947
3592
  realtime: {
@@ -1,4 +1,4 @@
1
- // VENDORED from worker/src/utils/function-bundle.ts (SW_INIT_JS) @ 58927b3
1
+ // VENDORED from worker/src/utils/function-bundle.ts (SW_INIT_JS) @ e8da46e
2
2
  // — the exact runtime deployed functions run against. Do not edit by hand;
3
3
  // re-sync with: node scripts/extract-runtime.mjs <monorepo>
4
4
  // Generated by somewhere.tech deploy pipeline. Sets up the global