longcelot-sheet-db 0.1.7 → 0.1.11

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +35 -20
  2. package/README.md +87 -10
  3. package/dist/adapter/crud.d.ts +6 -3
  4. package/dist/adapter/crud.d.ts.map +1 -1
  5. package/dist/adapter/crud.js +50 -4
  6. package/dist/adapter/crud.js.map +1 -1
  7. package/dist/adapter/sheetAdapter.d.ts +17 -2
  8. package/dist/adapter/sheetAdapter.d.ts.map +1 -1
  9. package/dist/adapter/sheetAdapter.js +154 -2
  10. package/dist/adapter/sheetAdapter.js.map +1 -1
  11. package/dist/cli/commands/export.d.ts +8 -0
  12. package/dist/cli/commands/export.d.ts.map +1 -0
  13. package/dist/cli/commands/export.js +166 -0
  14. package/dist/cli/commands/export.js.map +1 -0
  15. package/dist/cli/commands/generate.d.ts.map +1 -1
  16. package/dist/cli/commands/generate.js +1 -1
  17. package/dist/cli/commands/generate.js.map +1 -1
  18. package/dist/cli/commands/init.d.ts.map +1 -1
  19. package/dist/cli/commands/init.js +30 -5
  20. package/dist/cli/commands/init.js.map +1 -1
  21. package/dist/cli/commands/mock-users.d.ts.map +1 -1
  22. package/dist/cli/commands/mock-users.js +5 -2
  23. package/dist/cli/commands/mock-users.js.map +1 -1
  24. package/dist/cli/commands/seed.d.ts.map +1 -1
  25. package/dist/cli/commands/seed.js +2 -1
  26. package/dist/cli/commands/seed.js.map +1 -1
  27. package/dist/cli/commands/status.d.ts.map +1 -1
  28. package/dist/cli/commands/status.js +13 -4
  29. package/dist/cli/commands/status.js.map +1 -1
  30. package/dist/cli/commands/sync.d.ts +1 -0
  31. package/dist/cli/commands/sync.d.ts.map +1 -1
  32. package/dist/cli/commands/sync.js +175 -80
  33. package/dist/cli/commands/sync.js.map +1 -1
  34. package/dist/cli/commands/validate.d.ts.map +1 -1
  35. package/dist/cli/commands/validate.js +7 -5
  36. package/dist/cli/commands/validate.js.map +1 -1
  37. package/dist/cli/index.js +9 -0
  38. package/dist/cli/index.js.map +1 -1
  39. package/dist/errors/SchemaMismatchError.d.ts +6 -0
  40. package/dist/errors/SchemaMismatchError.d.ts.map +1 -0
  41. package/dist/errors/SchemaMismatchError.js +14 -0
  42. package/dist/errors/SchemaMismatchError.js.map +1 -0
  43. package/dist/errors/index.d.ts +1 -0
  44. package/dist/errors/index.d.ts.map +1 -1
  45. package/dist/errors/index.js +3 -1
  46. package/dist/errors/index.js.map +1 -1
  47. package/dist/index.d.ts +3 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +5 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/schema/columnBuilder.d.ts.map +1 -1
  52. package/dist/schema/columnBuilder.js +1 -0
  53. package/dist/schema/columnBuilder.js.map +1 -1
  54. package/dist/schema/defineTable.d.ts.map +1 -1
  55. package/dist/schema/defineTable.js +8 -0
  56. package/dist/schema/defineTable.js.map +1 -1
  57. package/dist/schema/types.d.ts +13 -1
  58. package/dist/schema/types.d.ts.map +1 -1
  59. package/dist/utils/schemaHash.d.ts +3 -0
  60. package/dist/utils/schemaHash.d.ts.map +1 -0
  61. package/dist/utils/schemaHash.js +24 -0
  62. package/dist/utils/schemaHash.js.map +1 -0
  63. package/package.json +20 -20
package/CHANGELOG.md CHANGED
@@ -18,26 +18,41 @@ This project follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) an
18
18
 
19
19
  ---
20
20
 
