digital-boardgame-framework 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,58 @@
1
+ export interface Point {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ /** A simple closed ring. First point need not repeat the last. */
6
+ export type Polygon = Point[];
7
+ export interface BBox {
8
+ minX: number;
9
+ minY: number;
10
+ maxX: number;
11
+ maxY: number;
12
+ }
13
+ export declare function boundingBox(polygon: Polygon): BBox;
14
+ /** Signed area (shoelace). Positive for counter-clockwise rings. */
15
+ export declare function signedArea(polygon: Polygon): number;
16
+ export declare function area(polygon: Polygon): number;
17
+ /** Area-weighted polygon centroid. NOTE: for a concave shape this can fall
18
+ * outside the polygon — do not use it to anchor tokens. Use
19
+ * poleOfInaccessibility instead. Kept for comparison / degenerate fallback. */
20
+ export declare function centroid(polygon: Polygon): Point;
21
+ export declare function pointInPolygon(point: Point, polygon: Polygon): boolean;
22
+ /** Signed distance from a point to the polygon boundary: positive inside,
23
+ * negative outside, magnitude = distance to the nearest edge. */
24
+ export declare function signedDistanceToPolygon(point: Point, polygon: Polygon): number;
25
+ /** The interior point farthest from any edge — the "visual centre" used for map
26
+ * labels. Always inside the polygon (for a valid simple polygon), and in its
27
+ * thickest part. `precision` is in coordinate units; smaller = more accurate,
28
+ * slower. This is the correct anchor for a token cluster. */
29
+ export declare function poleOfInaccessibility(polygon: Polygon, precision?: number): Point;
30
+ export interface LayoutOptions {
31
+ /** Radius of one token, in coordinate units. */
32
+ tokenRadius: number;
33
+ /** Extra spacing between adjacent tokens (default tokenRadius * 0.3). */
34
+ gap?: number;
35
+ /** Smallest scale to shrink the cluster to before collapsing to a stack
36
+ * (default 0.4). */
37
+ minScale?: number;
38
+ /** Precision passed to poleOfInaccessibility (default 1.0). */
39
+ precision?: number;
40
+ }
41
+ export interface TokenLayout {
42
+ /** The polygon's visual centre — the cluster anchor / stack location. */
43
+ anchor: Point;
44
+ /** One point per token, all inside the polygon. When `stacked` is true this
45
+ * holds a single point (the anchor) and the renderer should draw a pile. */
46
+ points: Point[];
47
+ /** Scale applied to fit the cluster (1 = full size, smaller = shrunk). */
48
+ scale: number;
49
+ /** True when the cluster couldn't fit even at minScale — render a single
50
+ * anchored pile with a count badge instead of N separate tokens. */
51
+ stacked: boolean;
52
+ }
53
+ /** Lay out `count` tokens inside `polygon`, clustered at its visual centre and
54
+ * kept within the boundary. Shrinks the cluster to fit; if even the shrunk
55
+ * cluster won't fit, returns a single anchored point with `stacked: true` so
56
+ * the renderer can draw a pile + count badge. Deterministic. */
57
+ export declare function layoutTokensInPolygon(polygon: Polygon, count: number, opts: LayoutOptions): TokenLayout;
58
+ //# sourceMappingURL=geo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../../src/core/geo.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,KAAK;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,kEAAkE;AAClE,MAAM,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;AAE9B,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAUlD;AAED,oEAAoE;AACpE,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAMnD;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAE7C;AAED;;gFAEgF;AAChF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,KAAK,CAkBhD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAUtE;AAeD;kEACkE;AAClE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAO9E;AAqDD;;;8DAG8D;AAC9D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,SAAM,GAAG,KAAK,CAgC9E;AAID,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;yBACqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,yEAAyE;IACzE,MAAM,EAAE,KAAK,CAAC;IACd;iFAC6E;IAC7E,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd;yEACqE;IACrE,OAAO,EAAE,OAAO,CAAC;CAClB;AA0BD;;;iEAGiE;AACjE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,aAAa,GAClB,WAAW,CAqBb"}
@@ -0,0 +1,226 @@
1
+ // Pure 2D polygon geometry for map games — no DOM, no rendering. Territory
2
+ // polygons live per-game (the data); this module is the shared math that turns a
3
+ // polygon + a token count into where the tokens go, anchored at the polygon's
4
+ // visual centre and kept inside its boundary.
5
+ //
6
+ // Coordinates are in whatever space the caller uses (typically pixels in the map
7
+ // image). Everything here is deterministic — no RNG — so the same state always
8
+ // lays out identically. See docs/territory-polygons-design.md.
9
+ export function boundingBox(polygon) {
10
+ if (polygon.length === 0)
11
+ throw new Error('boundingBox: empty polygon');
12
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
13
+ for (const p of polygon) {
14
+ if (p.x < minX)
15
+ minX = p.x;
16
+ if (p.y < minY)
17
+ minY = p.y;
18
+ if (p.x > maxX)
19
+ maxX = p.x;
20
+ if (p.y > maxY)
21
+ maxY = p.y;
22
+ }
23
+ return { minX, minY, maxX, maxY };
24
+ }
25
+ /** Signed area (shoelace). Positive for counter-clockwise rings. */
26
+ export function signedArea(polygon) {
27
+ let sum = 0;
28
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
29
+ sum += (polygon[j].x + polygon[i].x) * (polygon[j].y - polygon[i].y);
30
+ }
31
+ return sum / 2;
32
+ }
33
+ export function area(polygon) {
34
+ return Math.abs(signedArea(polygon));
35
+ }
36
+ /** Area-weighted polygon centroid. NOTE: for a concave shape this can fall
37
+ * outside the polygon — do not use it to anchor tokens. Use
38
+ * poleOfInaccessibility instead. Kept for comparison / degenerate fallback. */
39
+ export function centroid(polygon) {
40
+ let cx = 0, cy = 0, a = 0;
41
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
42
+ const cross = polygon[j].x * polygon[i].y - polygon[i].x * polygon[j].y;
43
+ cx += (polygon[j].x + polygon[i].x) * cross;
44
+ cy += (polygon[j].y + polygon[i].y) * cross;
45
+ a += cross;
46
+ }
47
+ if (a === 0) {
48
+ // Degenerate (zero area): fall back to the mean of vertices.
49
+ const n = polygon.length;
50
+ return {
51
+ x: polygon.reduce((s, p) => s + p.x, 0) / n,
52
+ y: polygon.reduce((s, p) => s + p.y, 0) / n,
53
+ };
54
+ }
55
+ a *= 3;
56
+ return { x: cx / a, y: cy / a };
57
+ }
58
+ export function pointInPolygon(point, polygon) {
59
+ let inside = false;
60
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
61
+ const a = polygon[i], b = polygon[j];
62
+ if ((a.y > point.y) !== (b.y > point.y) &&
63
+ point.x < ((b.x - a.x) * (point.y - a.y)) / (b.y - a.y) + a.x) {
64
+ inside = !inside;
65
+ }
66
+ }
67
+ return inside;
68
+ }
69
+ function segmentDistSq(p, a, b) {
70
+ let x = a.x, y = a.y;
71
+ let dx = b.x - x, dy = b.y - y;
72
+ if (dx !== 0 || dy !== 0) {
73
+ const t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
74
+ if (t > 1) {
75
+ x = b.x;
76
+ y = b.y;
77
+ }
78
+ else if (t > 0) {
79
+ x += dx * t;
80
+ y += dy * t;
81
+ }
82
+ }
83
+ dx = p.x - x;
84
+ dy = p.y - y;
85
+ return dx * dx + dy * dy;
86
+ }
87
+ /** Signed distance from a point to the polygon boundary: positive inside,
88
+ * negative outside, magnitude = distance to the nearest edge. */
89
+ export function signedDistanceToPolygon(point, polygon) {
90
+ let minSq = Infinity;
91
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
92
+ minSq = Math.min(minSq, segmentDistSq(point, polygon[i], polygon[j]));
93
+ }
94
+ const dist = Math.sqrt(minSq);
95
+ return pointInPolygon(point, polygon) ? dist : -dist;
96
+ }
97
+ function makeCell(x, y, h, polygon) {
98
+ const d = signedDistanceToPolygon({ x, y }, polygon);
99
+ return { x, y, h, d, max: d + h * Math.SQRT2 };
100
+ }
101
+ // Binary max-heap keyed on cell.max.
102
+ class CellHeap {
103
+ items = [];
104
+ get size() { return this.items.length; }
105
+ push(c) {
106
+ const a = this.items;
107
+ a.push(c);
108
+ let i = a.length - 1;
109
+ while (i > 0) {
110
+ const parent = (i - 1) >> 1;
111
+ if (a[parent].max >= a[i].max)
112
+ break;
113
+ [a[parent], a[i]] = [a[i], a[parent]];
114
+ i = parent;
115
+ }
116
+ }
117
+ pop() {
118
+ const a = this.items;
119
+ const top = a[0];
120
+ const last = a.pop();
121
+ if (a.length > 0) {
122
+ a[0] = last;
123
+ let i = 0;
124
+ for (;;) {
125
+ const l = 2 * i + 1, r = 2 * i + 2;
126
+ let largest = i;
127
+ if (l < a.length && a[l].max > a[largest].max)
128
+ largest = l;
129
+ if (r < a.length && a[r].max > a[largest].max)
130
+ largest = r;
131
+ if (largest === i)
132
+ break;
133
+ [a[largest], a[i]] = [a[i], a[largest]];
134
+ i = largest;
135
+ }
136
+ }
137
+ return top;
138
+ }
139
+ }
140
+ /** The interior point farthest from any edge — the "visual centre" used for map
141
+ * labels. Always inside the polygon (for a valid simple polygon), and in its
142
+ * thickest part. `precision` is in coordinate units; smaller = more accurate,
143
+ * slower. This is the correct anchor for a token cluster. */
144
+ export function poleOfInaccessibility(polygon, precision = 1.0) {
145
+ const bb = boundingBox(polygon);
146
+ const width = bb.maxX - bb.minX;
147
+ const height = bb.maxY - bb.minY;
148
+ const cellSize = Math.min(width, height);
149
+ if (cellSize === 0)
150
+ return { x: bb.minX, y: bb.minY };
151
+ const heap = new CellHeap();
152
+ const h0 = cellSize / 2;
153
+ for (let x = bb.minX; x < bb.maxX; x += cellSize) {
154
+ for (let y = bb.minY; y < bb.maxY; y += cellSize) {
155
+ heap.push(makeCell(x + h0, y + h0, h0, polygon));
156
+ }
157
+ }
158
+ // Seed best with the centroid and the bbox centre.
159
+ const c = centroid(polygon);
160
+ let best = makeCell(c.x, c.y, 0, polygon);
161
+ const bboxCell = makeCell(bb.minX + width / 2, bb.minY + height / 2, 0, polygon);
162
+ if (bboxCell.d > best.d)
163
+ best = bboxCell;
164
+ while (heap.size > 0) {
165
+ const cell = heap.pop();
166
+ if (cell.d > best.d)
167
+ best = cell;
168
+ if (cell.max - best.d <= precision)
169
+ continue;
170
+ const h = cell.h / 2;
171
+ heap.push(makeCell(cell.x - h, cell.y - h, h, polygon));
172
+ heap.push(makeCell(cell.x + h, cell.y - h, h, polygon));
173
+ heap.push(makeCell(cell.x - h, cell.y + h, h, polygon));
174
+ heap.push(makeCell(cell.x + h, cell.y + h, h, polygon));
175
+ }
176
+ return { x: best.x, y: best.y };
177
+ }
178
+ // Concentric-ring cluster positions around a centre. Ring k holds 6k points, so
179
+ // the cluster grows outward roughly evenly. `spacing` is the ring-radius step.
180
+ function clusterPositions(center, count, spacing) {
181
+ const pts = [{ x: center.x, y: center.y }];
182
+ for (let ring = 1; pts.length < count; ring++) {
183
+ const inRing = 6 * ring;
184
+ for (let i = 0; i < inRing && pts.length < count; i++) {
185
+ const angle = (2 * Math.PI * i) / inRing;
186
+ pts.push({
187
+ x: center.x + ring * spacing * Math.cos(angle),
188
+ y: center.y + ring * spacing * Math.sin(angle),
189
+ });
190
+ }
191
+ }
192
+ return pts;
193
+ }
194
+ function clusterFits(pts, polygon, clearance) {
195
+ for (const p of pts) {
196
+ if (signedDistanceToPolygon(p, polygon) < clearance)
197
+ return false;
198
+ }
199
+ return true;
200
+ }
201
+ /** Lay out `count` tokens inside `polygon`, clustered at its visual centre and
202
+ * kept within the boundary. Shrinks the cluster to fit; if even the shrunk
203
+ * cluster won't fit, returns a single anchored point with `stacked: true` so
204
+ * the renderer can draw a pile + count badge. Deterministic. */
205
+ export function layoutTokensInPolygon(polygon, count, opts) {
206
+ const anchor = poleOfInaccessibility(polygon, opts.precision ?? 1.0);
207
+ const gap = opts.gap ?? opts.tokenRadius * 0.3;
208
+ const minScale = opts.minScale ?? 0.4;
209
+ if (count <= 0)
210
+ return { anchor, points: [], scale: 1, stacked: false };
211
+ if (count === 1) {
212
+ const fits = signedDistanceToPolygon(anchor, polygon) >= opts.tokenRadius;
213
+ return { anchor, points: [anchor], scale: fits ? 1 : minScale, stacked: !fits };
214
+ }
215
+ const step = 2 * opts.tokenRadius + gap;
216
+ // Try full size, then shrink in 0.1 increments down to minScale.
217
+ for (let scale = 1.0; scale >= minScale - 1e-9; scale -= 0.1) {
218
+ const pts = clusterPositions(anchor, count, step * scale);
219
+ if (clusterFits(pts, polygon, opts.tokenRadius * scale)) {
220
+ return { anchor, points: pts, scale: Math.max(scale, minScale), stacked: false };
221
+ }
222
+ }
223
+ // Won't fit even shrunk — collapse to a single anchored pile.
224
+ return { anchor, points: [anchor], scale: minScale, stacked: true };
225
+ }
226
+ //# sourceMappingURL=geo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.js","sourceRoot":"","sources":["../../src/core/geo.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,4 +4,5 @@ export { StructuredLog, type LogEntry } from './core/log.js';
4
4
  export { EffectContext, type ChoiceRequest, type ChoiceResponse } from './core/effects.js';
