tantan-typeorm-gs 1.0.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/bun.lock +300 -0
  4. package/docs/authentication.md +118 -0
  5. package/docs/configuration.md +680 -0
  6. package/docs/contributing.md +177 -0
  7. package/docs/crud.md +351 -0
  8. package/docs/custom-client.md +165 -0
  9. package/docs/development.md +243 -0
  10. package/docs/entities.md +502 -0
  11. package/docs/find-options.md +296 -0
  12. package/docs/google-cloud.md +101 -0
  13. package/docs/installation.md +51 -0
  14. package/docs/integration-testing.md +178 -0
  15. package/docs/limitations.md +223 -0
  16. package/docs/pagination.md +384 -0
  17. package/docs/performance.md +171 -0
  18. package/docs/security.md +153 -0
  19. package/docs/sorting.md +0 -0
  20. package/docs/supported-features.md +310 -0
  21. package/docs/synchronization.md +203 -0
  22. package/package.json +43 -0
  23. package/src/client/index.ts +589 -0
  24. package/src/core/data-source.ts +47 -0
  25. package/src/core/driver.ts +290 -0
  26. package/src/core/error.ts +144 -0
  27. package/src/core/memory.ts +152 -0
  28. package/src/core/query/interpreter.ts +949 -0
  29. package/src/core/query/runner.ts +1297 -0
  30. package/src/core/query/types.ts +155 -0
  31. package/src/core/schema-builder.ts +64 -0
  32. package/src/core/types.ts +113 -0
  33. package/src/core/utils.ts +13 -0
  34. package/src/index.ts +3 -0
  35. package/tantan-typeorm-gs.code-workspace +8 -0
  36. package/test/base/0001-data-source.test.ts +252 -0
  37. package/test/base/0002-operator.test.ts +616 -0
  38. package/test/base/0003-select.test.ts +281 -0
  39. package/test/base/0004-aggregate.test.ts +213 -0
  40. package/test/base/0005-transcation.test.ts +1566 -0
  41. package/test/base/0006-relation.test.ts +1611 -0
  42. package/test/base/0007-logging.test.ts +182 -0
  43. package/test/base/0008-soft-delete.test.ts +649 -0
  44. package/test/google-sheets/0001-client.test.ts +1705 -0
  45. package/test/google-sheets/0002-worksheet-management.test.ts +408 -0
  46. package/test/google-sheets/0003-data-source.test.ts +1935 -0
  47. package/test/google-sheets/0004-transactions.test.ts +952 -0
  48. package/test/google-sheets/0005-operator.test.ts +1124 -0
  49. package/test/google-sheets/0006-object-criteria.test.ts +283 -0
  50. package/test/google-sheets/0007-performance.test.ts +532 -0
  51. package/test/public-api.test.ts +34 -0
  52. package/tsconfig.build.json +21 -0
  53. package/tsconfig.json +34 -0