21
- ## [Unreleased]
22
-
23
- ### Planned (Phase 2 & 3 Roadmap)
24
- - **Added**: `init --integrate` CLI command to integrate `longcelot-sheet-db` into an existing Node.js project without destroying existing configuration.
25
- - **Added**: `sync --all-users` CLI command to bulk-update Google Sheets schemas across all initialized user endpoints.
26
- - **Added**: Developer sandbox CLI tools (`sheet-db mock-users`).
27
- - **Added**: Prisma/SQL DDL automated migration script (`sheet-db export`).
28
- - **Changed**: Improved documentation for integrating with existing backends and planning forward migration to SQL.
29
- - **Removed**: Redundant `Docs/apiReference.md` file layout to consolidate into root `API.md`.
30
-
31
- ### New Documentation (Q1-Q9)
32
- - **Added**: Clarified OAuth requirement cannot be skipped, required for backend-to-Sheets communication.
33
- - **Added**: Integration workflow for existing projects mapping user auth to sheet-db context.
34
- - **Added**: Development workflow documentation how to test with multiple actor sheets.
35
- - **Added**: Migration path documentation`user_id` vs `sheet_id` purpose.
36
- - **Added**: Cross-actor operations documentation — join APIs and permission models (roadmap).
37
- - **Added**: Export CLI (`sheet-db export`) for generating Prisma schemas and SQL DDL.
38
- - **Added**: Sync `--all-users` for pushing schema changes to all registered users.
39
-
40
- _Nothing yet add your in-progress changes here before the next release._
21
+ ## [0.1.9] — ready to publish
22
+
23
+ ### Added
24
+
25
+ #### Schema Integrity (Q11)
26
+ - **`computeSchemaHash(schema)`** exported utility that computes a deterministic SHA-256 hash of a table's column definitions. Hash changes when any column is added, removed, or retyped; ordering of column definitions does not affect the hash.
27
+ - **`SchemaMismatchError`** new error class thrown when `onSchemaMismatch: 'error'` is set and a user actor sheet's schema hash differs from the registered schema.
28
+ - **`schema_versions` built-in admin table** scaffolded automatically by `sheet-db init`. Stores one row per `(actor_sheet_id, table_name)` with `schema_hash`, `synced_at`, and `column_count`. Read and written internally by the adapter.
29
+ - **`onSchemaMismatch` adapter option** `'warn'` logs to stderr and continues (default), `'error'` throws `SchemaMismatchError`, `'auto-sync'` silently syncs the actor sheet and updates the version record before the first CRUD operation completes.
30
+ - **Schema pre-flight in `withContext()`** — when `onSchemaMismatch` is configured, `withContext()` immediately starts an async version check in the background. All CRUD methods (`create`, `findMany`, `findOne`, `update`, `delete`) await this shared promise before proceeding — so the check runs exactly once per context instance, never per call.
31
+ - **`SheetAdapter.upsertSchemaVersion(actorSheetId, tableName, hash, columnCount)`** — public method for CLI tools and external tooling to write version records.
32
+ - **`SheetAdapter.getSchemaVersion(actorSheetId, tableName)`** public method to read the stored version record for a given sheet + table pair.
33
+ - **`sync --all-users`** pushes schema changes to every registered user sheet by reading `actor_sheet_id` values from the admin `users` table. Updates `schema_versions` after each successful sync. Skips sheets that are already up-to-date (hash match).
34
+ - **`sync --all-users --dry-run`**previews which user sheets are outdated and what would be synced without applying any changes.
35
+ - **Exponential backoff in `sync --all-users`** retries failing API calls up to 5 times with delays of 1 s → 2 s → 4 s → 8 s → 16 s (capped at 32 s) on Google Sheets rate-limit errors (HTTP 429 / quota exceeded).
36
+
37
+ #### Multi-Actor Config (Q10)
38
+ - **`ActorConfig` type** actors in `sheet-db.config.ts` now use `{ role: string; sheetIdEnv: string }` objects, mapping each actor role to its sheet ID environment variable.
39
+ - **`SchemaMismatchBehaviour` type** — exported union `'warn' | 'error' | 'auto-sync'`.
40
+ - **Multi-actor `.env` scaffolding** `sheet-db init` generates a `DEV_<ROLE>_SHEET_ID=` line for every non-admin actor.
41
+ - **Per-actor status table in `sync`** — `sheet-db sync` iterates all configured actors and prints: Actor | Sheet ID | Tables | Status. Actors without a sheet ID env var are skipped with a warning (non-fatal).
42
+ - **`actor_sheet_id` column on admin `users` table** — included in the schema scaffolded by `sheet-db init`.
43
+ - **`schema_versions` schema file** — `sheet-db init` now also writes `schemas/admin/schema_versions.ts`.
44
+ - **`onSchemaMismatch: 'warn'`** — included as a commented default in the config scaffolded by `sheet-db init`.
45
+
46
+ #### Primary Key & Foreign Key (Q pre-existing — shipped in 0.1.8)
47
+ - `primary()` column modifier — auto-generates a nanoid on `create()` for string PKs; strips PK silently on `update()`.
48
+ - `ref('table.column')` — FK validation on `create()` and `update()`; skip via `{ skipFKValidation: true }`.
49
+ - Circular reference detection at `registerSchema()` time.
50
+ - `sheet-db export --prisma` / `--sql` — generates `schema.prisma` and SQL DDL from registered schemas.
51
+
52
+ ### Changed
53
+ - **`SheetDBConfig.actors`** type changed from `string[]` to `ActorConfig[]`. CLI commands normalize both shapes at runtime for backward compatibility.
54
+ - **`sync` command** resolves each actor's sheet ID from its `sheetIdEnv` field rather than hardcoding `ADMIN_SHEET_ID`.
55
+ - **`CRUDOperations` constructor** — accepts an optional fifth argument `preFlight?: Promise<void>` that each async method awaits before executing. Internal change; no API surface change for callers.
41
56
 
