@somewhere-tech/cli 0.29.1 → 0.30.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.
- package/dist/commands/auth.js +50 -4
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/browser.js +115 -16
- package/dist/commands/browser.js.map +1 -1
- package/dist/commands/check.js +7 -0
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/deploy.js +82 -6
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.js +64 -7
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docs.js +139 -7
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/errors.js +29 -6
- package/dist/commands/errors.js.map +1 -1
- package/dist/commands/status.js +89 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/lib/client.js +59 -4
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/config.js +18 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/files.js +57 -6
- package/dist/lib/files.js.map +1 -1
- package/dist/lib/output.js +4 -2
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/publish-surface.js +94 -0
- package/dist/lib/publish-surface.js.map +1 -0
- package/dist/local/browser-run.js +321 -0
- package/dist/local/browser-run.js.map +1 -0
- package/dist/local/chrome.js +320 -0
- package/dist/local/chrome.js.map +1 -0
- package/dist/local/compiler.js +67 -12
- package/dist/local/compiler.js.map +1 -1
- package/dist/local/runtime.js +72 -2
- package/dist/local/runtime.js.map +1 -1
- package/dist/local/telemetry-filter.js +120 -0
- package/dist/local/telemetry-filter.js.map +1 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
- package/runtime/VENDOR.json +4 -3
- package/runtime/browser-probes.mjs +6 -0
- package/runtime/compiler/VENDOR.json +40 -3
- package/runtime/compiler/compile-core.cjs +18 -0
- package/runtime/compiler/typed-functions.cjs +31 -1
- package/runtime/platform-context.mjs +241 -23
- package/runtime/sw-init.mjs +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// VENDORED from worker/src/runtime/context.ts (PLATFORM_CONTEXT_JS) @
|
|
1
|
+
// VENDORED from worker/src/runtime/context.ts (PLATFORM_CONTEXT_JS) @ 1bf981dd
|
|
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
|
// ── Primordial freeze (tsk_327fe8a4, opinionated-not-greenfield) ───────────
|
|
@@ -128,9 +128,13 @@ function __sw_initTrace(request) {
|
|
|
128
128
|
traceId: inbound ? inbound.traceId : __sw_randHex(16),
|
|
129
129
|
spanId: __sw_randHex(8),
|
|
130
130
|
parentSpanId: inbound ? inbound.parentSpanId : null,
|
|
131
|
-
// A function has no sampling policy of its own
|
|
132
|
-
//
|
|
133
|
-
//
|
|
131
|
+
// A function has no sampling policy of its own. An inbound decision to
|
|
132
|
+
// DROP is honored; otherwise this is "no opinion", not "store this" --
|
|
133
|
+
// the platform applies TRACE_SAMPLE_RATE deterministically on the trace
|
|
134
|
+
// id, both on its own hops and when these spans are posted to
|
|
135
|
+
// /v1/traces, so the whole chain is kept or dropped together
|
|
136
|
+
// (tsk_07a7465a). Before that this true was load-bearing and exempted
|
|
137
|
+
// every runtime-originated request from sampling entirely.
|
|
134
138
|
sampled: inbound ? inbound.sampled : true,
|
|
135
139
|
tracestate: __sw_traceHeader(request, 'tracestate', 512),
|
|
136
140
|
baggage: __sw_traceHeader(request, 'baggage', 8192),
|
|
@@ -1069,6 +1073,24 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
1069
1073
|
err.__sw_expose = true;
|
|
1070
1074
|
return err;
|
|
1071
1075
|
}
|
|
1076
|
+
// A PLAN answer, not a connection failure. The platform refuses the
|
|
1077
|
+
// local loop's database calls on a plan without the local-dev database
|
|
1078
|
+
// entitlement, and its message already names the plans, this account's
|
|
1079
|
+
// plan, and that deploying is unaffected. Surfacing it verbatim is the
|
|
1080
|
+
// whole fix: this used to fall through to connectionNotReady below,
|
|
1081
|
+
// whose copy tells the developer to redeploy the function — a
|
|
1082
|
+
// remediation that has never changed this answer (tsk_4df056ea).
|
|
1083
|
+
function planGateError(body) {
|
|
1084
|
+
const code = body && (body.error || body.code);
|
|
1085
|
+
if (code !== 'LOCAL_DEV_DB_NOT_ENABLED' && code !== 'CLOUD_DEV_NOT_ENABLED') return null;
|
|
1086
|
+
const err = new Error((body && body.message) ||
|
|
1087
|
+
'This plan does not include reaching the project database from `somewhere dev`. `somewhere deploy` publishes on every plan.');
|
|
1088
|
+
err.code = code;
|
|
1089
|
+
err.status = 403;
|
|
1090
|
+
err.retryable = false;
|
|
1091
|
+
err.__sw_expose = true;
|
|
1092
|
+
return err;
|
|
1093
|
+
}
|
|
1072
1094
|
function connectionNotReady(operation, response, body) {
|
|
1073
1095
|
const upstreamCode = body && (body.error || body.code);
|
|
1074
1096
|
if (response.status !== 401 && response.status !== 403) return null;
|
|
@@ -1095,6 +1117,8 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
1095
1117
|
try { j = txt ? JSON.parse(txt) : null; } catch (_) { j = null; }
|
|
1096
1118
|
const managedErr = managedCapabilityError(j);
|
|
1097
1119
|
if (managedErr) throw managedErr;
|
|
1120
|
+
const planErr = planGateError(j);
|
|
1121
|
+
if (planErr) throw planErr;
|
|
1098
1122
|
const responseCode = j && (j.error || j.code);
|
|
1099
1123
|
const isolationCode = draftExecution && responseCode === 'DRAFT_DB_NOT_ISOLATED'
|
|
1100
1124
|
? 'DRAFT_DB_NOT_ISOLATED'
|
|
@@ -1181,6 +1205,8 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
1181
1205
|
if (!r.ok || !j || j.ok === false) {
|
|
1182
1206
|
const managedErr = managedCapabilityError(j);
|
|
1183
1207
|
if (managedErr) throw managedErr;
|
|
1208
|
+
const planErr = planGateError(j);
|
|
1209
|
+
if (planErr) throw planErr;
|
|
1184
1210
|
const connectionErr = connectionNotReady('batch', r, j);
|
|
1185
1211
|
if (connectionErr) throw connectionErr;
|
|
1186
1212
|
const sizeErr = responseSizeError(r, j, 'The database rejected this batch because an input exceeded a size limit.');
|
|
@@ -2728,6 +2754,20 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
2728
2754
|
}
|
|
2729
2755
|
return v; // unknown declared type: fail open on typing, never on transport
|
|
2730
2756
|
}
|
|
2757
|
+
// Refuse BEFORE the write a whole number the database cannot store as one
|
|
2758
|
+
// (tsk_daf4d6d2). Whole numbers are kept as 64-bit values; anything past
|
|
2759
|
+
// that range is stored as an approximate decimal and can never be read
|
|
2760
|
+
// back exactly, so it must be refused up front rather than discovered
|
|
2761
|
+
// after the row has already landed. A column declared as a decimal
|
|
2762
|
+
// ('number') is exempt: large decimals are legitimate there.
|
|
2763
|
+
function __sw_structAssertStorableWholeNumber(verb, table, column, v, col) {
|
|
2764
|
+
if (typeof v !== 'number' || !Number.isInteger(v)) return;
|
|
2765
|
+
if (col && col.t === 'number') return;
|
|
2766
|
+
if (v < 9223372036854775808 && v >= -9223372036854775808) return;
|
|
2767
|
+
__sw_structThrowSchema('DATABASE_VALUE_TOO_LARGE',
|
|
2768
|
+
'sw.db.' + verb + ' on "' + table + '": the value for "' + column + '" is outside the range of whole numbers this database stores ' +
|
|
2769
|
+
'(-9223372036854775808 to 9223372036854775807). Keep the value inside that range, or store it as text.');
|
|
2770
|
+
}
|
|
2731
2771
|
function __sw_structAssertKnownColumn(verb, table, shape, ownerColumn, name, where) {
|
|
2732
2772
|
if (ownerColumn && __sw_lower(name) === __sw_lower(ownerColumn)) return;
|
|
2733
2773
|
if (__sw_structFindDeclaredColumn(shape, name)) return;
|
|
@@ -2749,6 +2789,7 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
2749
2789
|
if (v !== null && typeof v === 'object') {
|
|
2750
2790
|
__sw_structThrow('sw.db.' + verb + ' value for "' + ir.values[i].column + '": values must be a string, number, boolean, or null (got object).');
|
|
2751
2791
|
}
|
|
2792
|
+
__sw_structAssertStorableWholeNumber(verb, table, ir.values[i].column, v, null);
|
|
2752
2793
|
}
|
|
2753
2794
|
return;
|
|
2754
2795
|
}
|
|
@@ -2761,6 +2802,7 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
2761
2802
|
for (let i = 0; i < ir.values.length; i++) {
|
|
2762
2803
|
__sw_structAssertKnownColumn(verb, table, shape, null, ir.values[i].column, verb === 'update' ? 'set' : 'values');
|
|
2763
2804
|
const col = __sw_structFindDeclaredColumn(shape, ir.values[i].column);
|
|
2805
|
+
__sw_structAssertStorableWholeNumber(verb, table, ir.values[i].column, ir.values[i].value, col);
|
|
2764
2806
|
ir.values[i].value = __sw_structCheckWriteValue(verb, table, col, ir.values[i].value);
|
|
2765
2807
|
}
|
|
2766
2808
|
}
|
|
@@ -2902,8 +2944,19 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
2902
2944
|
__sw_structThrow403('TABLE_INTENT_INVALID',
|
|
2903
2945
|
'sw.db.' + verb + ' on "' + table + '": the table is declared user-owned but its ownership column is missing. Repair the scope declaration (sw.db.scope / db_scope_set), then redeploy.');
|
|
2904
2946
|
}
|
|
2947
|
+
// Name the file AND the step that applies it (tsk_883e3ede). The old
|
|
2948
|
+
// message pointed only at sw.db.scope / db_scope_set, so a developer
|
|
2949
|
+
// who had already declared the table in db/schema.ts and was running
|
|
2950
|
+
// local dev read it as "your schema file does not count" and
|
|
2951
|
+
// abandoned the managed API for raw SQL. db/schema.ts is applied on a
|
|
2952
|
+
// production deploy only — never from local dev, a preview, or a dry
|
|
2953
|
+
// run — so "declared but not yet deployed" is the single most likely
|
|
2954
|
+
// reason to be reading this, and the message has to say so.
|
|
2905
2955
|
__sw_structThrow403('TABLE_INTENT_REQUIRED',
|
|
2906
|
-
'sw.db.' + verb + ' on "' + table + '": this table has no declared intent, so the platform cannot prove how to access it safely.
|
|
2956
|
+
'sw.db.' + verb + ' on "' + table + '": this table has no declared intent, so the platform cannot prove how to access it safely. ' +
|
|
2957
|
+
'Declare it in db/schema.ts — table({ ... }, { scope: owner() }) for per-user rows, shared() for intentional cross-user access, serverOnly() for trusted server access — then run `somewhere deploy`, which is the step that applies db/schema.ts. ' +
|
|
2958
|
+
'Already declared it there? It needs that one production deploy: local dev, previews and dry runs do not apply the schema file, so the table still reads as undeclared here. ' +
|
|
2959
|
+
'Not using a schema file? sw.db.scope / db_scope_set declares a table immediately. For manual access, use raw sw.db.query(sql, params).');
|
|
2907
2960
|
}
|
|
2908
2961
|
if (asServer === true) {
|
|
2909
2962
|
// Trusted server code reading cross-user, explicitly. No principal
|
|
@@ -2986,12 +3039,83 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
2986
3039
|
}
|
|
2987
3040
|
}
|
|
2988
3041
|
|
|
2989
|
-
//
|
|
2990
|
-
//
|
|
3042
|
+
// ── Whole-number fidelity on the affected-row RETURNING read (tsk_daf4d6d2) ──
|
|
3043
|
+
// The database stores whole numbers as 64-bit values; a JavaScript number
|
|
3044
|
+
// is only exact to 9007199254740991. On a READ the platform recovers the
|
|
3045
|
+
// exact value by re-running the SELECT with a cast-to-text projection
|
|
3046
|
+
// (db-result-fidelity.ts). A WRITE cannot be re-run, so the same guard has
|
|
3047
|
+
// to be INSIDE the statement: the platform composes the RETURNING list
|
|
3048
|
+
// itself and casts any out-of-range whole number to text there, so the
|
|
3049
|
+
// affected row comes back exactly as the read path would return it.
|
|
3050
|
+
// Before this, a write whose affected row held a 64-bit id COMMITTED and
|
|
3051
|
+
// THEN threw DATABASE_VALUE_TOO_LARGE from the read-back — a successful
|
|
3052
|
+
// write reported as a failure, and no structured update to such a row was
|
|
3053
|
+
// possible even when it did not touch that column.
|
|
3054
|
+
//
|
|
3055
|
+
// Column names come from the declared schema when the table is managed
|
|
3056
|
+
// (free) and otherwise from ONE table-shape read, cached for this request.
|
|
3057
|
+
// If the shape cannot be read, or carries a name this builder would not
|
|
3058
|
+
// quote, the statement keeps its long-standing RETURNING * (rule 9: the
|
|
3059
|
+
// fidelity guard must never turn a working write into a failing one).
|
|
3060
|
+
const __sw_returningShapeCache = new Map();
|
|
3061
|
+
|
|
3062
|
+
// The column list RETURNING must project, or null to keep RETURNING *.
|
|
3063
|
+
async function __sw_structReturningColumns(label, table) {
|
|
3064
|
+
const key = __sw_lower(table);
|
|
3065
|
+
if (__sw_returningShapeCache.has(key)) return __sw_returningShapeCache.get(key);
|
|
3066
|
+
let cols = null;
|
|
3067
|
+
const shape = __sw_structManagedShape(table);
|
|
3068
|
+
if (shape) {
|
|
3069
|
+
cols = [];
|
|
3070
|
+
for (let i = 0; i < shape.columns.length; i++) {
|
|
3071
|
+
cols.push({ n: shape.columns[i].n, guard: shape.columns[i].t === 'integer' });
|
|
3072
|
+
}
|
|
3073
|
+
const ownerColumn = __sw_structResolveOwnerColumn(table);
|
|
3074
|
+
if (ownerColumn) {
|
|
3075
|
+
let present = false;
|
|
3076
|
+
for (let i = 0; i < cols.length; i++) if (cols[i].n === __sw_lower(ownerColumn)) present = true;
|
|
3077
|
+
if (!present) cols.push({ n: __sw_lower(ownerColumn), guard: false });
|
|
3078
|
+
}
|
|
3079
|
+
} else if (!__sw_quotableColumnName(table)) {
|
|
3080
|
+
// Unreachable through the builder (the renderer quotes and validates
|
|
3081
|
+
// the table first), and the only place a table name is spelled into
|
|
3082
|
+
// SQL text rather than bound, so it is checked here on its own.
|
|
3083
|
+
cols = null;
|
|
3084
|
+
} else {
|
|
3085
|
+
// hidden = 1 is a virtual table's hidden column, which SELECT * and
|
|
3086
|
+
// RETURNING * both omit; generated columns (2/3) are included by both.
|
|
3087
|
+
const shapeSql = "SELECT name, type, hidden FROM pragma_table_xinfo('" + table + "')";
|
|
3088
|
+
try {
|
|
3089
|
+
const r = await __sw_execOne(label, DB, shapeSql, []);
|
|
3090
|
+
const rows = (r && r.results) || [];
|
|
3091
|
+
const out = [];
|
|
3092
|
+
for (let i = 0; i < rows.length; i++) {
|
|
3093
|
+
const row = rows[i];
|
|
3094
|
+
if (!row || typeof row !== 'object') { out.length = 0; break; }
|
|
3095
|
+
if (Number(row.hidden) === 1) continue;
|
|
3096
|
+
out.push({ n: row.name, guard: __sw_integerCapableAffinity(row.type) });
|
|
3097
|
+
}
|
|
3098
|
+
cols = out.length > 0 ? out : null;
|
|
3099
|
+
} catch (_) {
|
|
3100
|
+
cols = null;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
if (cols) {
|
|
3104
|
+
for (let i = 0; i < cols.length; i++) {
|
|
3105
|
+
if (!__sw_quotableColumnName(cols[i].n)) { cols = null; break; }
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
__sw_returningShapeCache.set(key, cols);
|
|
3109
|
+
return cols;
|
|
3110
|
+
}
|
|
3111
|
+
|
|
3112
|
+
// THE renderer: a pure function of (IR, principalId, returning). Identifiers
|
|
3113
|
+
// are validated + double-quoted; every value — including LIMIT/OFFSET — is a
|
|
2991
3114
|
// bound ?. For 'user' scope it appends "ownerColumn" = ? (and, for
|
|
2992
3115
|
// INSERT, adds the owner column + value itself). The owner predicate is
|
|
2993
|
-
// NEVER in ir.where; the renderer owns it.
|
|
2994
|
-
|
|
3116
|
+
// NEVER in ir.where; the renderer owns it. The 'returning' argument is the
|
|
3117
|
+
// write-path column list resolved above (null keeps RETURNING *).
|
|
3118
|
+
function __sw_renderIR(ir, principalId, returning) {
|
|
2995
3119
|
// Only the 'project' table arm is renderable today. The IR reserves a
|
|
2996
3120
|
// 'primitive' arm for future platform primitives (users/calendar/inbox/
|
|
2997
3121
|
// files) that arrive pre-scoped — but the runtime must REJECT it
|
|
@@ -3132,7 +3256,7 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
3132
3256
|
existsSql += ' AND ' + q(m.mg[i]) + ' = ?';
|
|
3133
3257
|
params.push(gv);
|
|
3134
3258
|
}
|
|
3135
|
-
sql = 'INSERT INTO ' + tableSql + ' (' + colList + ') SELECT ' + placeholders + ' WHERE EXISTS (' + existsSql + ')
|
|
3259
|
+
sql = 'INSERT INTO ' + tableSql + ' (' + colList + ') SELECT ' + placeholders + ' WHERE EXISTS (' + existsSql + ')' + __sw_returningSql(returning);
|
|
3136
3260
|
return { sql: sql, params: params };
|
|
3137
3261
|
}
|
|
3138
3262
|
sql = 'INSERT INTO ' + tableSql + ' (' + allCols.map(q).join(', ') + ') VALUES (' + allCols.map(function () { return '?'; }).join(', ') + ')';
|
|
@@ -3151,13 +3275,13 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
3151
3275
|
sql += ' ON CONFLICT DO UPDATE SET ' + sets.join(', ');
|
|
3152
3276
|
if (ir.scope.mode === 'user') { sql += ' WHERE ' + q(ir.scope.ownerColumn) + ' = ?'; params.push(principalId); }
|
|
3153
3277
|
}
|
|
3154
|
-
sql +=
|
|
3278
|
+
sql += __sw_returningSql(returning);
|
|
3155
3279
|
} else if (ir.kind === 'update') {
|
|
3156
3280
|
if (ir.values.length === 0) __sw_structThrow('sw.db.update requires a non-empty set.');
|
|
3157
3281
|
const assigns = ir.values.map(function (e) { params.push(e.value); return q(e.column) + ' = ?'; });
|
|
3158
|
-
sql = 'UPDATE ' + tableSql + ' SET ' + assigns.join(', ') + whereSql() +
|
|
3282
|
+
sql = 'UPDATE ' + tableSql + ' SET ' + assigns.join(', ') + whereSql() + __sw_returningSql(returning);
|
|
3159
3283
|
} else {
|
|
3160
|
-
sql = 'DELETE FROM ' + tableSql + whereSql() +
|
|
3284
|
+
sql = 'DELETE FROM ' + tableSql + whereSql() + __sw_returningSql(returning);
|
|
3161
3285
|
}
|
|
3162
3286
|
return { sql: sql, params: params };
|
|
3163
3287
|
}
|
|
@@ -3380,11 +3504,22 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
3380
3504
|
const __sw_t0 = Date.now();
|
|
3381
3505
|
let rendered;
|
|
3382
3506
|
try {
|
|
3383
|
-
rendered = __sw_renderIR(ir, principalId);
|
|
3507
|
+
rendered = __sw_renderIR(ir, principalId, null);
|
|
3384
3508
|
} catch (err) {
|
|
3385
3509
|
__sw_emitQueryEvent(ir, Date.now() - __sw_t0, 0, false, err && err.code);
|
|
3386
3510
|
throw err;
|
|
3387
3511
|
}
|
|
3512
|
+
// Writes project their affected row through a fidelity-guarded
|
|
3513
|
+
// RETURNING list (tsk_daf4d6d2). Resolved AFTER the render above so a
|
|
3514
|
+
// rejected call still reaches zero transport, and re-rendered rather
|
|
3515
|
+
// than patched — the renderer is a pure function, so the second pass
|
|
3516
|
+
// produces the same statement with the guarded projection and the
|
|
3517
|
+
// identical bound params. Reads never take this path: same call, same
|
|
3518
|
+
// SQL, same params as before.
|
|
3519
|
+
if (ir.kind === 'insert' || ir.kind === 'update' || ir.kind === 'delete') {
|
|
3520
|
+
const returning = await __sw_structReturningColumns(label, ir.table.name);
|
|
3521
|
+
if (returning) rendered = __sw_renderIR(ir, principalId, returning);
|
|
3522
|
+
}
|
|
3388
3523
|
let r;
|
|
3389
3524
|
const liveReadFingerprint = ir.kind === 'select' ? __sw_structFingerprint(ir) : null;
|
|
3390
3525
|
const liveReadDeclared = !!liveReadFingerprint && LIVE_MANIFEST.liveViews.some(function (view) {
|
|
@@ -3945,6 +4080,30 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
3945
4080
|
return err;
|
|
3946
4081
|
}
|
|
3947
4082
|
|
|
4083
|
+
// The same failure on a statement that has ALREADY RUN. Saying so is the
|
|
4084
|
+
// whole point: a caller that reads 'failed' off a committed INSERT retries
|
|
4085
|
+
// it and writes the row twice (tsk_daf4d6d2). Raw SQL is run exactly as
|
|
4086
|
+
// written, so the platform cannot add the cast for the caller here — the
|
|
4087
|
+
// composed writers (sw.db.insert / update / remove) do it themselves.
|
|
4088
|
+
function __sw_exactIntegerWriteError(column) {
|
|
4089
|
+
const err = new Error(
|
|
4090
|
+
'The statement ran and its changes were applied, but the row it returned holds an integer outside ' +
|
|
4091
|
+
"JavaScript's exact number range in column " + JSON.stringify(String(column)) + ', which cannot be read ' +
|
|
4092
|
+
'back exactly afterwards. Do not retry the statement. Select the column as CAST(' +
|
|
4093
|
+
__sw_quoteResultColumn(column) + ' AS TEXT) in the RETURNING list, or write through sw.db.insert / ' +
|
|
4094
|
+
'sw.db.update / sw.db.remove, which compose that cast for you.'
|
|
4095
|
+
);
|
|
4096
|
+
err.code = 'DATABASE_VALUE_TOO_LARGE';
|
|
4097
|
+
err.status = 400;
|
|
4098
|
+
err.__sw_expose = true;
|
|
4099
|
+
return err;
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
function __sw_dbStatementApplied(sql) {
|
|
4103
|
+
const kind = __sw_dbStatementKind(sql);
|
|
4104
|
+
return kind === 'INSERT' || kind === 'UPDATE' || kind === 'DELETE' || kind === 'REPLACE';
|
|
4105
|
+
}
|
|
4106
|
+
|
|
3948
4107
|
// THE result-row decoding chokepoint for every sw.db transport and shape:
|
|
3949
4108
|
// native binding, REST fallback, raw query, structured query, and batch
|
|
3950
4109
|
// statement results all pass here before customer code can observe them.
|
|
@@ -3956,7 +4115,11 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
3956
4115
|
async function __sw_decodeDbResult(database, sql, params, result) {
|
|
3957
4116
|
const unsafeColumn = __sw_unsafeIntegerResultColumn(result);
|
|
3958
4117
|
if (!unsafeColumn) return result;
|
|
3959
|
-
if (__sw_dbStatementKind(sql) !== 'SELECT')
|
|
4118
|
+
if (__sw_dbStatementKind(sql) !== 'SELECT') {
|
|
4119
|
+
throw __sw_dbStatementApplied(sql)
|
|
4120
|
+
? __sw_exactIntegerWriteError(unsafeColumn)
|
|
4121
|
+
: __sw_exactIntegerReadError(unsafeColumn);
|
|
4122
|
+
}
|
|
3960
4123
|
|
|
3961
4124
|
const first = result.results && result.results[0];
|
|
3962
4125
|
if (!first || typeof first !== 'object' || __sw_arrayIsArray(first)) {
|
|
@@ -3980,6 +4143,38 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
3980
4143
|
return exact;
|
|
3981
4144
|
}
|
|
3982
4145
|
|
|
4146
|
+
// SQLite column affinity, from the declared type. Only INTEGER, NUMERIC
|
|
4147
|
+
// and BLOB/none affinity can hold a whole number out of exact range —
|
|
4148
|
+
// TEXT affinity stores it as text and REAL affinity as a decimal, neither
|
|
4149
|
+
// of which the read path ever flags. Guarding only these keeps the
|
|
4150
|
+
// composed RETURNING list short on wide tables.
|
|
4151
|
+
function __sw_integerCapableAffinity(declaredType) {
|
|
4152
|
+
const t = __sw_lower(String(declaredType == null ? '' : declaredType));
|
|
4153
|
+
if (t.indexOf('int') !== -1) return true;
|
|
4154
|
+
if (t.indexOf('char') !== -1 || t.indexOf('clob') !== -1 || t.indexOf('text') !== -1) return false;
|
|
4155
|
+
if (t === '' || t.indexOf('blob') !== -1) return true;
|
|
4156
|
+
if (t.indexOf('real') !== -1 || t.indexOf('floa') !== -1 || t.indexOf('doub') !== -1) return false;
|
|
4157
|
+
return true;
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4160
|
+
function __sw_quotableColumnName(name) {
|
|
4161
|
+
return typeof name === 'string' && name.length > 0 && name.length <= 64 &&
|
|
4162
|
+
__sw_reMatch(__SW_STRUCT_IDENT_RE, name);
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
function __sw_returningSql(cols) {
|
|
4166
|
+
if (!cols || cols.length === 0) return ' RETURNING *';
|
|
4167
|
+
const parts = [];
|
|
4168
|
+
for (let i = 0; i < cols.length; i++) {
|
|
4169
|
+
const name = __sw_structQuote(cols[i].n);
|
|
4170
|
+
parts.push(cols[i].guard
|
|
4171
|
+
? 'CASE WHEN typeof(' + name + ") = 'integer' AND (" + name + ' > 9007199254740991 OR ' +
|
|
4172
|
+
name + ' < -9007199254740991) THEN CAST(' + name + ' AS TEXT) ELSE ' + name + ' END AS ' + name
|
|
4173
|
+
: name);
|
|
4174
|
+
}
|
|
4175
|
+
return ' RETURNING ' + parts.join(', ');
|
|
4176
|
+
}
|
|
4177
|
+
|
|
3983
4178
|
function __sw_execOne(label, database, sql, params) {
|
|
3984
4179
|
return __sw_timedExec(label, sql, async function () {
|
|
3985
4180
|
const result = await __sw_prepOn(database, sql, params).all();
|
|
@@ -6617,20 +6812,43 @@ function buildPlatformContext(env, request, runtimeFetch = fetch, outboundFetch
|
|
|
6617
6812
|
// resolver; there is no parallel derivation.
|
|
6618
6813
|
__sw_authResolveRequest = __sw_resolveRequestUser;
|
|
6619
6814
|
__sw_authVerifyPrincipal = __sw_resolveRequestUser;
|
|
6620
|
-
// Ambient visitor identity (tsk_99bec0b4
|
|
6621
|
-
// this ONLY when there is no verified user
|
|
6622
|
-
// owner-identity mode is 'visitor'. It reads
|
|
6623
|
-
// auth snapshot (the same source verified
|
|
6624
|
-
// customer code cannot spoof or downgrade it),
|
|
6625
|
-
// otherwise mints a fresh id AND queues the
|
|
6626
|
-
// no caller Response to attach it to on the
|
|
6815
|
+
// Ambient visitor identity (tsk_99bec0b4; made reachable by tsk_11f8f791).
|
|
6816
|
+
// sw.db's owner() resolver calls this ONLY when there is no verified user
|
|
6817
|
+
// AND the project's baked owner-identity mode is 'visitor'. It reads
|
|
6818
|
+
// __Host-sw_anon_id from the FROZEN auth snapshot (the same source verified
|
|
6819
|
+
// identity is derived from, so customer code cannot spoof or downgrade it),
|
|
6820
|
+
// validates the format, and otherwise mints a fresh id AND queues the
|
|
6821
|
+
// Set-Cookie itself — there is no caller Response to attach it to on the
|
|
6822
|
+
// structured path. Never throws.
|
|
6823
|
+
//
|
|
6824
|
+
// What binds the owner predicate, exactly (the security claim):
|
|
6825
|
+
// - ONE channel. The value is read from the __Host-sw_anon_id cookie on the
|
|
6826
|
+
// frozen snapshot and nowhere else — no header, query param, body field,
|
|
6827
|
+
// or sw.db argument can name a visitor. An id offered through any other
|
|
6828
|
+
// channel is not read, so the request is simply a visitor with no cookie
|
|
6829
|
+
// and the platform mints its own.
|
|
6830
|
+
// - NEVER over a verified user. The caller reaches this only when identity
|
|
6831
|
+
// resolution found no verified principal, so a cookie cannot downgrade or
|
|
6832
|
+
// impersonate a signed-in user.
|
|
6833
|
+
// - The anon_ prefix is mandatory, which is what keeps the visitor
|
|
6834
|
+
// namespace disjoint from the app-user namespace: an id shaped like an
|
|
6835
|
+
// app user's (a uuid) fails the test and is discarded, so a visitor
|
|
6836
|
+
// cannot address a signed-in user's rows by naming their id.
|
|
6837
|
+
// - Bounded (tsk_11f8f791): a conforming id is 37 chars; anything past 64
|
|
6838
|
+
// is not ours and is discarded rather than written into an owner column.
|
|
6839
|
+
// - Anything that fails the test is IGNORED, never repaired and never used:
|
|
6840
|
+
// the platform mints a fresh id, so a malformed or hostile cookie yields
|
|
6841
|
+
// a brand-new empty visitor, never a partial or borrowed one.
|
|
6842
|
+
// The conforming value itself is a bearer credential with the same standing
|
|
6843
|
+
// as a session cookie — HttpOnly, Secure, __Host-, 122 bits of entropy —
|
|
6844
|
+
// and is stated as such in the docs. Holding it IS being that visitor.
|
|
6627
6845
|
__sw_resolveVisitorIdentity = function () {
|
|
6628
6846
|
let id = null;
|
|
6629
6847
|
try {
|
|
6630
6848
|
const ch = __sw_authSnapshotRequest.headers.get('cookie');
|
|
6631
6849
|
__sw_expireLegacyAuthCookiesIfPresent(ch, request && request.url);
|
|
6632
6850
|
const existing = __sw_readCookie(ch, '__Host-sw_anon_id');
|
|
6633
|
-
if (existing && /^anon_[a-zA-Z0-9_-]{8,}$/.test(existing)) id = existing;
|
|
6851
|
+
if (existing && /^anon_[a-zA-Z0-9_-]{8,64}$/.test(existing)) id = existing;
|
|
6634
6852
|
} catch (_) { /* fall through and mint */ }
|
|
6635
6853
|
if (!id) {
|
|
6636
6854
|
id = 'anon_' + crypto.randomUUID().replace(/-/g, '');
|
package/runtime/sw-init.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// VENDORED from worker/src/utils/function-bundle.ts (SW_INIT_JS) @
|
|
1
|
+
// VENDORED from worker/src/utils/function-bundle.ts (SW_INIT_JS) @ 1bf981dd
|
|
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
|