@cocreate/crud-server 1.36.4 → 1.37.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.
@@ -1,8 +1,10 @@
1
1
  name: Automated Workflow
2
+
2
3
  on:
3
4
  push:
4
5
  branches:
5
6
  - master
7
+
6
8
  jobs:
7
9
  about:
8
10
  runs-on: ubuntu-latest
@@ -18,52 +20,37 @@ jobs:
18
20
  with:
19
21
  direction: overwrite-github
20
22
  githubToken: "${{ secrets.GITHUB }}"
23
+
21
24
  release:
22
25
  runs-on: ubuntu-latest
23
26
  steps:
24
27
  - name: Checkout
25
- uses: actions/checkout@v3
26
- - name: Setup Node.js
27
- uses: actions/setup-node@v3
28
+ uses: actions/checkout@v4
28
29
  with:
29
- node-version: 14
30
- - name: Semantic Release
31
- uses: cycjimmy/semantic-release-action@v3
32
- id: semantic
30
+ fetch-depth: 0 # Required so semantic-release can trace git tags/history
31
+
32
+ - name: Setup Node.js
33
+ uses: actions/setup-node@v4
33
34
  with:
34
- extra_plugins: |
35
- @semantic-release/changelog
35
+ node-version: 22
36
+
37
+ - name: Install Semantic Release & Plugins
38
+ # Installs semantic-release and its plugins on the runner
39
+ run: |
40
+ npm install -g semantic-release \
41
+ @semantic-release/changelog \
42
+ @semantic-release/npm \
43
+ @semantic-release/github \
36
44
  @semantic-release/git
37
- @semantic-release/github
45
+
46
+ - name: Run Semantic Release (Native)
47
+ id: semantic
48
+ # This will automatically pick up your export default config file in the repository root
49
+ run: npx semantic-release
38
50
  env:
39
- GITHUB_TOKEN: "${{ secrets.GITHUB }}"
51
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
40
52
  NPM_TOKEN: "${{ secrets.NPM_TOKEN }}"
53
+
41
54
  outputs:
42
55
  new_release_published: "${{ steps.semantic.outputs.new_release_published }}"