@@ -0,0 +1,296 @@
1
+ ## Find Options
2
+
3
+ `tantan-typeorm-gs` supports TypeORM find options for filtering, ordering, and limiting query results.
4
+
5
+ The options are passed directly to repository methods such as `find()`, `findOne()`, `findAndCount()`, and `count()`.
6
+
7
+ ### Where
8
+
9
+ Use `where` to filter rows.
10
+
11
+ ```ts
12
+ const users = await repository.find({
13
+ where: {
14
+ name: "Budi"
15
+ }
16
+ });
17
+ ```
18
+
19
+ Multiple properties can be specified:
20
+
21
+ ```ts
22
+ const users = await repository.find({
23
+ where: {
24
+ name: "Budi",
25
+ email: "budi@example.com"
26
+ }
27
+ });
28
+ ```
29
+
30
+ The conditions are evaluated against the corresponding worksheet columns.
31
+
32
+ ### OR Conditions
33
+
34
+ Multiple `where` objects can be supplied to express OR conditions:
35
+
36
+ ```ts
37
+ const users = await repository.find({
38
+ where: [
39
+ {
40
+ name: "Budi"
41
+ },
42
+ {
43
+ name: "Siti"
44
+ }
45
+ ]
46
+ });
47
+ ```
48
+
49
+ This returns rows matching either condition.
50
+
51
+ Conceptually:
52
+
53
+ ```text
54
+ name = 'Budi'
55
+ OR
56
+ name = 'Siti'
57
+ ```
58
+
59
+ ### Order
60
+
61
+ Use `order` to sort the result.
62
+
63
+ ```ts
64
+ const users = await repository.find({
65
+ order: {
66
+ name: "ASC"
67
+ }
68
+ });
69
+ ```
70
+
71
+ Descending order:
72
+
73
+ ```ts
74
+ const users = await repository.find({
75
+ order: {
76
+ name: "DESC"
77
+ }
78
+ });
79
+ ```
80
+
81
+ Multiple fields can be specified:
82
+
83
+ ```ts
84
+ const users = await repository.find({
85
+ order: {
86
+ name: "ASC",
87
+ email: "DESC"
88
+ }
89
+ });
90
+ ```
91
+
92
+ The ordering is applied to the returned result rather than changing the underlying worksheet.
93
+
94
+ ### Skip
95
+
96
+ Use `skip` to ignore a number of matching rows from the beginning of the result.
97
+
98
+ ```ts
99
+ const users = await repository.find({
100
+ skip: 10
101
+ });
102
+ ```
103
+
104
+ For example, with:
105
+
106
+ ```text
107
+ skip: 10
108
+ ```
109
+
110
+ the first 10 matching rows are omitted from the returned result.
111
+
112
+ ### Take
113
+
114
+ Use `take` to limit the number of returned rows.
115
+
116
+ ```ts
117
+ const users = await repository.find({
118
+ take: 10
119
+ });
120
+ ```
121
+
122
+ This returns at most 10 rows.
123
+
124
+ ### Pagination
125
+
126
+ `skip` and `take` can be combined to implement offset-based pagination:
127
+
128
+ ```ts
129
+ const page = 2;
130
+ const pageSize = 10;
131
+
132
+ const users = await repository.find({
133
+ skip: (page - 1) * pageSize,
134
+ take: pageSize
135
+ });
136
+ ```
137
+
138
+ For page 2 with a page size of 10:
139
+
140
+ ```text
141
+ skip = 10
142
+ take = 10
143
+ ```
144
+
145
+ The dedicated **Pagination** section provides a more complete example.
146
+
147
+ ### Combining Options
148
+
149
+ Find options can be combined:
150
+
151
+ ```ts
152
+ const users = await repository.find({
153
+ where: {
154
+ name: "Budi"
155
+ },
156
+
157
+ order: {
158
+ email: "ASC"
159
+ },
160
+
161
+ skip: 10,
162
+
163
+ take: 10
164
+ });
165
+ ```
166
+
167
+ The operation can therefore:
168
+
169
+ 1. filter matching rows
170
+ 2. order the result
171
+ 3. skip rows
172
+ 4. limit the returned rows
173
+
174
+ ### Find One
175
+
176
+ `findOne()` accepts the same style of find options:
177
+
178
+ ```ts
179
+ const user = await repository.findOne({
180
+ where: {
181
+ id: 1
182
+ }
183
+ });
184
+ ```
185
+
186
+ Ordering can also be supplied when selecting a single result:
187
+
188
+ ```ts
189
+ const user = await repository.findOne({
190
+ where: {
191
+ name: "Budi"
192
+ },
193
+
194
+ order: {
195
+ id: "ASC"
196
+ }
197
+ });
198
+ ```
199
+
200
+ If no matching entity exists, `findOne()` returns `null`.
201
+
202
+ ### Find and Count
203
+
204
+ Use `findAndCount()` when both the result set and the total number of matching rows are required.
205
+
206
+ ```ts
207
+ const [users, total] = await repository.findAndCount({
208
+ where: {
209
+ name: "Budi"
210
+ }
211
+ });
212
+ ```
213
+
214
+ The result contains:
215
+
216
+ ```text
217
+ users
218
+ total
219
+ ```
220
+
221
+ `skip` and `take` can be used to limit the returned entities while the count represents the total matching entities.
222
+
223
+ For example:
224
+
225
+ ```ts
226
+ const [users, total] = await repository.findAndCount({
227
+ where: {
228
+ name: "Budi"
229
+ },
230
+
231
+ skip: 10,
232
+
233
+ take: 10
234
+ });
235
+ ```
236
+
237
+ This allows the application to implement pagination while still knowing the total number of matching rows.
238
+
239
+ ### Count
240
+
241
+ Use `count()` to count matching entities:
242
+
243
+ ```ts
244
+ const total = await repository.count({
245
+ where: {
246
+ name: "Budi"
247
+ }
248
+ });
249
+ ```
250
+
251
+ The count is based on the matching rows.
252
+
253
+ `skip` and `take` should not be used when the intention is to obtain the total number of matching entities.
254
+
255
+ ### Example
256
+
257
+ A typical paginated search can be written as:
258
+
259
+ ```ts
260
+ const page = 1;
261
+ const pageSize = 20;
262
+
263
+ const [users, total] = await repository.findAndCount({
264
+ where: {
265
+ name: "Budi"
266
+ },
267
+
268
+ order: {
269
+ name: "ASC"
270
+ },
271
+
272
+ skip: (page - 1) * pageSize,
273
+
274
+ take: pageSize
275
+ });
276
+
277
+ console.log("Total:", total);
278
+ console.log("Rows:", users);
279
+ ```
280
+
281
+ ### Supported Find Options
282
+
283
+ The currently supported and tested find-option behavior includes:
284
+
285
+ | Option | Purpose |
286
+ | ---------------- | --------------------------------- |
287
+ | `where` | Filter rows |
288
+ | `where: []` | OR conditions |
289
+ | `order` | Sort results |
290
+ | `skip` | Offset results |
291
+ | `take` | Limit results |
292
+ | `findOne()` | Retrieve one matching entity |
293
+ | `findAndCount()` | Retrieve entities and total count |
294
+ | `count()` | Count matching entities |
295
+
296
+ More advanced TypeORM find operators should only be considered supported when explicitly implemented and tested by the driver.
@@ -0,0 +1,101 @@
1
+ ## Google Cloud Setup
2
+
3
+ `tantan-typeorm-gs` uses the Google Sheets API to read and modify spreadsheet data.
4
+
5
+ The recommended setup for this driver is a **Google Cloud service account** with access to the target Google Spreadsheet.
6
+
7
+ ### 1. Create a Google Cloud Project
8
+
9
+ Create or select a project in Google Cloud Console.
10
+
11
+ The project will be used to manage the Google Sheets API and the service account used by the application.
12
+
13
+ ### 2. Enable Google Sheets API
14
+
15
+ Open the **API Library** in Google Cloud Console and enable:
16
+
17
+ ```text
18
+ Google Sheets API
19
+ ```
20
+
21
+ The API service name is:
22
+
23
+ ```text
24
+ sheets.googleapis.com
25
+ ```
26
+
27
+ Google Cloud requires the corresponding API to be enabled before an application can use it.
28
+
29
+ ### 3. Create a Service Account
30
+
31
+ Open:
32
+
33
+ ```text
34
+ IAM & Admin → Service Accounts
35
+ ```
36
+
37
+ Create a new service account for the application.
38
+
39
+ For example:
40
+
41
+ ```text
42
+ tantan-typeorm-gs
43
+ ```
44
+
45
+ A service account represents the application rather than an individual Google user and can be granted access to Google Cloud resources.
46
+
47
+ After creating the service account, note its email address:
48
+
49
+ ```text
50
+ tantan-typeorm-gs@YOUR_PROJECT_ID.iam.gserviceaccount.com
51
+ ```
52
+
53
+ ### 4. Create Service Account Credentials
54
+
55
+ For local development, create a service account key in JSON format from the service account's **Keys** section.
56
+
57
+ The generated credential contains sensitive information, including the private key.
58
+
59
+ **Do not commit the JSON key to Git.**
60
+
61
+ Google notes that user-managed service account keys are long-lived credentials and should be handled carefully. For workloads running on Google Cloud or supported external environments, more secure alternatives such as attached service accounts or Workload Identity Federation should be considered.
62
+
63
+ ### 5. Share the Spreadsheet
64
+
65
+ Open the Google Spreadsheet that will be used by the application.
66
+
67
+ Share the spreadsheet with the service account email:
68
+
69
+ ```text
70
+ tantan-typeorm-gs@YOUR_PROJECT_ID.iam.gserviceaccount.com
71
+ ```
72
+
73
+ Grant the service account the required access level.
74
+
75
+ For this driver, the service account must be able to access the spreadsheet and perform the operations required by the application. Google also documents sharing the spreadsheet directly with the service account as part of Sheets API service-account setup.
76
+
77
+ ### 6. Get the Spreadsheet ID
78
+
79
+ The spreadsheet ID is part of the Google Sheets URL:
80
+
81
+ ```text
82
+ https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit
83
+ ```
84
+
85
+ For example:
86
+
87
+ ```text
88
+ https://docs.google.com/spreadsheets/d/1AbCdEfGhIjKlMnOpQrStUvWxYz/edit
89
+ ```
90
+
91
+ The spreadsheet ID is:
92
+
93
+ ```text
94
+ 1AbCdEfGhIjKlMnOpQrStUvWxYz
95
+ ```
96
+
97
+ This value is passed to the driver's configuration.
98
+
99
+ ### Next Step
100
+
101
+ After Google Cloud and spreadsheet access have been configured, configure the driver's authentication credentials in the application.
@@ -0,0 +1,51 @@
1
+ ## Installation
2
+
3
+ ### Requirements
4
+
5
+ Before installing `tantan-typeorm-gs`, make sure your project has:
6
+
7
+ - Node.js or Bun
8
+ - TypeScript
9
+ - TypeORM `1.1.x`
10
+
11
+ The driver is distributed as an npm package and uses TypeORM as its ORM layer.
12
+
13
+ ### Install
14
+
15
+ Using npm:
16
+
17
+ ```bash
18
+ npm install tantan-typeorm-gs typeorm googleapis
19
+ ```
20
+
21
+ Using Bun:
22
+
23
+ ```bash
24
+ bun add tantan-typeorm-gs typeorm googleapis
25
+ ```
26
+
27
+ ### TypeScript
28
+
29
+ The project using the driver should have TypeScript configured.
30
+
31
+ For example:
32
+
33
+ ```json
34
+ {
35
+ "compilerOptions": {
36
+ "target": "ES2022",
37
+ "module": "ESNext",
38
+ "moduleResolution": "Bundler"
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### Verify Installation
44
+
45
+ After installation, the package can be imported from the application:
46
+
47
+ ```ts
48
+ import { createGoogleSheetsDataSource } from "tantan-typeorm-gs";
49
+ ```
50
+
51
+ The next step is configuring Google Cloud and Google Sheets API access.
@@ -0,0 +1,178 @@
1
+ ## Integration Testing
2
+
3
+ The project includes integration tests for validating the Google Sheets API client against the real Google Sheets API.
4
+
5
+ Integration tests are different from the unit and driver-level tests that use `Memory`.
6
+
7
+ ### Test Layers
8
+
9
+ The test suite can be viewed in several layers:
10
+
11
+ ```text
12
+ Unit / Driver Tests
13
+ ↓
14
+ Memory
15
+ ↓
16
+ GoogleSheetsDriver
17
+ ↓
18
+ Repository / QueryRunner
19
+
20
+ Integration Tests
21
+ ↓
22
+ GoogleSheetsApiClient
23
+ ↓
24
+ Google Sheets API
25
+ ↓
26
+ Real Spreadsheet
27
+ ```
28
+
29
+ The fake client is used for fast and deterministic tests.
30
+
31
+ The integration tests validate the behavior of the actual Google Sheets API client.
32
+
33
+ ### Fake Client Tests
34
+
35
+ Most driver behavior can be tested without network access:
36
+
37
+ ```ts id="8r3m1v"
38
+ const client = new Memory({
39
+ users: []
40
+ });
41
+
42
+ const dataSource = createGoogleSheetsDataSource({
43
+ type: "google-sheets",
44
+
45
+ client,
46
+
47
+ entities: [User]
48
+ });
49
+ ```
50
+
51
+ This approach is useful for testing:
52
+
53
+ - repository operations
54
+ - query execution
55
+ - metadata handling
56
+ - lifecycle hooks
57
+ - subscribers
58
+ - generated primary keys
59
+ - error handling
60
+ - pagination and sorting
61
+ - driver behavior
62
+
63
+ These tests should remain fast and deterministic.
64
+
65
+ ### Google Sheets API Integration Tests
66
+
67
+ The Google Sheets API client has dedicated integration coverage.
68
+
69
+ The integration tests exercise operations against the actual Google Sheets API, including:
70
+
71
+ - reading rows
72
+ - inserting rows
73
+ - updating rows
74
+ - deleting rows
75
+ - batch-compatible operations
76
+ - handling Google API responses
77
+
78
+ The test file is:
79
+
80
+ ```text id="3q7m9x"
81
+ test/google-sheets/0001-client.test.ts
82
+ ```
83
+
84
+ ### Required Configuration
85
+
86
+ Integration tests require valid Google Cloud credentials and access to a test spreadsheet.
87
+
88
+ The test environment should provide the required configuration without committing credentials to the repository.
89
+
90
+ For example:
91
+
92
+ ```env id="6v2k4p"
93
+ GOOGLE_SHEETS_SPREADSHEET_ID=...
94
+ GOOGLE_SHEETS_CLIENT_EMAIL=...
95
+ GOOGLE_SHEETS_PRIVATE_KEY=...
96
+ ```
97
+
98
+ The service account must have access to the spreadsheet used by the integration tests.
99
+
100
+ ### Keep Integration Data Isolated
101
+
102
+ A dedicated spreadsheet should be used for integration testing whenever possible.
103
+
104
+ The test spreadsheet should not contain production data.
105
+
106
+ Integration tests may modify worksheet contents, so the test environment should be isolated from important spreadsheets.
107
+
108
+ ### Running Integration Tests
109
+
110
+ Run the API client integration test directly:
111
+
112
+ ```bash id="1m8x5c"
113
+ bun test test/google-sheets/0001-client.test.ts
114
+ ```
115
+
116
+ The integration test suite currently contains coverage for the Google Sheets API client and batch operations.
117
+
118
+ ### Unit Tests vs Integration Tests
119
+
120
+ Use the fake client when the behavior being tested belongs to the driver:
121
+
122
+ ```text id="7q3n8v"
123
+ Driver behavior
124
+ ↓
125
+ Memory
126
+ ```
127
+
128
+ Use the real API integration test when validating the Google API client itself:
129
+
130
+ ```text id="5c1m7x"
131
+ GoogleSheetsApiClient
132
+ ↓
133
+ Google Sheets API
134
+ ```
135
+
136
+ This separation prevents network availability or Google API latency from affecting the majority of the test suite.
137
+
138
+ ### Recommended Development Workflow
139
+
140
+ During normal development:
141
+
142
+ ```bash id="9v4k2m"
143
+ bun test
144
+ ```
145
+
146
+ should be used for the regular test suite.
147
+
148
+ When changing the Google Sheets API client, run the dedicated integration tests as well:
149
+
150
+ ```bash id="2x7p5n"
151
+ bun test test/google-sheets/0001-client.test.ts
152
+ ```
153
+
154
+ Performance tests can be run separately:
155
+
156
+ ```bash id="6k3m8q"
157
+ bun test test/google-sheets/0007-performance.test.ts
158
+ ```
159
+
160
+ ### Security
161
+
162
+ Never commit integration-test credentials to source control.
163
+
164
+ Use environment variables or the project's secure CI/CD secret mechanism.
165
+
166
+ Integration-test spreadsheets should also be treated as test infrastructure and should not contain sensitive production information.
167
+
168
+ ### Summary
169
+
170
+ The project intentionally separates testing responsibilities:
171
+
172
+ | Test type | Client | Purpose |
173
+ | ----------------------- | ----------------------- | --------------------------------- |
174
+ | Driver/repository tests | `Memory` | Fast driver behavior testing |
175
+ | API integration tests | `GoogleSheetsApiClient` | Real Google Sheets API validation |
176
+ | Performance tests | `Memory` | Driver regression baseline |
177
+
178
+ This separation keeps the normal test suite fast while still providing coverage against the real Google Sheets API.