5
5
  export { type GameAdapter, type GameResult } from './core/adapter.js';
6
6
  export { type PlayerController, type ControllerContext, RandomAI, ScriptedController, } from './core/controller.js';
7
+ export { type Point, type Polygon, type BBox, type LayoutOptions, type TokenLayout, boundingBox, signedArea, area, centroid, pointInPolygon, signedDistanceToPolygon, poleOfInaccessibility, layoutTokensInPolygon, } from './core/geo.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,KAAK,EACV,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,WAAW,EACX,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -3,4 +3,5 @@ export { jsonCodec } from './core/codec.js';
3
3
  export { StructuredLog } from './core/log.js';
4
4
  export { EffectContext } from './core/effects.js';
5
5
  export { RandomAI, ScriptedController, } from './core/controller.js';
6
+ export { boundingBox, signedArea, area, centroid, pointInPolygon, signedDistanceToPolygon, poleOfInaccessibility, layoutTokensInPolygon, } from './core/geo.js';
6
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAiB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAA2C,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAGL,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,SAAS,EAAc,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAiB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,aAAa,EAA2C,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAGL,QAAQ,EACR,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAML,WAAW,EACX,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digital-boardgame-framework",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Foundation library for turn-based digital boardgames: deterministic engine plumbing, async multiplayer, agent-friendly bug triage.",
5
5
  "keywords": ["boardgame", "board-game", "multiplayer", "async-multiplayer", "turn-based", "game-framework", "supabase", "cloudflare-pages"],
