@cocreate/firestore 1.0.0 → 1.2.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 (3) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +13 -10
  3. package/src/index.js +66 -31
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.2.0](https://github.com/CoCreate-app/CoCreate-firestore/compare/v1.1.0...v1.2.0) (2026-07-18)
2
+
3
+
4
+ ### Features
5
+
6
+ * Update package.json description and keywords; refactor index.js to use ES modules ([1764e13](https://github.com/CoCreate-app/CoCreate-firestore/commit/1764e13f050671a8d2b55609bf118bcf9c510e17))
7
+
8
+ # [1.1.0](https://github.com/CoCreate-app/CoCreate-firestore/compare/v1.0.0...v1.1.0) (2026-07-18)
9
+
10
+
11
+ ### Features
12
+
13
+ * Enhance Firestore client connection handling and normalize storage URL usage ([a97f76b](https://github.com/CoCreate-app/CoCreate-firestore/commit/a97f76b0dde45697e24875af2436e3b53c3b5b8f))
14
+
1
15
  # 1.0.0 (2026-07-18)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "@cocreate/firestore",
3
- "version": "1.0.0",
4
- "description": "A simple firestore component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
3
+ "version": "1.2.0",
4
+ "description": "Lightweight Google Cloud Firestore CRUD wrapper and multi-tenant state routing client for the CoCreate ecosystem.",
5
5
  "keywords": [
6
6
  "firestore",
7
- "low-code",
8
- "realtime",
9
- "realtime-framework",
10
- "collaboration",
11
- "shared-editing",
12
- "html5-framework",
13
- "javascript-framework"
7
+ "firebase",
8
+ "google-cloud-firestore",
9
+ "nosql",
10
+ "crud-api",
11
+ "abstract-crud",
12
+ "database-abstraction",
13
+ "multi-tenant",
14
+ "multi-tenant-routing",
15
+ "realtime-sync",
16
+ "state-synchronization"
14
17
  ],
15
18
  "publishConfig": {
16
19
  "access": "public"
@@ -38,6 +41,6 @@
38
41
  "main": "./src/index.js",
39
42
  "dependencies": {
40
43
  "@cocreate/utils": "^1.44.0",
41
- "firestore": "^7.5.0"
44
+ "@google-cloud/firestore": "^7.5.0"
42
45
  }
43
46
  }
package/src/index.js CHANGED
@@ -1,37 +1,70 @@
1
- const { Firestore } = require("@google-cloud/firestore");
2
- const {
1
+ /********************************************************************************
2
+ * Copyright (C) 2026 CoCreate and Contributors.
3
+ *
4
+ * This program is free software: you can redistribute it and/or modify
5
+ * it under the terms of the GNU Affero General Public License as published
6
+ * by the Free Software Foundation, either version 3 of the License, or
7
+ * (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ * GNU Affero General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU Affero General Public License
15
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+ ********************************************************************************/
17
+
18
+ import { Firestore } from "@google-cloud/firestore";
19
+ import {
3
20
  dotNotationToObject,
4
21
  queryData,
5
22
  searchData,
6
23
  sortData,
7
24
  isValidDate
8
- } = require("@cocreate/utils");
25
+ } from "@cocreate/utils";
9
26
 
10
27
  const organizations = {};
11
28
 
12
29
  /**
13
30
  * Dynamically resolves or caches a long-lived Firestore database client connection.
14
31
  * Caches the connection promise to prevent duplicate authentication handshakes.
15
- * Normalizes custom credential protocols directly out of standard CoCreate storageUrls.
32
+ * Normalizes custom credential protocols directly out of standard CoCreate storageUrls or urls.
16
33
  */
17
34
  async function dbClient(data) {
18
- if (data.storageUrl) {
35
+ const storageUrl = data.storageUrl || data.url;
36
+
37
+ if (storageUrl) {
19
38
  if (!organizations[data.organization_id]) {
20
39
  organizations[data.organization_id] = {};
21
40
  }
22
41
  try {
23
- if (!organizations[data.organization_id][data.storageUrl]) {
42
+ if (!organizations[data.organization_id][storageUrl]) {
24
43
  const connectionPromise = (async () => {
25
44
  let config = {};
26
45
 
27
- // Option A: The Credentials Path URL (e.g. firestore:///path/to/credentials.json)
28
- if (data.storageUrl.startsWith("firestore:///")) {
29
- const filePath = data.storageUrl.replace("firestore://", "");
46
+ // Option A: The Structured Credentials Object (Highly recommended)
47
+ if (typeof storageUrl === "object" && storageUrl !== null) {
48
+ const projectId = storageUrl.projectId || storageUrl.project_id;
49
+ const clientEmail = storageUrl.clientEmail || storageUrl.client_email;
50
+ const privateKey = storageUrl.privateKey || storageUrl.private_key;
51
+
52
+ config = {
53
+ projectId,
54
+ credentials: {
55
+ client_email: clientEmail,
56
+ private_key: privateKey ? privateKey.replace(/\\n/g, "\n") : undefined
57
+ }
58
+ };
59
+ }
60
+ // Option B: The Credentials Path URL (e.g. firestore:///path/to/credentials.json)
61
+ else if (typeof storageUrl === "string" && storageUrl.startsWith("firestore:///")) {
62
+ const filePath = storageUrl.replace("firestore://", "");
30
63
  config = { keyFilename: filePath };
31
64
  }
32
- // Option B: Query Params Encoded URI (e.g. firestore://project-id?client_email=...&private_key=...)
33
- else if (data.storageUrl.startsWith("firestore://")) {
34
- const urlParsed = new URL(data.storageUrl);
65
+ // Option C: Query Params Encoded URI (e.g. firestore://project-id?client_email=...&private_key=...)
66
+ else if (typeof storageUrl === "string" && storageUrl.startsWith("firestore://")) {
67
+ const urlParsed = new URL(storageUrl);
35
68
  const projectId = urlParsed.host || urlParsed.hostname;
36
69
  const clientEmail = urlParsed.searchParams.get("client_email");
37
70
  const privateKey = urlParsed.searchParams.get("private_key");
@@ -57,21 +90,21 @@ async function dbClient(data) {
57
90
  return client;
58
91
  })();
59
92
 
60
- organizations[data.organization_id][data.storageUrl] = connectionPromise;
93
+ organizations[data.organization_id][storageUrl] = connectionPromise;
61
94
  }
62
95
 
63
96
  // Await connection resolution safely
64
- return await organizations[data.organization_id][data.storageUrl];
97
+ return await organizations[data.organization_id][storageUrl];
65
98
  } catch (error) {
66
99
  console.error(
67
- `${data.organization_id}: storageUrl ${data.storageUrl} failed to connect to Firestore`
100
+ `${data.organization_id}: Firestore failed to connect`
68
101
  );
69
102
  errorHandler(data, error);
70
103
  return { status: false };
71
104
  }
72
105
  }
73
106
 
74
- errorHandler(data, "missing StorageUrl");
107
+ errorHandler(data, "missing Firestore Connection URL/Credentials");
75
108
  return;
76
109
  }
77
110
 
@@ -110,7 +143,7 @@ process.on("orgDeleted", (organization_id) => {
110
143
  /**
111
144
  * Universal router matching the standard CoCreate pipeline signature.
112
145
  */
113
- function send(data) {
146
+ export function send(data) {
114
147
  let [type, method] = data.method.split(".");
115
148
  if (type === "database") return database(method, data);
116
149
  if (type === "array") return array(method, data);
@@ -121,6 +154,7 @@ function send(data) {
121
154
  * "database" operations mapped to Firestore Databases.
122
155
  */
123
156
  function database(method, data) {
157
+ const storageName = data.storageName || data.provider || "firestore";
124
158
  return new Promise(
125
159
  async (resolve, reject) => {
126
160
  let type = "database";
@@ -128,7 +162,7 @@ function database(method, data) {
128
162
 
129
163
  try {
130
164
  const db = await dbClient(data);
131
- if (!db || db.status === false) return data;
165
+ if (!db || db.status === false) return resolve(data);
132
166
 
133
167
  if (method === "read") {
134
168
  // Firestore is serverless and generally operates on a single default schema database per context.
@@ -147,13 +181,13 @@ function database(method, data) {
147
181
  if (isFilter) {
148
182
  databaseArray.push({
149
183
  database: dbObj,
150
- storage: data.storageName
184
+ storage: storageName
151
185
  });
152
186
  }
153
187
  } else {
154
188
  databaseArray.push({
155
189
  database: dbObj,
156
- storage: data.storageName
190
+ storage: storageName
157
191
  });
158
192
  }
159
193
  resolve(createData(data, databaseArray, type));
@@ -187,6 +221,7 @@ function database(method, data) {
187
221
  * "array" operations mapped to Firestore Collections.
188
222
  */
189
223
  function array(method, data) {
224
+ const storageName = data.storageName || data.provider || "firestore";
190
225
  return new Promise(
191
226
  async (resolve, reject) => {
192
227
  let type = "array";
@@ -194,7 +229,7 @@ function array(method, data) {
194
229
 
195
230
  try {
196
231
  const db = await dbClient(data);
197
- if (!db || db.status === false) return data;
232
+ if (!db || db.status === false) return resolve(data);
198
233
 
199
234
  if (data.request) data.array = data.request;
200
235
 
@@ -220,14 +255,14 @@ function array(method, data) {
220
255
  arrayArray.push({
221
256
  name: col.id,
222
257
  database,
223
- storage: data.storageName
258
+ storage: storageName
224
259
  });
225
260
  }
226
261
  } else {
227
262
  arrayArray.push({
228
263
  name: col.id,
229
264
  database,
230
- storage: data.storageName
265
+ storage: storageName
231
266
  });
232
267
  }
233
268
  }
@@ -260,7 +295,7 @@ function array(method, data) {
260
295
  arrayArray.push({
261
296
  name: array,
262
297
  database,
263
- storage: data.storageName
298
+ storage: storageName
264
299
  });
265
300
 
266
301
  arraysLength -= 1;
@@ -295,7 +330,7 @@ function array(method, data) {
295
330
  name: newName,
296
331
  oldName: oldName,
297
332
  database,
298
- storage: data.storageName
333
+ storage: storageName
299
334
  });
300
335
 
301
336
  arraysLength -= 1;
@@ -319,7 +354,7 @@ function array(method, data) {
319
354
  arrayArray.push({
320
355
  name: array,
321
356
  database,
322
- storage: data.storageName
357
+ storage: storageName
323
358
  });
324
359
 
325
360
  arraysLength -= 1;
@@ -349,11 +384,12 @@ function array(method, data) {
349
384
  * "object" operations mapped to Firestore Documents.
350
385
  */
351
386
  function object(method, data) {
387
+ const storageName = data.storageName || data.provider || "firestore";
352
388
  return new Promise(
353
389
  async (resolve, reject) => {
354
390
  try {
355
391
  const db = await dbClient(data);
356
- if (!db || db.status === false) return data;
392
+ if (!db || db.status === false) return resolve(data);
357
393
 
358
394
  let type = "object";
359
395
  let documents = [];
@@ -371,7 +407,7 @@ function object(method, data) {
371
407
 
372
408
  for (let array of arrays) {
373
409
  const reference = {
374
- $storage: data.storageName,
410
+ $storage: storageName,
375
411
  $database: database,
376
412
  $array: array
377
413
  };
@@ -396,7 +432,7 @@ function object(method, data) {
396
432
  if (!Array.isArray($database)) $database = [data[type][i].$database];
397
433
  if (!Array.isArray($array)) $array = [data[type][i].$array];
398
434
 
399
- if (!$storage.includes(data.storageName)) $storage.push(data.storageName);
435
+ if (!$storage.includes(storageName)) $storage.push(storageName);
400
436
  if (!$database.includes(database)) $database.push(database);
401
437
  if (!$array.includes(array)) $array.push(array);
402
438
 
@@ -456,7 +492,6 @@ function object(method, data) {
456
492
  queryRef = queryRef.limit(data.$filter.limit);
457
493
  }
458
494
  if (data.$filter && typeof data.$filter.index === "number") {
459
- // Standard offset queries are supported but startAt is normally preferred in production
460
495
  queryRef = queryRef.offset(data.$filter.index);
461
496
  }
462
497
 
@@ -844,4 +879,4 @@ function errorHandler(data, error, database, array) {
844
879
  }
845
880
  }
846
881
 
847
- module.exports = { send };
882
+ export default { send };