42
57
  ---
43
58
 
package/README.md CHANGED
@@ -24,9 +24,11 @@ Instead of running MySQL, PostgreSQL, or MongoDB for staging:
24
24
  - 🔄 **Auto CRUD**: Automatic create, read, update, delete operations
25
25
  - 🎭 **Role-Based Permissions**: Built-in security boundaries
26
26
  - 🔑 **Authentication**: Google OAuth + bcrypt password hashing
27
- - 🛠️ **CLI Tools**: Initialize, generate, sync, and validate schemas
27
+ - 🛠️ **CLI Tools**: Initialize, generate, sync, validate, export, mock-users
28
28
  - 📊 **Type-Safe**: Full TypeScript support
29
29
  - 💰 **Cost-Free**: No infrastructure costs for staging
30
+ - 🔒 **Schema Integrity**: Hash-based version tracking detects stale user sheets at runtime
31
+ - ♻️ **Safe Migrations**: `sync --all-users` pushes schema changes to every user sheet with rate-limit backoff
30
32
 
31
33
  ## 🚀 Quick Start
32
34
 
@@ -149,15 +151,28 @@ const bookings = await userContext.table('bookings').findMany({
149
151
 
150
152
  ### Actors
151
153
 
152
- Actors are user roles that determine where data is stored:
154
+ Actors are user roles that determine where data is stored. Each actor maps to a sheet ID via an environment variable:
153
155
 
154
156
  ```typescript
155
- actors: ["admin", "user", "seller"]
157
+ // sheet-db.config.ts
158
+ actors: [
159
+ { role: "admin", sheetIdEnv: "ADMIN_SHEET_ID" },
160
+ { role: "user", sheetIdEnv: "DEV_USER_SHEET_ID" },
161
+ { role: "seller", sheetIdEnv: "DEV_SELLER_SHEET_ID" },
162
+ ]
156
163
  ```
157
164
 
158
- - **admin**: Data stored in central admin sheet
159
- - **user**: Data stored in user's personal sheet
160
- - **seller**: Data stored in seller's personal sheet
165
+ ```env
166
+ # .env
167
+ ADMIN_SHEET_ID=1ABCyourAdminSheetId
168
+ DEV_USER_SHEET_ID=1DEFyourDevUserSheetId # optional for local dev
169
+ DEV_SELLER_SHEET_ID=1GHIyourDevSellerSheetId # optional for local dev
170
+ ```
171
+
172
+ - **admin**: Data stored in central admin sheet (always required)
173
+ - **user / seller**: Each actor gets a personal sheet at runtime; `DEV_*_SHEET_ID` values let you sync schemas during development without registering real users
174
+
175
+ `sheet-db init` scaffolds all env vars automatically based on the actors you define.
161
176
 
162
177
  ### Schema DSL
163
178
 
@@ -228,6 +243,50 @@ Permissions are enforced automatically:
228
243
  - Admin can access admin tables
229
244
  - Cross-actor access is blocked
230
245
 
246
+ ### Schema Version Tracking
247
+
248
+ `longcelot-sheet-db` computes a **SHA-256 hash** of every table schema and compares it against the hash stored in the built-in `schema_versions` admin table. When `withContext()` is called for a non-admin user, the check runs in the background — every subsequent CRUD call awaits the result before proceeding.
249
+
250
+ Configure the behaviour in `sheet-db.config.ts`:
251
+
252
+ ```typescript
253
+ export default {
254
+ // ...
255
+ onSchemaMismatch: 'warn', // log warning and continue (default)
256
+ // onSchemaMismatch: 'error', // throw SchemaMismatchError
257
+ // onSchemaMismatch: 'auto-sync', // sync the actor sheet automatically
258
+ };
259
+ ```
260
+
261
+ And pass it through to the adapter:
262
+
263
+ ```typescript
264
+ import { createSheetAdapter } from 'longcelot-sheet-db';
265
+
266
+ const adapter = createSheetAdapter({
267
+ adminSheetId: process.env.ADMIN_SHEET_ID,
268
+ credentials: { clientId, clientSecret, redirectUri },
269
+ tokens: oauthTokens,
270
+ onSchemaMismatch: 'warn', // or 'error' or 'auto-sync'
271
+ });
272
+ ```
273
+
274
+ | Mode | Behaviour |
275
+ |------|-----------|
276
+ | `'warn'` | Log a warning to stderr and continue — safe for production rollouts |
277
+ | `'error'` | Throw `SchemaMismatchError` — useful in staging to hard-fail stale clients |
278
+ | `'auto-sync'` | Silently sync the actor sheet and update the version record before proceeding |
279
+
280
+ When you push a schema change, run `sheet-db sync --all-users` to propagate it to every registered user sheet and update the version records in one go:
281
+
282
+ ```bash
283
+ # Push schema changes to all user sheets
284
+ npx sheet-db sync --all-users
285
+
286
+ # Preview what would change without applying
287
+ npx sheet-db sync --all-users --dry-run
288
+ ```
289
+
231
290
  ### Integrating into an Existing Project
232
291
  If you already have a working backend (e.g., Express, NestJS), you can safely inject `longcelot-sheet-db` without ripping out your framework:
233
292
 
@@ -318,7 +377,26 @@ npx sheet-db sync
318
377
  # bunx sheet-db sync
319
378
  ```
320
379
 
321
- Creates missing sheets and adds missing columns (never deletes data).
380
+ Creates missing sheets and adds missing columns (never deletes data). Iterates **all actors** defined in `sheet-db.config.ts` and prints a per-actor status table:
381
+
382
+ ```
383
+ Actor │ Sheet ID │ Tables │ Status
384
+ ───────────┼────────────────────────────┼──────────┼────────────
385
+ admin │ 1ABCyourAdminSheetId │ 3 │ ✅ synced
386
+ student │ 1DEFyourStudentSheetId │ 5 │ ✅ synced
387
+ teacher │ (not set) │ 4 │ ⚠ skipped
388
+ ```
389
+
390
+ Actors whose sheet ID env var is not set are skipped with a warning (non-fatal).
391
+
392
+ **`--all-users`** — after syncing dev actor sheets, reads all rows from the admin `users` table and pushes any missing columns/tables to every registered user sheet. Updates the `schema_versions` record for each one. Uses exponential backoff (1s → 32s) to handle Google Sheets API rate limits.
393
+
394
+ **`--dry-run`** — combine with `--all-users` to preview which user sheets are outdated without writing any changes:
395
+
396
+ ```bash
397
+ npx sheet-db sync --all-users # apply
398
+ npx sheet-db sync --all-users --dry-run # preview only
399
+ ```
322
400
 
323
401
  ### Validate Schemas
324
402
 
@@ -404,10 +482,9 @@ const { valid, errors } = validatePasswordStrength('password');
404
482
 
405
483
  ### Central Admin Sheet
406
484
 
407
- - `users` - User registry
485
+ - `users` - User registry with `actor_sheet_id` per user
408
486
  - `credentials` - Authentication data
409
- - `actors` - Actor definitions
410
- - `permissions` - Role permissions (optional)
487
+ - `schema_versions` - Schema hash per `(actor_sheet_id, table_name)` — used for mismatch detection
411
488
 
412
489
  ### User-Owned Sheets
413
490
 
@@ -1,17 +1,20 @@
1
1
  import { SheetClient } from './sheetClient';
2
- import { TableSchema, FindOptions, UpdateOptions, DeleteOptions } from '../schema/types';
2
+ import { TableSchema, FindOptions, UpdateOptions, DeleteOptions, CreateOptions, FKResolver } from '../schema/types';
3
3
  export declare class CRUDOperations {
4
4
  private client;
5
5
  private spreadsheetId;
6
6
  private schema;
7
- constructor(client: SheetClient, spreadsheetId: string, schema: TableSchema);
8
- create(data: Record<string, any>): Promise<Record<string, any>>;
7
+ private fkResolver?;
8
+ private preFlight?;
9
+ constructor(client: SheetClient, spreadsheetId: string, schema: TableSchema, fkResolver?: FKResolver | undefined, preFlight?: Promise<void> | undefined);
10
+ create(data: Record<string, any>, options?: CreateOptions): Promise<Record<string, any>>;
9
11
  findMany(options?: FindOptions): Promise<Record<string, any>[]>;
10
12
  findOne(options?: FindOptions): Promise<Record<string, any> | null>;
11
13
  update(options: UpdateOptions): Promise<number>;
12
14
  delete(options: DeleteOptions): Promise<number>;
13
15
  private getHeaders;
14
16
  private validateAndApplyDefaults;
17
+ private validateForeignKeys;
15
18
  private serializeValue;
16
19
  private deserializeRow;
17
20
  private checkUniqueness;
@@ -1 +1 @@
1
- {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/adapter/crud.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGzF,qBAAa,cAAc;IAEvB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,MAAM;gBAFN,MAAM,EAAE,WAAW,EACnB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,WAAW;IAGvB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAqB/D,QAAQ,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;IAmCnE,OAAO,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAKvE,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAiC/C,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;YA6BvC,UAAU;IAYxB,OAAO,CAAC,wBAAwB;IAiDhC,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,cAAc;YAuCR,eAAe;IAgB7B,OAAO,CAAC,YAAY;CAQrB"}
1
+ {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/adapter/crud.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,WAAW,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACX,MAAM,iBAAiB,CAAC;AAGzB,qBAAa,cAAc;IAEvB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,UAAU,CAAC;IACnB,OAAO,CAAC,SAAS,CAAC;gBAJV,MAAM,EAAE,WAAW,EACnB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,WAAW,EACnB,UAAU,CAAC,EAAE,UAAU,YAAA,EACvB,SAAS,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,YAAA;IAG7B,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAoC5F,QAAQ,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;IAoCnE,OAAO,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAKvE,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IA4C/C,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;YA+BvC,UAAU;IAYxB,OAAO,CAAC,wBAAwB;YAiDlB,mBAAmB;IAmBjC,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,cAAc;YAuCR,eAAe;IAgB7B,OAAO,CAAC,YAAY;CAQrB"}
@@ -4,15 +4,30 @@ exports.CRUDOperations = void 0;
4
4
  const nanoid_1 = require("nanoid");
5
5
  const ValidationError_1 = require("../errors/ValidationError");
6
6
  class CRUDOperations {
7
- constructor(client, spreadsheetId, schema) {
7
+ constructor(client, spreadsheetId, schema, fkResolver, preFlight) {
8
8
  this.client = client;
9
9
  this.spreadsheetId = spreadsheetId;
10
10
  this.schema = schema;
11
+ this.fkResolver = fkResolver;
12
+ this.preFlight = preFlight;
11
13
  }
12
- async create(data) {
14
+ async create(data, options = {}) {
15
+ if (this.preFlight)
16
+ await this.preFlight;
17
+ let incoming = { ...data };
18
+ // Auto-generate string PK if not supplied
19
+ if (this.schema.pkColumn) {
20
+ const pkDef = this.schema.columns[this.schema.pkColumn];
21
+ if (pkDef?.type === 'string' && (incoming[this.schema.pkColumn] === undefined || incoming[this.schema.pkColumn] === null)) {
22
+ incoming[this.schema.pkColumn] = (0, nanoid_1.nanoid)();
23
+ }
24
+ }
13
25
  // Generate _id before validation so the required-_id check always passes
14
- const dataWithId = { _id: (0, nanoid_1.nanoid)(), ...data };
26
+ const dataWithId = { _id: (0, nanoid_1.nanoid)(), ...incoming };
15
27
  const validated = this.validateAndApplyDefaults(dataWithId, 'create');
28
+ if (!options.skipFKValidation) {
29
+ await this.validateForeignKeys(validated);
30
+ }
16
31
  await this.checkUniqueness(validated, null);
17
32
  if (this.schema.timestamps) {
18
33
  const now = new Date().toISOString();
@@ -25,6 +40,8 @@ class CRUDOperations {
25
40
  return validated;
26
41
  }
27
42
  async findMany(options = {}) {
43
+ if (this.preFlight)
44
+ await this.preFlight;
28
45
  const rows = await this.client.getAllRows(this.spreadsheetId, this.schema.name);
29
46
  if (rows.length === 0)
30
47
  return [];
@@ -56,6 +73,8 @@ class CRUDOperations {
56
73
  return results[0] || null;
57
74
  }
58
75
  async update(options) {
76
+ if (this.preFlight)
77
+ await this.preFlight;
59
78
  const rows = await this.client.getAllRows(this.spreadsheetId, this.schema.name);
60
79
  if (rows.length === 0)
61
80
  return 0;
@@ -65,7 +84,15 @@ class CRUDOperations {
65
84
  for (let i = 0; i < dataRows.length; i++) {
66
85
  const item = this.deserializeRow(headers, dataRows[i]);
67
86
  if (this.matchesWhere(item, options.where)) {
68
- const validated = this.validateAndApplyDefaults(options.data, 'update');
87
+ // Strip pkColumn silently — PK is readonly on update
88
+ const updateData = { ...options.data };
89
+ if (this.schema.pkColumn && this.schema.pkColumn in updateData) {
90
+ delete updateData[this.schema.pkColumn];
91
+ }
92
+ const validated = this.validateAndApplyDefaults(updateData, 'update');
93
+ if (!options.skipFKValidation) {
94
+ await this.validateForeignKeys(validated);
95
+ }
69
96
  await this.checkUniqueness(validated, item._id);
70
97
  if (this.schema.timestamps) {
71
98
  validated._updated_at = new Date().toISOString();
@@ -79,10 +106,13 @@ class CRUDOperations {
79
106
  return updated;
80
107
  }
81
108
  async delete(options) {
109
+ if (this.preFlight)
110
+ await this.preFlight;
82
111
  if (this.schema.softDelete) {
83
112
  return await this.update({
84
113
  where: options.where,
85
114
  data: { _deleted_at: new Date().toISOString() },
115
+ skipFKValidation: true,
86
116
  });
87
117
  }
88
118
  const rows = await this.client.getAllRows(this.spreadsheetId, this.schema.name);
@@ -150,6 +180,22 @@ class CRUDOperations {
150
180
  }
151
181
  return result;
152
182
  }
183
+ async validateForeignKeys(data) {
184
+ if (!this.fkResolver)
185
+ return;
186
+ for (const [columnName, column] of Object.entries(this.schema.columns)) {
187
+ if (!column.ref)
188
+ continue;
189
+ const value = data[columnName];
190
+ if (value === undefined || value === null)
191
+ continue;
192
+ const [refTable, refColumn] = column.ref.split('.');
193
+ const exists = await this.fkResolver(refTable, refColumn, value);
194
+ if (!exists) {
195
+ throw new ValidationError_1.ValidationError(`FK violation: ${refTable}.${refColumn} '${value}' does not exist`, columnName);
196
+ }
197
+ }
198
+ }
153
199
  serializeValue(value) {
154
200
  if (value === null || value === undefined)
155
201
  return '';
@@ -1 +1 @@
1
- {"version":3,"file":"crud.js","sourceRoot":"","sources":["../../src/adapter/crud.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"crud.js","sourceRoot":"","sources":["../../src/adapter/crud.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { SheetClient } from './sheetClient';
2
2
  import { CRUDOperations } from './crud';
3
- import { TableSchema, UserContext } from '../schema/types';
3
+ import { TableSchema, UserContext, SchemaMismatchBehaviour } from '../schema/types';
4
4
  export interface SheetAdapterConfig {
5
5
  adminSheetId: string;
6
6
  credentials: {
@@ -8,13 +8,17 @@ export interface SheetAdapterConfig {
8
8
  clientSecret: string;
9
9
  redirectUri: string;
10
10
  };
11
- tokens: any;
11
+ tokens: unknown;
12
+ onSchemaMismatch?: SchemaMismatchBehaviour;
12
13
  }
13
14
  export declare class SheetAdapter {
14
15
  private client;
15
16
  private adminSheetId;
16
17
  private schemas;
17
18
  private context?;
19
+ private onSchemaMismatch?;
20
+ /** Pending schema version check promise set by withContext() */
21
+ private _pendingSchemaCheck?;
18
22
  constructor(config: SheetAdapterConfig);
19
23
  registerSchema(schema: TableSchema): void;
20
24
  registerSchemas(schemas: TableSchema[]): void;
@@ -22,6 +26,17 @@ export declare class SheetAdapter {
22
26
  table(tableName: string): CRUDOperations;
23
27
  createUserSheet(userId: string, role: string, email: string): Promise<string>;
24
28
  syncSchema(schema: TableSchema): Promise<void>;
29
+ upsertSchemaVersion(actorSheetId: string, tableName: string, schemaHash: string, columnCount: number): Promise<void>;
30
+ getSchemaVersion(actorSheetId: string, tableName: string): Promise<{
31
+ schema_hash: string;
32
+ synced_at: string;
33
+ column_count: number;
34
+ } | null>;
35
+ private _svCrud;
36
+ private _ensureSchemaVersionsSheet;
37
+ private _doSchemaVersionCheck;
38
+ private createFKResolver;
39
+ private detectCircularRefs;
25
40
  private resolveSpreadsheetId;
26
41
  private hasPermission;
27
42
  private sheetExists;
@@ -1 +1 @@
1
- {"version":3,"file":"sheetAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/sheetAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI3D,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,GAAG,CAAC;CACb;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,OAAO,CAAC,CAAc;gBAElB,MAAM,EAAE,kBAAkB;IAKtC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAIzC,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;IAI7C,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY;IAM/C,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc;IAgBlC,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA0B7E,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBpD,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,aAAa;YAoBP,WAAW;IAMzB,SAAS,IAAI,WAAW;CAGzB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAE3E"}
1
+ {"version":3,"file":"sheetAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/sheetAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAc,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAsBhG,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE;QACX,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,OAAO,CAAC;IAChB,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;CAC5C;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,gBAAgB,CAAC,CAA0B;IACnD,gEAAgE;IAChE,OAAO,CAAC,mBAAmB,CAAC,CAAgB;gBAEhC,MAAM,EAAE,kBAAkB;IAQtC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI;IAKzC,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI;IAI7C,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,YAAY;IAY/C,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc;IAiBlC,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA0B7E,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9C,mBAAmB,CACvB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAyBV,gBAAgB,CACpB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAkBnF,OAAO,CAAC,OAAO;YAID,0BAA0B;YAY1B,qBAAqB;IAqCnC,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,kBAAkB;IA0C1B,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,aAAa;YAoBP,WAAW;IAMzB,SAAS,IAAI,WAAW;CAGzB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAE3E"}
@@ -6,14 +6,35 @@ const sheetClient_1 = require("./sheetClient");
6
6
  const crud_1 = require("./crud");
7
7
  const PermissionError_1 = require("../errors/PermissionError");
8
8
  const SchemaError_1 = require("../errors/SchemaError");
9
+ const SchemaMismatchError_1 = require("../errors/SchemaMismatchError");
10
+ const schemaHash_1 = require("../utils/schemaHash");
11
+ const defineTable_1 = require("../schema/defineTable");
12
+ const columnBuilder_1 = require("../schema/columnBuilder");
13
+ // Built-in admin table — tracks schema versions per actor sheet
14
+ const SCHEMA_VERSIONS_SCHEMA = (0, defineTable_1.defineTable)({
15
+ name: 'schema_versions',
16
+ actor: 'admin',
17
+ columns: {
18
+ schema_version_id: (0, columnBuilder_1.string)().primary(),
19
+ actor_sheet_id: (0, columnBuilder_1.string)().required(),
20
+ table_name: (0, columnBuilder_1.string)().required(),
21
+ schema_hash: (0, columnBuilder_1.string)().required(),
22
+ synced_at: (0, columnBuilder_1.string)().required(),
23
+ column_count: (0, columnBuilder_1.number)().required(),
24
+ },
25
+ });
9
26
  class SheetAdapter {
10
27
  constructor(config) {
11
28
  this.schemas = new Map();
12
- this.client = new sheetClient_1.SheetClient(config.credentials, config.tokens);
29
+ // Allow test injection via _client (cast through unknown for type safety)
30
+ this.client = config._client
31
+ ?? new sheetClient_1.SheetClient(config.credentials, config.tokens);
13
32
  this.adminSheetId = config.adminSheetId;
33
+ this.onSchemaMismatch = config.onSchemaMismatch;
14
34
  }
15
35
  registerSchema(schema) {
16
36
  this.schemas.set(schema.name, schema);
37
+ this.detectCircularRefs();
17
38
  }
18
39
  registerSchemas(schemas) {
19
40
  schemas.forEach((schema) => this.registerSchema(schema));
@@ -21,6 +42,10 @@ class SheetAdapter {
21
42
  withContext(context) {
22
43
  const newAdapter = Object.create(this);
23
44
  newAdapter.context = context;
45
+ newAdapter._pendingSchemaCheck = undefined;
46
+ if (this.onSchemaMismatch && context.actorSheetId && context.role !== 'admin') {
47
+ newAdapter._pendingSchemaCheck = newAdapter._doSchemaVersionCheck(context);
48
+ }
24
49
  return newAdapter;
25
50
  }
26
51
  table(tableName) {
@@ -32,7 +57,8 @@ class SheetAdapter {
32
57
  if (!this.hasPermission(schema)) {
33
58
  throw new PermissionError_1.PermissionError(`User does not have permission to access ${tableName}`, this.context?.role);
34
59
  }
35
- return new crud_1.CRUDOperations(this.client, spreadsheetId, schema);
60
+ const fkResolver = this.createFKResolver();
61
+ return new crud_1.CRUDOperations(this.client, spreadsheetId, schema, fkResolver, this._pendingSchemaCheck);
36
62
  }
37
63
  async createUserSheet(userId, role, email) {
38
64
  const sheetId = await this.client.createSpreadsheet(`${role}-${userId}`);
@@ -70,6 +96,132 @@ class SheetAdapter {
70
96
  }
71
97
  }
72
98
  }
99
+ // ── Schema version tracking ───────────────────────────────────────────────
100
+ async upsertSchemaVersion(actorSheetId, tableName, schemaHash, columnCount) {
101
+ try {
102
+ await this._ensureSchemaVersionsSheet();
103
+ const crud = this._svCrud();
104
+ const existing = await crud.findOne({
105
+ where: { actor_sheet_id: actorSheetId, table_name: tableName },
106
+ });
107
+ const synced_at = new Date().toISOString();
108
+ if (existing) {
109
+ await crud.update({
110
+ where: { actor_sheet_id: actorSheetId, table_name: tableName },
111
+ data: { schema_hash: schemaHash, synced_at, column_count: columnCount },
112
+ skipFKValidation: true,
113
+ });
114
+ }
115
+ else {
116
+ await crud.create({ actor_sheet_id: actorSheetId, table_name: tableName, schema_hash: schemaHash, synced_at, column_count: columnCount }, { skipFKValidation: true });
117
+ }
118
+ }
119
+ catch {
120
+ // Silently ignore version tracking errors to not break CRUD operations
121
+ }
122
+ }
123
+ async getSchemaVersion(actorSheetId, tableName) {
124
+ try {
125
+ await this._ensureSchemaVersionsSheet();
126
+ const crud = this._svCrud();
127
+ const record = await crud.findOne({ where: { actor_sheet_id: actorSheetId, table_name: tableName } });
128
+ if (!record)
129
+ return null;
130
+ return {
131
+ schema_hash: record.schema_hash,
132
+ synced_at: record.synced_at,
133
+ column_count: record.column_count,
134
+ };
135
+ }
136
+ catch {
137
+ return null;
138
+ }
139
+ }
140
+ // ── Internal helpers ──────────────────────────────────────────────────────
141
+ _svCrud() {
142
+ return new crud_1.CRUDOperations(this.client, this.adminSheetId, SCHEMA_VERSIONS_SCHEMA);
143
+ }
144
+ async _ensureSchemaVersionsSheet() {
145
+ const sheets = await this.client.getSheetNames(this.adminSheetId);
146
+ if (!sheets.includes('schema_versions')) {
147
+ await this.client.addSheet(this.adminSheetId, 'schema_versions');
148
+ await this.client.writeHeader(this.adminSheetId, 'schema_versions', Object.keys(SCHEMA_VERSIONS_SCHEMA.columns));
149
+ }
150
+ }
151
+ async _doSchemaVersionCheck(context) {
152
+ if (!context.actorSheetId)
153
+ return;
154
+ const roleSchemas = Array.from(this.schemas.values()).filter((s) => s.actor === context.role && s.name !== 'schema_versions');
155
+ for (const schema of roleSchemas) {
156
+ const currentHash = (0, schemaHash_1.computeSchemaHash)(schema);
157
+ const stored = await this.getSchemaVersion(context.actorSheetId, schema.name);
158
+ if (!stored || stored.schema_hash !== currentHash) {
159
+ switch (this.onSchemaMismatch) {
160
+ case 'warn':
161
+ console.warn(`[sheet-db] Schema mismatch: table '${schema.name}' on sheet '${context.actorSheetId}' ` +
162
+ `is outdated. Run 'sheet-db sync --all-users' to fix.`);
163
+ break;
164
+ case 'error':
165
+ throw new SchemaMismatchError_1.SchemaMismatchError(schema.name, context.actorSheetId);
166
+ case 'auto-sync':
167
+ await this.syncSchema(schema);
168
+ await this.upsertSchemaVersion(context.actorSheetId, schema.name, currentHash, Object.keys(schema.columns).length);
169
+ break;
170
+ }
171
+ }
172
+ }
173
+ }
174
+ createFKResolver() {
175
+ return async (tableName, columnName, value) => {
176
+ const refSchema = this.schemas.get(tableName);
177
+ if (!refSchema) {
178
+ throw new SchemaError_1.SchemaError(`Referenced table '${tableName}' is not registered`, tableName);
179
+ }
180
+ const refSpreadsheetId = this.resolveSpreadsheetId(refSchema);
181
+ const refCrud = new crud_1.CRUDOperations(this.client, refSpreadsheetId, refSchema);
182
+ const row = await refCrud.findOne({ where: { [columnName]: value } });
183
+ return row !== null;
184
+ };
185
+ }
186
+ detectCircularRefs() {
187
+ const adj = new Map();
188
+ for (const [name, schema] of this.schemas) {
189
+ const deps = new Set();
190
+ for (const col of Object.values(schema.columns)) {
191
+ if (col.ref) {
192
+ const [refTable] = col.ref.split('.');
193
+ deps.add(refTable);
194
+ }
195
+ }
196
+ adj.set(name, deps);
197
+ }
198
+ const visited = new Set();
199
+ const inStack = new Set();
200
+ const dfs = (node) => {
201
+ visited.add(node);
202
+ inStack.add(node);
203
+ for (const neighbor of adj.get(node) ?? new Set()) {
204
+ if (!adj.has(neighbor))
205
+ continue;
206
+ if (!visited.has(neighbor)) {
207
+ if (dfs(neighbor))
208
+ return true;
209
+ }
210
+ else if (inStack.has(neighbor)) {
211
+ return true;
212
+ }
213
+ }
214
+ inStack.delete(node);
215
+ return false;
216
+ };
217
+ for (const name of adj.keys()) {
218
+ if (!visited.has(name)) {
219
+ if (dfs(name)) {
220
+ throw new SchemaError_1.SchemaError('Circular reference detected in schema definitions');
221
+ }
222
+ }
223
+ }
224
+ }
73
225
  resolveSpreadsheetId(schema) {
74
226
  if (schema.actor === 'admin') {
75
227
  return this.adminSheetId;