6
6
  "author": "John Champaign",
@@ -59,9 +59,16 @@ create index if not exists dbf_reports_category on dbf_reports(category);
59
59
  create index if not exists dbf_reports_unresolved on dbf_reports(report_id) where resolution is null;
60
60
  create index if not exists dbf_reports_game on dbf_reports(game_id);
61
61
 
62
- -- Row-level security: this library does its own auth via per-player tokens
63
- -- looked up in dbf_games.tokens. Disable RLS or allow the service role only.
64
- -- Do NOT expose these tables directly via PostgREST/anon-key go through
65
- -- your application's server-side endpoints (Cloudflare Pages Functions,
66
- -- Supabase Edge Functions, etc.) which instantiate GameServer with the
67
- -- service-role key.
62
+ -- Row-level security REQUIRED, not optional.
63
+ -- These tables hold secrets (dbf_games.tokens), unredacted game state
64
+ -- (dbf_snapshots.state hidden hands!), and chat. All access goes through your
65
+ -- server endpoints using the SERVICE-ROLE key, which BYPASSES RLS. Enabling RLS
66
+ -- with NO policies therefore denies the public anon key everything (which is
67
+ -- now in client bundles for Realtime) while the server keeps full access.
68
+ -- Realtime *broadcast* doesn't touch these tables, so it's unaffected.
69
+ -- WITHOUT this, anyone with the project URL + anon key can read tokens and
70
+ -- everyone's hidden state. (Supabase flags it as rls_disabled_in_public.)
71
+ alter table dbf_games enable row level security;
72
+ alter table dbf_snapshots enable row level security;
73
+ alter table dbf_messages enable row level security;
74
+ alter table dbf_reports enable row level security;