@b2bc-devkit/sheetorm 0.1.0 → 1.0.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/{readme.md → README.md} +59 -159
- package/dist/npm/core/Registry.d.ts.map +1 -1
- package/dist/npm/core/Registry.js +7 -8
- package/dist/npm/core/Registry.js.map +1 -1
- package/dist/npm/core/SheetRepository.d.ts +6 -6
- package/dist/npm/core/SheetRepository.d.ts.map +1 -1
- package/dist/npm/core/SheetRepository.js +12 -12
- package/dist/npm/core/SheetRepository.js.map +1 -1
- package/dist/npm/core/types/ISheetAdapter.d.ts +2 -3
- package/dist/npm/core/types/ISheetAdapter.d.ts.map +1 -1
- package/dist/npm/core/types/ISpreadsheetAdapter.d.ts +2 -3
- package/dist/npm/core/types/ISpreadsheetAdapter.d.ts.map +1 -1
- package/dist/npm/index/IndexStore.d.ts +6 -8
- package/dist/npm/index/IndexStore.d.ts.map +1 -1
- package/dist/npm/index/IndexStore.js +13 -15
- package/dist/npm/index/IndexStore.js.map +1 -1
- package/dist/npm/storage/GoogleSheetAdapter.d.ts +3 -4
- package/dist/npm/storage/GoogleSheetAdapter.d.ts.map +1 -1
- package/dist/npm/storage/GoogleSheetAdapter.js +3 -4
- package/dist/npm/storage/GoogleSheetAdapter.js.map +1 -1
- package/license.md +471 -14
- package/package.json +9 -1
package/{readme.md → README.md}
RENAMED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
# SheetORM
|
|
2
2
|
|
|
3
|
+
<p>
|
|
4
|
+
<img src="assets/logo.png" alt="SheetORM logo" width="100%" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
A TypeScript ORM for Google Sheets running in Google Apps Script (GAS). SheetORM brings a structured,
|
|
4
8
|
type-safe persistence layer to spreadsheet-based applications with an **ActiveRecord** API — define a class,
|
|
5
9
|
extend `Record`, and everything just works.
|
|
6
10
|
|
|
11
|
+
<img src="assets/demo.gif" alt="SheetORM demo" />
|
|
12
|
+
|
|
7
13
|
## Features
|
|
8
14
|
|
|
9
15
|
- **ActiveRecord pattern** — Extend `Record`, define fields, and call `save()` / `find()` / `delete()`
|
|
@@ -207,40 +213,11 @@ Car.groupBy("make");
|
|
|
207
213
|
|
|
208
214
|
See [`examples/cars-crud.ts`](examples/cars-crud.ts) for a complete runnable example.
|
|
209
215
|
|
|
210
|
-
## Publishing (Apps Script & npm)
|
|
211
|
-
|
|
212
|
-
### Apps Script library
|
|
213
|
-
|
|
214
|
-
- Prerequisites: `npm run login` (clasp), `appsscript.json` configured for your script, and a Script ID to share.
|
|
215
|
-
- Run `npm run publish:gas` (or VS Code task “Publish: Apps Script library”). It lints, tests, builds, pushes via `clasp push -f`, creates a new version with `GAS_VERSION_MESSAGE` (default: “SheetORM Apps Script release”), and, if `GAS_DEPLOYMENT_ID` is set, deploys with that description.
|
|
216
|
-
- In the Apps Script editor, add the library by Script ID. Use `GasEntrypoints.Record`, `GasEntrypoints.Query`, and `GasEntrypoints.Decorators` in your GAS project; callable menu functions stay limited to `runTests`, `validateTests`, and `runBenchmark`.
|
|
217
|
-
|
|
218
|
-
### npm package
|
|
219
|
-
|
|
220
|
-
- Build distributable JS + type definitions with `npm run build:npm` (emits to `dist/npm`).
|
|
221
|
-
- Vite stays dedicated to the GAS bundle; the npm build is plain TypeScript output so consumers can use their own bundler.
|
|
222
|
-
- Publish with `node scripts/publish-npm.mjs` (or VS Code task “Publish: npm package”); set `NPM_TAG` to publish under a dist-tag if needed. The script runs lint, tests, build, then `npm publish --access public`.
|
|
223
|
-
- Import via the bundled aggregator:
|
|
224
|
-
|
|
225
|
-
```ts
|
|
226
|
-
import { SheetOrm } from "sheetorm";
|
|
227
|
-
|
|
228
|
-
const { Record, Query, Decorators } = SheetOrm;
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
- Or import individual surfaces directly: `import { Record } from "sheetorm/core/Record";`, `import { Query } from "sheetorm/query/Query";`, `import { Decorators } from "sheetorm/core/Decorators";`.
|
|
232
|
-
|
|
233
|
-
### VS Code tasks
|
|
234
|
-
|
|
235
|
-
Two tasks are available in `.vscode/tasks.json`:
|
|
236
|
-
|
|
237
|
-
- `Publish: Apps Script library` → runs `npm run publish:gas`
|
|
238
|
-
- `Publish: npm package` → runs `npm run publish:npm`
|
|
239
|
-
|
|
240
216
|
## Architecture
|
|
241
217
|
|
|
242
218
|
```
|
|
243
219
|
src/
|
|
220
|
+
core/Decorators.ts — @Field, @Indexed, @Required property decorators
|
|
244
221
|
core/Record.ts — ActiveRecord base class (primary API); tableName = tbl_{Name}s, indexTableName = idx_{Name}s
|
|
245
222
|
core/RecordConstructor.ts — Constructor contract for Record subclasses
|
|
246
223
|
core/RecordStatic.ts — Registry-facing static Record contract
|
|
@@ -269,11 +246,10 @@ src/
|
|
|
269
246
|
index/IndexStore.ts — Secondary index management
|
|
270
247
|
storage/GoogleSheetAdapter.ts — Native GAS sheet wrapper
|
|
271
248
|
storage/GoogleSpreadsheetAdapter.ts — Native GAS spreadsheet wrapper
|
|
272
|
-
storage/SheetsAPIv4SheetAdapter.ts — Buffered Sheets API v4 sheet wrapper
|
|
273
|
-
storage/SheetsAPIv4SpreadsheetAdapter.ts — Buffered Sheets API v4 spreadsheet wrapper
|
|
274
249
|
core/cache/MemoryCache.ts — In-memory cache implementation
|
|
275
250
|
utils/Uuid.ts — UUID v4 generation (GAS / fallback)
|
|
276
251
|
utils/Serialization.ts — Row ↔ Entity conversion
|
|
252
|
+
utils/SheetOrmLogger.ts — Verbose logger for API-call tracing
|
|
277
253
|
testing/ParityCatalog.ts — Canonical Jest ↔ runtime test case list
|
|
278
254
|
testing/RuntimeParity.ts — GAS runtime parity suite
|
|
279
255
|
testing/RuntimeBenchmark.ts — GAS runtime benchmark (Cars + Workers, 1 000 records)
|
|
@@ -283,18 +259,6 @@ examples/
|
|
|
283
259
|
cars-crud.ts — Full ActiveRecord example
|
|
284
260
|
```
|
|
285
261
|
|
|
286
|
-
## Code Organization Rules
|
|
287
|
-
|
|
288
|
-
Repository conventions are intentionally strict:
|
|
289
|
-
|
|
290
|
-
- one exported class / interface / type / enum per file
|
|
291
|
-
- no barrel files and no multi-export "grab bag" modules
|
|
292
|
-
- exported free functions are not allowed; behavior should live on classes as static methods
|
|
293
|
-
- filenames must match the exported artifact name (for example `Record.ts`, `QueryOptions.ts`, `SystemColumns.ts`)
|
|
294
|
-
- internal helpers may stay private to a file, but the file's public surface must stay singular and obvious
|
|
295
|
-
|
|
296
|
-
When adding new files, follow these rules first and treat them as part of the repository contract.
|
|
297
|
-
|
|
298
262
|
## API Reference
|
|
299
263
|
|
|
300
264
|
### Record (ActiveRecord base class)
|
|
@@ -419,19 +383,21 @@ const ids = indexStore.searchCombined("idx_Cars", "model", "320i");
|
|
|
419
383
|
npm test
|
|
420
384
|
```
|
|
421
385
|
|
|
422
|
-
Runs **
|
|
386
|
+
Runs **333 unit and benchmark tests** across 11 test suites using Jest + ts-jest with in-memory mock adapters:
|
|
423
387
|
|
|
424
388
|
| Suite | Tests | Description |
|
|
425
389
|
| -------------------------- | ----- | -------------------------------------------- |
|
|
426
|
-
| `record.test.ts` |
|
|
427
|
-
| `query-engine.test.ts` |
|
|
428
|
-
| `serialization.test.ts` |
|
|
429
|
-
| `
|
|
430
|
-
| `
|
|
431
|
-
| `
|
|
432
|
-
| `
|
|
390
|
+
| `record.test.ts` | 74 | ActiveRecord API (save, find, query, Query) |
|
|
391
|
+
| `query-engine.test.ts` | 60 | Filter, sort, paginate, group |
|
|
392
|
+
| `serialization.test.ts` | 47 | Row ↔ Entity conversion |
|
|
393
|
+
| `index-store.test.ts` | 44 | Secondary index CRUD |
|
|
394
|
+
| `query.test.ts` | 41 | Fluent query API |
|
|
395
|
+
| `sheet-repository.test.ts` | 35 | SheetRepository CRUD, batch, hooks |
|
|
396
|
+
| `cache.test.ts` | 16 | MemoryCache TTL behavior |
|
|
397
|
+
| `benchmark.test.ts` | 6 | 1 000-record perf benchmark: Cars vs Workers |
|
|
398
|
+
| `uuid.test.ts` | 5 | UUID generation |
|
|
433
399
|
| `parity-validator.test.ts` | 3 | Jest ↔ GAS runtime parity check |
|
|
434
|
-
| `
|
|
400
|
+
| `sheetorm.test.ts` | 2 | npm entry point smoke tests |
|
|
435
401
|
|
|
436
402
|
### Benchmark Tests (`benchmark.test.ts`)
|
|
437
403
|
|
|
@@ -482,7 +448,7 @@ npm test
|
|
|
482
448
|
|
|
483
449
|
Run in Google Apps Script (real Sheets API):
|
|
484
450
|
|
|
485
|
-
- `
|
|
451
|
+
- `runTestsStageOne()` / `runTestsStageTwo()` / `runTestsStageThree()` — staged runtime parity suite
|
|
486
452
|
- `validateTests()` — validates mapping only (fast drift check)
|
|
487
453
|
|
|
488
454
|
### GAS Runtime Benchmark
|
|
@@ -497,25 +463,21 @@ Run in Google Apps Script (real Sheets API):
|
|
|
497
463
|
|
|
498
464
|
## Exposed GAS Functions
|
|
499
465
|
|
|
500
|
-
|
|
466
|
+
The following callable functions are surfaced as GAS globals (visible in the Apps Script editor Run menu and
|
|
501
467
|
callable as triggers). Everything else is an internal implementation detail bundled into `Code.js`.
|
|
502
468
|
|
|
503
|
-
| Function
|
|
504
|
-
|
|
|
505
|
-
| `
|
|
506
|
-
| `
|
|
507
|
-
| `
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
3. Production build (`npm run build`)
|
|
516
|
-
4. Verify `Code.js` output exists
|
|
517
|
-
|
|
518
|
-
Matrix: Node 18, 20, 22.
|
|
469
|
+
| Function | Purpose |
|
|
470
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------ |
|
|
471
|
+
| `runTestsStageOne()` | Stage-one parity tests (basic CRUD + query) against the active spreadsheet. |
|
|
472
|
+
| `runTestsStageTwo()` | Stage-two parity tests (indexes + search). |
|
|
473
|
+
| `runTestsStageThree()` | Stage-three parity tests (advanced queries + pagination). |
|
|
474
|
+
| `validateTests()` | Checks Jest ↔ GAS handler mapping parity — no Sheets API calls, fails immediately on drift. |
|
|
475
|
+
| `runBenchmark()` | Write/read/query/delete cycle for Cars + Workers (100 records each); logs per-operation timings. |
|
|
476
|
+
| `removeAllSheets()` | Deletes every sheet in the active spreadsheet (destructive — use with caution). |
|
|
477
|
+
| `demoCreate()` | Creates 5 sample DemoCar records in the sheet. |
|
|
478
|
+
| `demoRead()` | Reads and logs DemoCar records using find/query/where. |
|
|
479
|
+
| `demoUpdate()` | Updates existing DemoCar records and logs changes. |
|
|
480
|
+
| `demoDelete()` | Deletes DemoCar records and verifies removal. |
|
|
519
481
|
|
|
520
482
|
## Available Scripts
|
|
521
483
|
|
|
@@ -526,8 +488,6 @@ Matrix: Node 18, 20, 22.
|
|
|
526
488
|
| `npm run lint` | Lint with ESLint |
|
|
527
489
|
| `npm run format` | Format with Prettier |
|
|
528
490
|
| `npm run login` | Authenticate with Google Apps Script (once) |
|
|
529
|
-
| `npm run push` | Build + push to GAS |
|
|
530
|
-
| `npm run deploy` | Build + push + create versioned deployment |
|
|
531
491
|
|
|
532
492
|
## Sheet Layout
|
|
533
493
|
|
|
@@ -543,18 +503,22 @@ Special sheets:
|
|
|
543
503
|
|
|
544
504
|
## Storage Adapters
|
|
545
505
|
|
|
546
|
-
SheetORM separates sheet I/O behind two interfaces defined in `src/core/types/ISheetAdapter.ts` and
|
|
506
|
+
SheetORM separates sheet I/O behind two interfaces defined in `src/core/types/ISheetAdapter.ts` and
|
|
507
|
+
`src/core/types/ISpreadsheetAdapter.ts`:
|
|
547
508
|
|
|
548
|
-
- **`ISpreadsheetAdapter`** — spreadsheet-level operations: `getSheetByName`, `createSheet`, `deleteSheet`,
|
|
549
|
-
|
|
509
|
+
- **`ISpreadsheetAdapter`** — spreadsheet-level operations: `getSheetByName`, `createSheet`, `deleteSheet`,
|
|
510
|
+
`getSheetNames`
|
|
511
|
+
- **`ISheetAdapter`** — sheet-level operations: read (`getAllData`, `getHeaders`, `getRow`, …), write
|
|
512
|
+
(`appendRows`, `writeRowsAt`, `updateRow`, …), `clear`, `flush`
|
|
550
513
|
|
|
551
|
-
|
|
514
|
+
One production adapter pair is provided:
|
|
552
515
|
|
|
553
516
|
### `GoogleSpreadsheetAdapter` / `GoogleSheetAdapter`
|
|
554
517
|
|
|
555
518
|
**Location**: `src/storage/GoogleSpreadsheetAdapter.ts`, `src/storage/GoogleSheetAdapter.ts`
|
|
556
519
|
|
|
557
|
-
Uses the native GAS `SpreadsheetApp` / `Sheet` objects directly. This is the default adapter — no
|
|
520
|
+
Uses the native GAS `SpreadsheetApp` / `Sheet` objects directly. This is the default adapter — no
|
|
521
|
+
configuration required.
|
|
558
522
|
|
|
559
523
|
```ts
|
|
560
524
|
import { GoogleSpreadsheetAdapter } from "./src/storage/GoogleSpreadsheetAdapter";
|
|
@@ -569,93 +533,29 @@ Registry.getInstance().configure({
|
|
|
569
533
|
});
|
|
570
534
|
```
|
|
571
535
|
|
|
572
|
-
| Property
|
|
573
|
-
|
|
|
574
|
-
| Write mechanism
|
|
575
|
-
| Calls per `saveAll`
|
|
576
|
-
| Flush required
|
|
577
|
-
| Quota impact
|
|
578
|
-
| Read operations
|
|
579
|
-
| GAS V8 latency
|
|
580
|
-
| Best for
|
|
581
|
-
|
|
582
|
-
### `SheetsAPIv4SpreadsheetAdapter` / `SheetsAPIv4SheetAdapter`
|
|
583
|
-
|
|
584
|
-
**Location**: `src/storage/SheetsAPIv4SpreadsheetAdapter.ts`, `src/storage/SheetsAPIv4SheetAdapter.ts`
|
|
585
|
-
|
|
586
|
-
Buffers all write operations (`writeRowsAt`, `appendRows`) in memory as A1-notation `ValueRange` objects and sends them all in a **single HTTP `spreadsheets.values.batchUpdate` request** when `flushAllPending()` is called. Read operations still use the native GAS API (the ORM reads from in-memory cache after `saveAll`, so extra sheet reads are rare).
|
|
587
|
-
|
|
588
|
-
```ts
|
|
589
|
-
import { SheetsAPIv4SpreadsheetAdapter } from "./src/storage/SheetsAPIv4SpreadsheetAdapter";
|
|
590
|
-
import { Registry } from "./src/core/Registry";
|
|
591
|
-
|
|
592
|
-
const v4 = new SheetsAPIv4SpreadsheetAdapter(); // uses active spreadsheet
|
|
593
|
-
Registry.getInstance().configure({ adapter: v4 });
|
|
594
|
-
|
|
595
|
-
MyRecord.saveAll(items); // buffers entity rows + index rows in memory
|
|
596
|
-
v4.flushAllPending(); // ONE HTTP batchUpdate — both sheets at once
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
| Property | Value |
|
|
600
|
-
| -------------------- | -------------------------------------------------------------------------- |
|
|
601
|
-
| Write mechanism | `UrlFetchApp.fetch()` → Sheets REST API v4 `values:batchUpdate` |
|
|
602
|
-
| Calls per `saveAll` | **1** — entity sheet + index sheet rows combined in one HTTP request |
|
|
603
|
-
| Flush required | **Yes** — call `v4.flushAllPending()` after each `saveAll` (or batch) |
|
|
604
|
-
| Auth | `ScriptApp.getOAuthToken()` — reuses the existing `spreadsheets` scope |
|
|
605
|
-
| Quota impact | `UrlFetchApp` quota: 20 000 calls/day per script |
|
|
606
|
-
| Read operations | Native `Range.getValues()` — unchanged |
|
|
607
|
-
| GAS V8 latency | 1 HTTP round-trip instead of N `setValues()` calls |
|
|
608
|
-
| Best for | Benchmarking / high-volume batch imports where minimising API calls matters |
|
|
609
|
-
|
|
610
|
-
**Required OAuth scopes** (already set in `appsscript.json`):
|
|
611
|
-
|
|
612
|
-
```json
|
|
613
|
-
"oauthScopes": [
|
|
614
|
-
"https://www.googleapis.com/auth/spreadsheets",
|
|
615
|
-
"https://www.googleapis.com/auth/script.external_request"
|
|
616
|
-
]
|
|
617
|
-
```
|
|
618
|
-
|
|
619
|
-
### Adapter comparison
|
|
620
|
-
|
|
621
|
-
| Criterion | `GoogleSpreadsheetAdapter` | `SheetsAPIv4SpreadsheetAdapter` |
|
|
622
|
-
| --------------------------- | :------------------------: | :-----------------------------: |
|
|
623
|
-
| Setup complexity | None | Requires `flushAllPending()` call |
|
|
624
|
-
| Write batching | 2 calls per `saveAll` | 1 HTTP call per flush |
|
|
625
|
-
| Flush step needed | No | Yes |
|
|
626
|
-
| UrlFetch daily quota used | No | Yes (1 per flush) |
|
|
627
|
-
| Works without internet auth | Yes (native GAS) | No (needs `UrlFetchApp`) |
|
|
628
|
-
| In-band error visibility | Immediate on write | Deferred to flush |
|
|
629
|
-
| Recommended default | ✅ Yes | Only for bulk-write optimisation |
|
|
630
|
-
|
|
631
|
-
The `GoogleSpreadsheetAdapter` is the default and recommended choice for all production use. The `SheetsAPIv4SpreadsheetAdapter` is provided as an opt-in for scenarios where reducing the number of GAS API calls is the primary concern (e.g. large batch imports exceeding GAS execution-time limits).
|
|
536
|
+
| Property | Value |
|
|
537
|
+
| ------------------- | ------------------------------------------------------------------- |
|
|
538
|
+
| Write mechanism | `Range.setValues()` — GAS native API |
|
|
539
|
+
| Calls per `saveAll` | **2** — one `setValues` for entity sheet + one for index sheet |
|
|
540
|
+
| Flush required | No — writes are synchronous and immediately visible |
|
|
541
|
+
| Quota impact | Counts against SpreadsheetApp call budget (no daily UrlFetch quota) |
|
|
542
|
+
| Read operations | Native `Range.getValues()` |
|
|
543
|
+
| GAS V8 latency | ~300–500 ms per `setValues` call on large ranges |
|
|
544
|
+
| Best for | All production use; default choice |
|
|
632
545
|
|
|
633
546
|
## Development Notes
|
|
634
547
|
|
|
635
548
|
### Exposing Functions to GAS
|
|
636
549
|
|
|
637
|
-
`src/index.ts`
|
|
638
|
-
|
|
639
|
-
functions so that Apps Script still shows them in the Run menu.
|
|
640
|
-
|
|
641
|
-
```ts
|
|
642
|
-
export class GasEntrypoints {
|
|
643
|
-
static runTests(): void {
|
|
644
|
-
RuntimeParity.run();
|
|
645
|
-
}
|
|
550
|
+
`src/index.ts` exports a single `GasEntrypoints` class. During bundling, the Vite GAS plugin maps its static
|
|
551
|
+
methods to GAS globals and emits matching stub functions so that Apps Script shows them in the Run menu.
|
|
646
552
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
static runBenchmark(): void {
|
|
652
|
-
RuntimeBenchmark.run();
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
```
|
|
553
|
+
GAS-callable methods include staged test runners (`runTestsStageOne`/`Two`/`Three`), `validateTests`,
|
|
554
|
+
`runBenchmark`, `removeAllSheets`, and four CRUD demos (`demoCreate`, `demoRead`, `demoUpdate`, `demoDelete`).
|
|
555
|
+
The class also exposes core API classes (`Record`, `Query`, `Decorators`, `IndexStore`, `Registry`,
|
|
556
|
+
`SheetOrmLogger`) as static readonly fields for use inside GAS scripts.
|
|
656
557
|
|
|
657
|
-
This keeps the source aligned with the repository rule of one public export per file
|
|
658
|
-
three callable GAS entrypoints.
|
|
558
|
+
This keeps the source aligned with the repository rule of one public export per file.
|
|
659
559
|
|
|
660
560
|
### Circular Dependencies
|
|
661
561
|
|
|
@@ -669,4 +569,4 @@ included in this template):
|
|
|
669
569
|
|
|
670
570
|
## License
|
|
671
571
|
|
|
672
|
-
GPL-
|
|
572
|
+
GPL-3.0-or-later — see [license.md](license.md).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Registry.d.ts","sourceRoot":"","sources":["../../../src/core/Registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD;;;;;GAKG;AACH,qBAAa,QAAQ;IACnB,6CAA6C;IAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAyB;IAEhD,qEAAqE;IACrE,OAAO,CAAC,OAAO,CAAoC;IACnD,4DAA4D;IAC5D,OAAO,CAAC,KAAK,CAA+B;IAC5C,sCAAsC;IACtC,OAAO,CAAC,UAAU,CAA2B;IAC7C,yCAAyC;IACzC,OAAO,CAAC,KAAK,CAA8C;IAC3D,mEAAmE;IACnE,OAAO,CAAC,cAAc,CAAmC;IACzD,yEAAyE;IACzE,OAAO,CAAC,aAAa,CAAmC;IAExD,0DAA0D;IAC1D,MAAM,CAAC,WAAW,IAAI,QAAQ;IAO9B,+EAA+E;IAC/E,MAAM,CAAC,KAAK,IAAI,IAAI;IAIpB;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,mBAAmB,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI;IAUnF;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"Registry.d.ts","sourceRoot":"","sources":["../../../src/core/Registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD;;;;;GAKG;AACH,qBAAa,QAAQ;IACnB,6CAA6C;IAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAyB;IAEhD,qEAAqE;IACrE,OAAO,CAAC,OAAO,CAAoC;IACnD,4DAA4D;IAC5D,OAAO,CAAC,KAAK,CAA+B;IAC5C,sCAAsC;IACtC,OAAO,CAAC,UAAU,CAA2B;IAC7C,yCAAyC;IACzC,OAAO,CAAC,KAAK,CAA8C;IAC3D,mEAAmE;IACnE,OAAO,CAAC,cAAc,CAAmC;IACzD,yEAAyE;IACzE,OAAO,CAAC,aAAa,CAAmC;IAExD,0DAA0D;IAC1D,MAAM,CAAC,WAAW,IAAI,QAAQ;IAO9B,+EAA+E;IAC/E,MAAM,CAAC,KAAK,IAAI,IAAI;IAIpB;;;OAGG;IACH,SAAS,CAAC,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,mBAAmB,CAAC;QAAC,KAAK,CAAC,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI;IAUnF;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IA0CnB;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IASvC;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,eAAe,CAAC,CAAC,CAAC;IAkD1E;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAItD,2DAA2D;IAC3D,aAAa,IAAI,UAAU;IAI3B,mEAAmE;IACnE,UAAU,IAAI,IAAI;CAInB"}
|
|
@@ -106,9 +106,8 @@ export class Registry {
|
|
|
106
106
|
created = true;
|
|
107
107
|
}
|
|
108
108
|
SheetOrmLogger.log(`[Registry] ensureTable "${schema.tableName}" → ${created ? "insertSheet (G4 new)" : "existing sheet"}`);
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
// SheetRepository, saving one GAS API call (~700 ms).
|
|
109
|
+
// Headers for newly-created sheets are deferred — they will be written
|
|
110
|
+
// together with the first data flush in SheetRepository, saving one GAS API call.
|
|
112
111
|
if (!created) {
|
|
113
112
|
// Nothing to do — existing sheet already has headers from a prior execution.
|
|
114
113
|
}
|
|
@@ -164,7 +163,7 @@ export class Registry {
|
|
|
164
163
|
const indexes = Decorators.getIndexes(ctor);
|
|
165
164
|
const schema = {
|
|
166
165
|
tableName,
|
|
167
|
-
//
|
|
166
|
+
// Only populate indexTableName when the class has @Indexed fields.
|
|
168
167
|
// Record.indexTableName always returns a string (e.g. "idx_Cars"), but for classes
|
|
169
168
|
// without indexes this causes every update/delete to call getSheetByName() on a
|
|
170
169
|
// non-existent index sheet, wasting ~700 ms per API call.
|
|
@@ -174,10 +173,10 @@ export class Registry {
|
|
|
174
173
|
};
|
|
175
174
|
// Create the sheet tab (or get existing) and register indexes
|
|
176
175
|
const { sheet, created } = this.ensureTable(schema, indexStore);
|
|
177
|
-
// Construct the repository with
|
|
178
|
-
// - sheet:
|
|
179
|
-
// - created ? 0 : undefined:
|
|
180
|
-
// - created:
|
|
176
|
+
// Construct the repository with pre-resolved sheet and row-count hints:
|
|
177
|
+
// - sheet: pre-resolved ISheetAdapter (avoids getSheetByName call)
|
|
178
|
+
// - created ? 0 : undefined: known row count (0 for new sheets, avoids getLastRow call)
|
|
179
|
+
// - created: defer header write to first data flush
|
|
181
180
|
const repo = new SheetRepository(this.getAdapter(), schema, indexStore, this.cache, undefined, sheet, created ? 0 : undefined, created);
|
|
182
181
|
this.repos.set(tableName, repo);
|
|
183
182
|
return repo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Registry.js","sourceRoot":"","sources":["../../../src/core/Registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,OAAO,QAAQ;IAArB;QAIE,qEAAqE;QAC7D,YAAO,GAA+B,IAAI,CAAC;QACnD,4DAA4D;QACpD,UAAK,GAA0B,IAAI,CAAC;QAC5C,sCAAsC;QAC9B,eAAU,GAAsB,IAAI,CAAC;QAC7C,yCAAyC;QACjC,UAAK,GAAG,IAAI,GAAG,EAAmC,CAAC;QAC3D,mEAAmE;QAC3D,mBAAc,GAAG,IAAI,GAAG,EAAwB,CAAC;QACzD,yEAAyE;QACjE,kBAAa,GAAG,IAAI,GAAG,EAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"Registry.js","sourceRoot":"","sources":["../../../src/core/Registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -71,7 +71,7 @@ export declare class SheetRepository<T extends Entity> {
|
|
|
71
71
|
private sheetCache;
|
|
72
72
|
/** Memoized set of indexed field names — avoids repeated getIndexedFields() array allocations in find(). */
|
|
73
73
|
private indexedFieldNames;
|
|
74
|
-
/**
|
|
74
|
+
/** True when headers haven't been written to the sheet yet (new sheet from ensureTable). */
|
|
75
75
|
private headersDeferred;
|
|
76
76
|
/**
|
|
77
77
|
* Constructs a new repository for the given table schema.
|
|
@@ -81,9 +81,9 @@ export declare class SheetRepository<T extends Entity> {
|
|
|
81
81
|
* @param indexStore - Shared IndexStore for secondary indexes.
|
|
82
82
|
* @param cache - Optional cache provider for in-memory row caching.
|
|
83
83
|
* @param hooks - Optional lifecycle hooks (onValidate, beforeSave, afterSave, beforeDelete, afterDelete).
|
|
84
|
-
* @param initialSheet - Pre-resolved sheet adapter (
|
|
85
|
-
* @param initialRowCount - Pre-fetched row count (
|
|
86
|
-
* @param headersDeferred - True if the sheet was just created and headers still need writing
|
|
84
|
+
* @param initialSheet - Pre-resolved sheet adapter (avoids redundant getSheetByName call).
|
|
85
|
+
* @param initialRowCount - Pre-fetched row count (avoids redundant getLastRow call).
|
|
86
|
+
* @param headersDeferred - True if the sheet was just created and headers still need writing.
|
|
87
87
|
*/
|
|
88
88
|
constructor(adapter: ISpreadsheetAdapter, schema: TableSchema, indexStore: IndexStore, cache?: ICacheProvider, hooks?: LifecycleHooks<T>, initialSheet?: ISheetAdapter, initialRowCount?: number, headersDeferred?: boolean);
|
|
89
89
|
/**
|
|
@@ -226,8 +226,8 @@ export declare class SheetRepository<T extends Entity> {
|
|
|
226
226
|
/**
|
|
227
227
|
* Commit all buffered operations from {@link beginBatch}.
|
|
228
228
|
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
229
|
+
* If the buffer contains only deletes, a bulk rewrite (replaceAllData) is
|
|
230
|
+
* used instead of N individual deleteRow() calls.
|
|
231
231
|
*/
|
|
232
232
|
commitBatch(): void;
|
|
233
233
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SheetRepository.d.ts","sourceRoot":"","sources":["../../../src/core/SheetRepository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAItE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAI1C;;;;;GAKG;AACH,qBAAa,eAAe,CAAC,CAAC,SAAS,MAAM;IAC3C,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,iBAAiB,CAAwB;IACjD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAA+B;IAC/C,gFAAgF;IAChF,OAAO,CAAC,YAAY,CAAoC;IAExD,2EAA2E;IAC3E,OAAO,CAAC,WAAW,CAAkE;IAErF;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAKF;IAEjB,4FAA4F;IAC5F,OAAO,CAAC,UAAU,CAA8B;IAChD,8EAA8E;IAC9E,OAAO,CAAC,iBAAiB,CAAuB;IAChD,2GAA2G;IAC3G,OAAO,CAAC,eAAe,CAAoB;IAC3C,mHAAmH;IACnH,OAAO,CAAC,kBAAkB,CAA4B;IACtD,8GAA8G;IAC9G,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,+GAA+G;IAC/G,OAAO,CAAC,oBAAoB,CAAS;IACrC,mGAAmG;IACnG,OAAO,CAAC,UAAU,CAA8B;IAChD,4GAA4G;IAC5G,OAAO,CAAC,iBAAiB,CAAc;IACvC,
|
|
1
|
+
{"version":3,"file":"SheetRepository.d.ts","sourceRoot":"","sources":["../../../src/core/SheetRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,9 +37,9 @@ export class SheetRepository {
|
|
|
37
37
|
* @param indexStore - Shared IndexStore for secondary indexes.
|
|
38
38
|
* @param cache - Optional cache provider for in-memory row caching.
|
|
39
39
|
* @param hooks - Optional lifecycle hooks (onValidate, beforeSave, afterSave, beforeDelete, afterDelete).
|
|
40
|
-
* @param initialSheet - Pre-resolved sheet adapter (
|
|
41
|
-
* @param initialRowCount - Pre-fetched row count (
|
|
42
|
-
* @param headersDeferred - True if the sheet was just created and headers still need writing
|
|
40
|
+
* @param initialSheet - Pre-resolved sheet adapter (avoids redundant getSheetByName call).
|
|
41
|
+
* @param initialRowCount - Pre-fetched row count (avoids redundant getLastRow call).
|
|
42
|
+
* @param headersDeferred - True if the sheet was just created and headers still need writing.
|
|
43
43
|
*/
|
|
44
44
|
constructor(adapter, schema, indexStore, cache, hooks, initialSheet, initialRowCount, headersDeferred) {
|
|
45
45
|
/** Fast-lookup map: entity ID → 1-based sheet row index for O(1) row access. */
|
|
@@ -71,7 +71,7 @@ export class SheetRepository {
|
|
|
71
71
|
this.indexStore = indexStore;
|
|
72
72
|
this.cache = cache ?? null;
|
|
73
73
|
this.hooks = hooks ?? {};
|
|
74
|
-
// Seed memoised sheet & row count when provided by Registry
|
|
74
|
+
// Seed memoised sheet & row count when provided by Registry
|
|
75
75
|
this.sheetCache = initialSheet ?? null;
|
|
76
76
|
this.physicalRowCount = initialRowCount ?? null;
|
|
77
77
|
// Build column header array from field definitions (used for serialisation)
|
|
@@ -90,7 +90,7 @@ export class SheetRepository {
|
|
|
90
90
|
}
|
|
91
91
|
// Pre-build indexed-field name set once — avoids getIndexedFields() array alloc on every find()
|
|
92
92
|
this.indexedFieldNames = new Set(schema.indexes.map((idx) => idx.field));
|
|
93
|
-
//
|
|
93
|
+
// Defer header write to first data flush — saves one GAS API call per new sheet
|
|
94
94
|
this.headersDeferred = headersDeferred ?? false;
|
|
95
95
|
SheetOrmLogger.log(`[Repo:${schema.tableName}] constructor — ` +
|
|
96
96
|
`sheetCache=${initialSheet ? "seeded" : "null"} ` +
|
|
@@ -225,7 +225,7 @@ export class SheetRepository {
|
|
|
225
225
|
};
|
|
226
226
|
const row = Serialization.entityToRow(entity, this.schema.fields, this.headers, this.fieldMap);
|
|
227
227
|
// Compute the 0-based data row index for writing.
|
|
228
|
-
// In batch mode (saveAll), batchBaseRowCount was captured once at start
|
|
228
|
+
// In batch mode (saveAll), batchBaseRowCount was captured once at batch start.
|
|
229
229
|
// Otherwise reuse physicalRowCount to avoid getLastRow() API calls.
|
|
230
230
|
const baseCount = this.batchBaseRowCount !== null
|
|
231
231
|
? this.batchBaseRowCount
|
|
@@ -251,7 +251,7 @@ export class SheetRepository {
|
|
|
251
251
|
this.entityBatch.push({ entity, row, dataIndex, mode: "create" });
|
|
252
252
|
}
|
|
253
253
|
else {
|
|
254
|
-
//
|
|
254
|
+
// Write headers + first data row in a single API call for newly-created sheets
|
|
255
255
|
if (this.headersDeferred) {
|
|
256
256
|
sheet.writeAllRowsWithHeaders(this.headers, [row]);
|
|
257
257
|
this.headersDeferred = false;
|
|
@@ -327,7 +327,7 @@ export class SheetRepository {
|
|
|
327
327
|
// Initialise batch state
|
|
328
328
|
this.entityBatch = [];
|
|
329
329
|
this.batchSheet = sheet;
|
|
330
|
-
//
|
|
330
|
+
// Reuse physicalRowCount when available — avoids a getLastRow() API call (~700 ms)
|
|
331
331
|
// when the row count is already known in-session (e.g. new table seeded to 0).
|
|
332
332
|
this.batchBaseRowCount = this.physicalRowCount !== null ? this.physicalRowCount : sheet.getRowCount();
|
|
333
333
|
if (this.schema.indexTableName) {
|
|
@@ -700,8 +700,8 @@ export class SheetRepository {
|
|
|
700
700
|
/**
|
|
701
701
|
* Commit all buffered operations from {@link beginBatch}.
|
|
702
702
|
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
703
|
+
* If the buffer contains only deletes, a bulk rewrite (replaceAllData) is
|
|
704
|
+
* used instead of N individual deleteRow() calls.
|
|
705
705
|
*/
|
|
706
706
|
commitBatch() {
|
|
707
707
|
if (!this.batchBuffer)
|
|
@@ -709,7 +709,7 @@ export class SheetRepository {
|
|
|
709
709
|
const buffer = this.batchBuffer;
|
|
710
710
|
this.batchBuffer = null;
|
|
711
711
|
try {
|
|
712
|
-
//
|
|
712
|
+
// Optimize delete-only batches (common for deleteAll() in batch mode)
|
|
713
713
|
// by applying one bulk rewrite instead of N × deleteRow() API calls.
|
|
714
714
|
if (buffer.length > 0 && buffer.every((op) => op.type === "delete")) {
|
|
715
715
|
this.commitDeleteBatch(buffer.map((op) => op.data));
|
|
@@ -947,7 +947,7 @@ export class SheetRepository {
|
|
|
947
947
|
SheetOrmLogger.log(`[Repo:${this.schema.tableName}] flushEntityBatch ${batch.length} rows (creates=${creates} updates=${updates}); calling sheet.updateRows()`);
|
|
948
948
|
// Sort by dataIndex for contiguous writes
|
|
949
949
|
const sorted = [...batch].sort((a, b) => a.dataIndex - b.dataIndex);
|
|
950
|
-
//
|
|
950
|
+
// Write header row + data rows in a single API call for newly-created sheets
|
|
951
951
|
if (this.headersDeferred) {
|
|
952
952
|
sheet.writeAllRowsWithHeaders(this.headers, sorted.map((item) => item.row));
|
|
953
953
|
this.headersDeferred = false;
|