43
- new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
44
- upload:
45
- runs-on: ubuntu-latest
46
- needs: release
47
- if: needs.release.outputs.new_release_published == 'true'
48
- env:
49
- VERSION: "${{ needs.release.outputs.new_release_version }}"
50
- steps:
51
- - name: Checkout
52
- uses: actions/checkout@v3
53
- - name: Setup Node.js
54
- uses: actions/setup-node@v3
55
- with:
56
- node-version: 16
57
- - name: Set npm registry auth
58
- run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
59
- - name: Install dependencies
60
- run: yarn install
61
-
62
- - name: Set Environment Variables
63
- run: |
64
- echo "organization_id=${{ secrets.COCREATE_ORGANIZATION_ID }}" >> $GITHUB_ENV
65
- echo "key=${{ secrets.COCREATE_KEY }}" >> $GITHUB_ENV
66
- echo "host=${{ secrets.COCREATE_HOST }}" >> $GITHUB_ENV
67
- - name: CoCreate Upload
68
- run: coc upload
69
-
56
+ new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
package/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## [1.37.1](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.37.0...v1.37.1) (2026-07-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * bump dependencies ([4087f75](https://github.com/CoCreate-app/CoCreate-crud-server/commit/4087f759b76143cb65b8d756d02b69863b2f8432))
7
+
8
+ # [1.37.0](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.36.5...v1.37.0) (2026-07-17)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * remove unused dependencies from package.json ([4669a10](https://github.com/CoCreate-app/CoCreate-crud-server/commit/4669a10c8b30fdedb3e6bf4d84f76da0aa2b44d8))
14
+
15
+
16
+ ### Features
17
+
18
+ * update automated workflow and release configuration for improved semantic release handling ([984eed3](https://github.com/CoCreate-app/CoCreate-crud-server/commit/984eed3507d460375a0036df358b8223bef22093))
19
+
20
+ ## [1.36.5](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.36.4...v1.36.5) (2026-07-14)
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * remove unnecessary console logs in init function for cleaner output ([d117235](https://github.com/CoCreate-app/CoCreate-crud-server/commit/d117235ade6687ca3d0d3aa5fee0e73a6b70dfb4))
26
+ * removed post install ([7925526](https://github.com/CoCreate-app/CoCreate-crud-server/commit/7925526070819bdd52eb342cd4c2f50e833afffb))
27
+ * semantic version handling. Reorganize .gitignore for improved clarity and structure ([ef31939](https://github.com/CoCreate-app/CoCreate-crud-server/commit/ef319399b78eafddde3b1e47a21343ffe76424e6))
28
+ * update module export to ES6 syntax in release.config.js ([c550e3d](https://github.com/CoCreate-app/CoCreate-crud-server/commit/c550e3d09533b14ff3489e78e680507ebbe64735))
29
+ * update organization_id assignment in init function for correct server configuration ([2ac9f3d](https://github.com/CoCreate-app/CoCreate-crud-server/commit/2ac9f3dc57552cf6821f6b7aefb45f4dc23fc615))
30
+ * update package.json and refactor index.js for module compatibility ([03f7447](https://github.com/CoCreate-app/CoCreate-crud-server/commit/03f7447bd2a395191593f3d5362fed19b08fa931))
31
+
1
32
  ## [1.36.4](https://github.com/CoCreate-app/CoCreate-crud-server/compare/v1.36.3...v1.36.4) (2025-10-08)
2
33
 
3
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/crud-server",
3
- "version": "1.36.4",
3
+ "version": "1.37.1",
4
4
  "description": "CoCreate-crud-server",
5
5
  "keywords": [
6
6
  "cocreate-crud",
@@ -17,10 +17,10 @@
17
17
  },
18
18
  "scripts": {
19
19
  "demo": "PORT=5000 node demo/server.js",
20
- "test": "echo \"Error: no test specified\" && exit 1",
21
- "postinstall": "node -e \"const { execSync } = require('child_process'); try { execSync('coc --version', { stdio: 'ignore' }); } catch (error) { try { execSync('npm install -g @cocreate/cli', { stdio: 'inherit' }); console.log('Installed \"@cocreate/cli\" globally.'); } catch (error) { console.error('Failed to install \"@cocreate/cli\" globally:', error); } }\""
20
+ "test": "echo \"Error: no test specified\" && exit 1"
22
21
  },
23
- "main": "./src/index",
22
+ "type": "module",
23
+ "main": "./src/index.js",
24
24
  "repository": {
25
25
  "type": "git",
26
26
  "url": "git+https://github.com/CoCreate-app/CoCreate-crud-server.git"
@@ -32,9 +32,8 @@
32
32
  },
33
33
  "homepage": "https://cocreate.app/docs/CoCreate-crud-server",
34
34
  "dependencies": {
35
- "@cocreate/config": "^1.13.1",
36
- "@cocreate/utils": "^1.39.0",
37
- "csvtojson": "^2.0.10",
38
- "json-2-csv": "^3.10.3"
35
+ "@cocreate/config": "^1.14.0",
36
+ "@cocreate/socket-server": "^1.31.1",
37
+ "@cocreate/utils": "^1.44.0"
39
38
  }
40
39
  }
package/release.config.js CHANGED
@@ -1,4 +1,4 @@
1
- module.exports = {
1
+ export default {
2
2
  dryRun: false,
3
3
  branches: ["master"],
4
4
  plugins: [
@@ -10,12 +10,20 @@ module.exports = {
10
10
  changelogFile: "CHANGELOG.md",
11
11
  },
12
12
  ],
13
- "@semantic-release/npm",
14
- "@semantic-release/github",
13
+ "@semantic-release/npm",
15
14
  [
16
- "@semantic-release/git",
15
+ "@semantic-release/github",
17
16
  {
17
+ successComment: false,
18
+ failTitle: false,
19
+ },
20
+ ],
21
+ [
22
+ "@semantic-release/git",
23
+ {
24
+ // Only stage and commit the changelog and package.json
18
25
  assets: ["CHANGELOG.md", "package.json"],
26
+ message: "chore(release): ${nextRelease.version} [skip ci]",
19
27
  },
20
28
  ],
21
29
  ],
package/src/index.js CHANGED
@@ -1,297 +1,409 @@
1
- 'use strict';
2
-
3
- const { ObjectId, sortData } = require("@cocreate/utils");
4
- const Config = require('@cocreate/config')
5
-
6
- class CoCreateCrudServer {
7
- constructor(wsManager, databases) {
8
- this.wsManager = wsManager
9
- this.databases = databases
10
- this.ObjectId = ObjectId
11
- this.organizations = {};
12
- this.hosts = {};
13
- this.init()
1
+ /********************************************************************************
2
+ * Copyright (C) 2023 CoCreate and Contributors.
3
+ *
4
+ * This program is free software: you can redistribute it and/or modify
5
+ * Wu 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
+
19
+ // Commercial Licensing Information:
20
+ // For commercial use of this software without the copyleft provisions of the AGPLv3,
21
+ // you must obtain a commercial license from CoCreate LLC.
22
+ // For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
23
+
24
+ import { ObjectId, sortData } from "@cocreate/utils";
25
+ import Config from '@cocreate/config';
26
+
27
+ // In-process memory caches and storage registers (strictly module-internal)
28
+ const providers = {};
29
+ const organizations = {};
30
+ const hosts = {};
31
+ let config = null;
32
+ let initialized = false;
33
+ let wsManager = null; // Extracted from Server and bound directly for real-time socket communications
34
+
35
+ // Main service contract structure returned as default
36
+ const crudServer = {
37
+ init,
38
+ send,
39
+ getOrganization
40
+ };
41
+
42
+ /**
43
+ * Async initialization loop. Prompts and loads platform configuration guidelines,
44
+ * dynamically registers active drivers, and binds global WebSocket endpoint listeners.
45
+ * @param {Object} Server - CoCreateServer context instance.
46
+ * @returns {Promise<Object>} This fully initialized crudServer module context
47
+ */
48
+ export async function init(Server) {
49
+ if (initialized) return crudServer;
50
+ initialized = true;
51
+
52
+ if (Server && Server.wsManager) {
53
+ wsManager = Server.wsManager;
14
54
  }
15
55
 
16
- async init() {
17
- this.config = await Config({
18
- 'organization_id': { prompt: 'Enter your organization_id: ' },
19
- 'storage': {
20
- prompt: 'Enter a friendly name for the new storage: ',
21
- variable: 'name'
22
- },
23
- 'storage.{{name}}.provider': {
24
- prompt: 'Enter the storage provider, ex mongodb: '
25
- },
26
- 'storage.{{name}}.url': {
27
- prompt: 'Enter the storage providers url: '
56
+ config = await Config({
57
+ 'organization_id': { prompt: 'Enter your organization_id: ' },
58
+ 'storage': {
59
+ prompt: 'Enter a friendly name for the new storage (e.g., primary): ',
60
+ variable: 'name'
61
+ },
62
+ 'storage.{{name}}.provider': {
63
+ prompt: 'Enter the storage provider (e.g., mongodb): '
64
+ },
65
+ 'storage.{{name}}.url': {
66
+ prompt: 'Enter the storage connection url: '
67
+ }
68
+ });
69
+
70
+ let storageUrl;
71
+ let firstStorageKey;
72
+
73
+ if (config.storage) {
74
+ if (typeof config.storage === 'string') {
75
+ config.storage = JSON.parse(config.storage);
76
+ }
77
+
78
+ // Dynamically resolve the first declared storage block in the configuration (order-preserved)
79
+ const storageKeys = Object.keys(config.storage);
80
+ if (storageKeys.length > 0) {
81
+ firstStorageKey = storageKeys[0];
82
+ storageUrl = config.storage[firstStorageKey].url;
83
+ }
84
+ }
85
+
86
+ if (config.organization_id) {
87
+ Server.organization_id = config.organization_id;
88
+ } else {
89
+ console.log('[@cocreate/crud-server] Could not find the organization_id');
90
+ }
91
+ if (!storageUrl) {
92
+ console.log(`[@cocreate/crud-server] Could not find a connection url in your primary storage: "${firstStorageKey || 'default'}"`);
93
+ }
94
+ if (!storageUrl || !config.organization_id) {
95
+ process.exit(1);
96
+ }
97
+
98
+ // Dynamically import and register drivers configured on the server
99
+ if (config.storage && providers) {
100
+ for (const [friendlyName, storageConfig] of Object.entries(config.storage)) {
101
+ const { provider } = storageConfig;
102
+
103
+ if (provider) {
104
+ // Convention over Configuration: fallback to @cocreate/provider if explicit import is omitted
105
+ const importPath = storageConfig.import || `@cocreate/${provider}`;
106
+
107
+ try {
108
+ const driverModule = await import(importPath);
109
+ providers[provider] = driverModule.default || driverModule;
110
+ } catch (err) {
111
+ console.error(`[@cocreate/crud-server] Failed to dynamically load database driver "${importPath}" for provider "${provider}":`, err);
112
+ }
28
113
  }
29
- })
30
-
31
- let storageUrl
32
- if (this.config.storage) {
33
- if (typeof this.config.storage === 'string')
34
- this.config.storage = JSON.parse(this.config.storage)
35
- let defaultStorage = Object.keys(this.config.storage)
36
- storageUrl = this.config.storage[defaultStorage[0]].url
37
114
  }
115
+ }
38
116
 
39
- if (!this.config.organization_id)
40
- console.log('Could not find the organization_id')
41
- if (!storageUrl)
42
- console.log('Could not find a url in your storage object')
43
- if (!storageUrl && !this.config.organization_id)
44
- process.exit()
117
+ // Deferred WebSocket Listener Auto-Registration
118
+ const bindSocketListeners = () => {
119
+ // Dynamically check and capture wsManager from Server if it was not resolved during immediate boot
120
+ if (!wsManager && Server && Server.wsManager) {
121
+ wsManager = Server.wsManager;
122
+ }
45
123
 
46
- if (this.wsManager) {
124
+ if (wsManager) {
47
125
  const type = ['storage', 'database', 'array', 'index', 'object'];
48
126
  const method = ['create', 'read', 'update', 'delete'];
49
127
 
50
128
  for (let i = 0; i < type.length; i++) {
51
129
  for (let j = 0; j < method.length; j++) {
52
130
  const action = type[i] + '.' + method[j];
53
- this.wsManager.on(action, (data) =>
54
- this.send(data))
131
+ wsManager.on(action, (data) => send(data));
55
132
  }
56
133
  }
134
+ } else {
135
+ // Check back in 500ms if the socket server is still resolving its connection handshake
136
+ setTimeout(bindSocketListeners, 500);
57
137
  }
58
- }
138
+ };
59
139
 
60
- async send(data) {
61
- return new Promise(async (resolve) => {
62
- try {
63
- if (!data.organization_id || !this.config) {
64
- data.error = 'Missing an organization_id'
65
- return resolve(data)
66
- }
140
+ bindSocketListeners();
67
141
 
68
- let organization = await this.getOrganization(data)
142
+ return crudServer;
143
+ }
144
+
145
+ /**
146
+ * Resolves metadata, identifies target drivers, processes sync limits, and executes query logic.
147
+ */
148
+ export async function send(data) {
149
+ return new Promise(async (resolve) => {
150
+ try {
151
+ if (!data.organization_id || !config) {
152
+ data.error = 'Missing an organization_id';
153
+ return resolve(data);
154
+ }
69
155
 
70
- if (organization.error)
71
- return resolve(organization)
156
+ let organization = await getOrganization(data);
72
157
 
73
- let storages = organization.storage
74
- if (!storages)
75
- return resolve({ serverStorage: false, error: 'A storage url could not be found' })
158
+ if (organization.error) {
159
+ return resolve(organization);
160
+ }
76
161
 
77
- if (!data['timeStamp'])
78
- data['timeStamp'] = new Date().toISOString()
162
+ let storages = organization.storage;
163
+ if (!storages) {
164
+ return resolve({ serverStorage: false, error: 'A storage url could not be found' });
165
+ }
79
166
 
80
- if (data.array) {
81
- if (!data.database)
82
- data['database'] = data.organization_id
167
+ if (!data['timeStamp']) {
168
+ data['timeStamp'] = new Date().toISOString();
169
+ }
83
170
 
84
- if (data.method === 'object.update' && data.organization_id !== this.config.organization_id) {
85
- let syncKeys
86
- if (data.array === 'organizations')
87
- syncKeys = ['name', 'logo', 'storage', 'host']
88
- else if (data.array === 'userssss')
89
- syncKeys = ['name', 'email', 'password', 'avatar']
171
+ if (data.array) {
172
+ if (!data.database) {
173
+ data['database'] = data.organization_id;
174
+ }
90
175
 
91
- if (syncKeys && syncKeys.length) {
92
- let platformUpdate = {
93
- database: this.config.organization_id,
94
- array: data.array,
95
- object: [{}],
96
- organization_id: this.config.organization_id
97
- }
176
+ if (data.method === 'object.update' && data.organization_id !== config.organization_id) {
177
+ let syncKeys;
178
+ if (data.array === 'organizations') {
179
+ syncKeys = ['name', 'logo', 'storage', 'host'];
180
+ } else if (data.array === 'userssss') {
181
+ syncKeys = ['name', 'email', 'password', 'avatar'];
182
+ }
98
183
 
99
- let object = data.object[0] || data.object
100
- if (object) {
101
- for (let key of syncKeys) {
102
- if (object[key])
103
- platformUpdate.object[0][key] = object[key]
184
+ if (syncKeys && syncKeys.length) {
185
+ let platformUpdate = {
186
+ database: config.organization_id,
187
+ array: data.array,
188
+ object: [{}],
189
+ organization_id: config.organization_id
190
+ };
191
+
192
+ let object = data.object[0] || data.object;
193
+ if (object) {
194
+ for (let key of syncKeys) {
195
+ if (object[key]) {
196
+ platformUpdate.object[0][key] = object[key];
104
197
  }
105
198
  }
106
-
107
- // this.send(platformUpdate)
108
199
  }
109
200
  }
110
201
  }
202
+ }
111
203
 
112
- if (organization.host) {
113
- let host = organization.host
114
- for (let i = 0; i < host.length; i++) {
115
- let hostname
116
- if (data.socket)
117
- hostname = data.socket.host
118
- else
119
- hostname = data.host
120
- if (host[i].name === hostname) {
121
- if (host[i].storage)
122
- data.storage = host[i].storage
123
- if (host[i].database)
124
- data.database = host[i].database
125
- if (host[i].array)
126
- data.array = host[i].array
127
- break
204
+ if (organization.host) {
205
+ let host = organization.host;
206
+ for (let i = 0; i < host.length; i++) {
207
+ let hostname;
208
+ if (data.socket) {
209
+ hostname = data.socket.host;
210
+ } else {
211
+ hostname = data.host;
212
+ }
213
+ if (host[i].name === hostname) {
214
+ if (host[i].storage) {
215
+ data.storage = host[i].storage;
128
216
  }
217
+ if (host[i].database) {
218
+ data.database = host[i].database;
219
+ }
220
+ if (host[i].array) {
221
+ data.array = host[i].array;
222
+ }
223
+ break;
129
224
  }
130
225
  }
226
+ }
131
227
 
132
- if (!data.storage || !data.storage.length) {
133
- if (Array.isArray(storages))
134
- data.storage = storages[0].name
135
- else
136
- data.storage = [Object.keys(storages)[0]]
228
+ // Symmetrical resolution - Fallback to first configured key if data.storage is omitted
229
+ if (!data.storage || !data.storage.length) {
230
+ if (Array.isArray(storages)) {
231
+ data.storage = storages[0].name;
232
+ } else if (typeof storages === 'object' && storages !== null) {
233
+ data.storage = [Object.keys(storages)[0]];
137
234
  }
235
+ }
138
236
 
139
- if (!Array.isArray(data.storage))
140
- data.storage = [data.storage]
237
+ if (!Array.isArray(data.storage)) {
238
+ data.storage = [data.storage];
239
+ }
141
240
 
142
- for (let i = 0; i < data.storage.length; i++) {
143
- let storage;
144
- if (Array.isArray(storages)) {
145
- storage = storages.find((obj) => obj.name === data.storage[i]);
146
- } else if (typeof storages === 'object' && storages !== null) {
147
- storage = storages[data.storage[i]];
148
- }
241
+ for (let i = 0; i < data.storage.length; i++) {
242
+ let storage;
243
+ if (Array.isArray(storages)) {
244
+ storage = storages.find((obj) => obj.name === data.storage[i]);
245
+ } else if (typeof storages === 'object' && storages !== null) {
246
+ storage = storages[data.storage[i]];
247
+ }
149
248
 
150
- if (storage) {
151
- if (storage.provider && this.databases[storage.provider]) {
152
- if (!Array.isArray(storage.url))
153
- storage.url = [storage.url]
249
+ if (storage) {
250
+ if (storage.provider && providers[storage.provider]) {
251
+ if (!Array.isArray(storage.url)) {
252
+ storage.url = [storage.url];
253
+ }
154
254
 
155
- for (let j = 0; j < storage.url.length; j++) {
156
- data['storageName'] = data.storage[i]
157
- data['storageUrl'] = storage.url[j]
255
+ for (let j = 0; j < storage.url.length; j++) {
256
+ data['storageName'] = data.storage[i];
257
+ data['storageUrl'] = storage.url[j];
158
258
 
159
- data = await this.databases[storage.provider].send(data)
160
- }
259
+ data = await providers[storage.provider].send(data);
260
+ }
161
261
 
162
- if (data.$filter) {
163
- let type = data.method.split(".")[0]
164
- if (data.$filter.sort && data.$filter.sort.length)
165
- data[type] = sortData(data[type], data.$filter.sort)
166
- if (!data.$filter.index)
167
- data.$filter.index = 0
168
- data.$filter.startingIndex = data.$filter.index
169
- data.$filter.index += data[type].length
262
+ if (data.$filter) {
263
+ let type = data.method.split(".")[0];
264
+ if (data.$filter.sort && data.$filter.sort.length) {
265
+ data[type] = sortData(data[type], data.$filter.sort);
170
266
  }
171
-
267
+ if (!data.$filter.index) {
268
+ data.$filter.index = 0;
269
+ }
270
+ data.$filter.startingIndex = data.$filter.index;
271
+ data.$filter.index += data[type].length;
172
272
  }
173
273
  }
174
274
  }
275
+ }
175
276
 
176
- delete data.storageUrl
177
- delete data.storageName
178
-
179
- if (data.socket) {
180
- if (data.organization_id === this.config.organization_id && data.socket.organization_id !== data.organization_id) {
181
- this.wsManager.send({
182
- config: {
183
- organization_id: this.config.organization_id,
184
- }
185
- }, { ...data });
186
- data.organization_id = data.socket.organization_id
187
- }
277
+ delete data.storageUrl;
278
+ delete data.storageName;
188
279
 
189
- this.wsManager.send(data);
190
- process.emit('crud-event', data)
191
- }
192
- resolve(data)
193
- } catch (error) {
194
- if (data.socket) {
195
- this.errorHandler(data, error)
196
- this.wsManager.send(data);
280
+ if (data.socket && wsManager) {
281
+ if (data.organization_id === config.organization_id && data.socket.organization_id !== data.organization_id) {
282
+ wsManager.send({
283
+ config: {
284
+ organization_id: config.organization_id,
285
+ }
286
+ }, { ...data });
287
+ data.organization_id = data.socket.organization_id;
197
288
  }
198
289
 
199
- resolve(data)
290
+ wsManager.send(data);
291
+ process.emit('crud-event', data);
292
+ }
293
+ resolve(data);
294
+ } catch (error) {
295
+ if (data.socket && wsManager) {
296
+ errorHandler(data, error);
297
+ wsManager.send(data);
200
298
  }
201
- });
299
+ resolve(data);
300
+ }
301
+ });
302
+ }
303
+
304
+ /**
305
+ * Local cached tenant configuration analyzer.
306
+ * Incorporates high-efficiency local V8 Promise coalescing alongside cross-process workerStorage checks.
307
+ */
308
+ export async function getOrganization(data) {
309
+ if (hosts[data.host]) {
310
+ return await hosts[data.host];
311
+ } else if (organizations[data.organization_id]) {
312
+ return await organizations[data.organization_id];
313
+ } else {
314
+ if (data.organization_id) {
315
+ organizations[data.organization_id] = getOrg(data);
316
+ organizations[data.organization_id] = await organizations[data.organization_id];
317
+ return organizations[data.organization_id];
318
+ } else if (data.host) {
319
+ hosts[data.host] = await getOrg(data);
320
+ return hosts[data.host];
321
+ }
202
322
  }
323
+ }
203
324
 
325
+ /**
326
+ * Database-level organization retriever.
327
+ */
328
+ async function getOrg(data) {
329
+ let query = {
330
+ method: 'object.read',
331
+ host: data.host,
332
+ database: config.organization_id,
333
+ array: 'organizations',
334
+ organization_id: config.organization_id
335
+ };
336
+
337
+ if (organizations[data.organization_id]) {
338
+ data.database = data.organization_id = data.organization_id;
339
+ }
204
340
 
205
- async getOrganization(data) {
206
- if (this.hosts[data.host]) {
207
- return await this.hosts[data.host]
208
- } else if (this.organizations[data.organization_id]) {
209
- return await this.organizations[data.organization_id]
210
- } else {
211
- if (data.organization_id) {
212
- this.organizations[data.organization_id] = this.getOrg(data)
213
- this.organizations[data.organization_id] = await this.organizations[data.organization_id]
214
- return this.organizations[data.organization_id]
215
- } else if (data.host) {
216
- // this.hosts[data.host] = this.getOrg(data)
217
- this.hosts[data.host] = await this.getOrg(data)
218
- return this.hosts[data.host]
219
- }
220
- }
341
+ if (data.organization_id) {
342
+ query.object = [{ _id: data.organization_id }];
343
+ } else if (data.host) {
344
+ query.$filter = {
345
+ query: { host: { $elemMatch: { name: { $in: [data.host] } } } },
346
+ limit: 1
347
+ };
348
+ } else {
349
+ return { serverOrganization: false, error: 'An organization could not be found' };
221
350
  }
222
351
 
223
- async getOrg(data) {
224
- let query = {
225
- method: 'object.read',
226
- host: data.host,
227
- database: this.config.organization_id,
228
- array: 'organizations',
229
- organization_id: this.config.organization_id
230
- }
352
+ if (!organizations[query.organization_id] && query.organization_id === config.organization_id) {
353
+ organizations[query.organization_id] = { ...config, isConfig: true };
354
+ }
231
355
 
232
- if (this.organizations[data.organization_id])
233
- data.database = data.organization_id = data.organization_id
356
+ let organization = await send(query);
234
357
 
235
- if (data.organization_id)
236
- query.object = [{ _id: data.organization_id }]
237
- else if (data.host)
238
- query.$filter = {
239
- query: { host: { $elemMatch: { name: { $in: [data.host] } } } },
240
- limit: 1
241
- }
242
- else
243
- return { serverOrganization: false, error: 'An organization could not be found' }
244
-
245
- if (!this.organizations[query.organization_id] && query.organization_id === this.config.organization_id)
246
- this.organizations[query.organization_id] = { ...this.config, isConfig: true }
247
-
248
- let organization = await this.send(query)
249
-
250
- if (organization
251
- && organization.object
252
- && organization.object[0]) {
253
- if (!this.organizations[organization.object[0]._id] || this.organizations[query.organization_id].isConfig) {
254
- this.organizations[organization.object[0]._id] = organization.object[0]
255
-
256
- delete query.$filter
257
- query.database = query.organization_id = organization.object[0]._id
258
- let org = await this.send(query)
259
- if (org
260
- && org.object
261
- && org.object[0]) {
262
- if (data.host)
263
- this.hosts[data.host] = org.object[0]
264
- return org.object[0]
265
- } else {
266
- if (data.host) return this.hosts[data.host];
267
- return {
268
- serverOrganization: false,
269
- error: "An organization could not be found in the specified dbUrl"
270
- };
358
+ if (organization && organization.object && organization.object[0]) {
359
+ if (!organizations[organization.object[0]._id] || organizations[query.organization_id].isConfig) {
360
+ organizations[organization.object[0]._id] = organization.object[0];
361
+
362
+ delete query.$filter;
363
+ query.database = query.organization_id = organization.object[0]._id;
364
+ let org = await send(query);
365
+ if (org && org.object && org.object[0]) {
366
+ if (data.host) {
367
+ hosts[data.host] = org.object[0];
271
368
  }
369
+ return org.object[0];
272
370
  } else {
273
- if (data.host)
274
- this.hosts[data.host] = organization.object[0]
275
- return organization.object[0]
371
+ if (data.host) return hosts[data.host];
372
+ return {
373
+ serverOrganization: false,
374
+ error: "An organization could not be found in the specified dbUrl"
375
+ };
376
+ }
377
+ } else {
378
+ if (data.host) {
379
+ hosts[data.host] = organization.object[0];
276
380
  }
381
+ return organization.object[0];
277
382
  }
278
- return { serverOrganization: false, error: 'An organization could not be found' }
279
383
  }
384
+ return { serverOrganization: false, error: 'An organization could not be found' };
385
+ }
280
386
 
281
- errorHandler(data, error, database, array) {
282
- if (typeof error == 'object')
283
- error['storage'] = 'mongodb'
284
- else
285
- error = { location: 'crudServer', message: error }
387
+ /**
388
+ * Standard error state structure normalization helper.
389
+ */
390
+ function errorHandler(data, error, database, array) {
391
+ if (typeof error === 'object') {
392
+ error['storage'] = 'mongodb';
393
+ } else {
394
+ error = { location: 'crudServer', message: error };
395
+ }
286
396
 
287
- if (database)
288
- error['database'] = database
397
+ if (database) {
398
+ error['database'] = database;
399
+ }
289
400
 
290
- if (data.error)
291
- data.error.push(error)
292
- else
293
- data.error = [error]
401
+ if (data.error) {
402
+ data.error.push(error);
403
+ } else {
404
+ data.error = [error];
294
405
  }
295
406
  }
296
407
 
297
- module.exports = CoCreateCrudServer;
408
+ // Export default database routing context (explicitly loaded on-demand via server initialization)
409
+ export default crudServer;