s3db.js 12.2.3 → 12.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "s3db.js",
3
- "version": "12.2.3",
3
+ "version": "12.3.0",
4
4
  "description": "Use AWS S3, the world's most reliable document storage, as a database with this ORM.",
5
5
  "main": "dist/s3db.cjs.js",
6
6
  "module": "dist/s3db.es.js",
@@ -59,10 +59,15 @@ export async function handleInsert({ resource, data, mappedData }) {
59
59
  '_v': mappedData._v || String(resource.version)
60
60
  };
61
61
  metadataOnly._map = JSON.stringify(resource.schema.map);
62
-
62
+
63
+ // Store pluginMap for backwards compatibility when plugins are added/removed
64
+ if (resource.schema.pluginMap && Object.keys(resource.schema.pluginMap).length > 0) {
65
+ metadataOnly._pluginMap = JSON.stringify(resource.schema.pluginMap);
66
+ }
67
+
63
68
  // Use the original object for the body
64
69
  const body = JSON.stringify(mappedData);
65
-
70
+
66
71
  return { mappedData: metadataOnly, body };
67
72
  }
68
73
 
@@ -70,16 +75,21 @@ export async function handleUpdate({ resource, id, data, mappedData }) {
70
75
  // For updates, we need to merge with existing data
71
76
  // Since we can't easily read the existing body during update,
72
77
  // we'll put the update data in the body and let the resource handle merging
73
-
78
+
74
79
  // Keep only the version field in metadata
75
80
  const metadataOnly = {
76
81
  '_v': mappedData._v || String(resource.version)
77
82
  };
78
83
  metadataOnly._map = JSON.stringify(resource.schema.map);
79
-
84
+
85
+ // Store pluginMap for backwards compatibility when plugins are added/removed
86
+ if (resource.schema.pluginMap && Object.keys(resource.schema.pluginMap).length > 0) {
87
+ metadataOnly._pluginMap = JSON.stringify(resource.schema.pluginMap);
88
+ }
89
+
80
90
  // Use the original object for the body
81
91
  const body = JSON.stringify(mappedData);
82
-
92
+
83
93
  return { mappedData: metadataOnly, body };
84
94
  }
85
95
 
@@ -90,6 +90,15 @@ export async function handleInsert({ resource, data, mappedData, originalData })
90
90
  currentSize += attributeSizes._v;
91
91
  }
92
92
 
93
+ // Always include plugin map for backwards compatibility when plugins are added/removed
94
+ // Note: _pluginMap is not in mappedData, it's added separately by behaviors
95
+ if (resource.schema?.pluginMap && Object.keys(resource.schema.pluginMap).length > 0) {
96
+ const pluginMapStr = JSON.stringify(resource.schema.pluginMap);
97
+ const pluginMapSize = calculateUTF8Bytes('_pluginMap') + calculateUTF8Bytes(pluginMapStr);
98
+ metadataFields._pluginMap = pluginMapStr;
99
+ currentSize += pluginMapSize;
100
+ }
101
+
93
102
  // Reserve space for $overflow if overflow is possible
94
103
  let reservedLimit = effectiveLimit;
95
104
  for (const [fieldName, size] of sortedFields) {
@@ -88,7 +88,14 @@ export async function handleInsert({ resource, data, mappedData, originalData })
88
88
  data: originalData || data
89
89
  });
90
90
  // If data exceeds limit, store in body
91
- return { mappedData: { _v: mappedData._v }, body: JSON.stringify(mappedData) };
91
+ const metadataOnly = { _v: mappedData._v };
92
+
93
+ // Store pluginMap for backwards compatibility when plugins are added/removed
94
+ if (resource.schema?.pluginMap && Object.keys(resource.schema.pluginMap).length > 0) {
95
+ metadataOnly._pluginMap = JSON.stringify(resource.schema.pluginMap);
96
+ }
97
+
98
+ return { mappedData: metadataOnly, body: JSON.stringify(mappedData) };
92
99
  }
93
100
 
94
101
  // If data fits in metadata, store only in metadata
@@ -190,10 +190,20 @@ export async function generateTypes(database, options = {}) {
190
190
  const resourceInterfaces = [];
191
191
 
192
192
  for (const [name, resource] of Object.entries(database.resources)) {
193
- const attributes = resource.config?.attributes || resource.attributes || {};
193
+ const allAttributes = resource.config?.attributes || resource.attributes || {};
194
194
  const timestamps = resource.config?.timestamps || false;
195
195
 
196
- const interfaceDef = generateResourceInterface(name, attributes, timestamps);
196
+ // Filter out plugin attributes - they are internal implementation details
197
+ // and should not be exposed in public TypeScript interfaces
198
+ const pluginAttrNames = resource.schema?._pluginAttributes
199
+ ? Object.values(resource.schema._pluginAttributes).flat()
200
+ : [];
201
+
202
+ const userAttributes = Object.fromEntries(
203
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
204
+ );
205
+
206
+ const interfaceDef = generateResourceInterface(name, userAttributes, timestamps);
197
207
  lines.push(interfaceDef);
198
208
 
199
209
  resourceInterfaces.push({
@@ -98,7 +98,17 @@ function generateResourceSchema(resource) {
98
98
  const properties = {};
99
99
  const required = [];
100
100
 
101
- const attributes = resource.config?.attributes || resource.attributes || {};
101
+ const allAttributes = resource.config?.attributes || resource.attributes || {};
102
+
103
+ // Filter out plugin attributes - they are internal implementation details
104
+ // and should not be exposed in public API documentation
105
+ const pluginAttrNames = resource.schema?._pluginAttributes
106
+ ? Object.values(resource.schema._pluginAttributes).flat()
107
+ : [];
108
+
109
+ const attributes = Object.fromEntries(
110
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
111
+ );
102
112
 
103
113
  // Extract resource description (supports both string and object format)
104
114
  const resourceDescription = resource.config?.description;
@@ -254,7 +264,16 @@ function generateResourcePaths(resource, version, config = {}) {
254
264
  }
255
265
 
256
266
  // Create query parameters only for partition fields
257
- const attributes = resource.config?.attributes || resource.attributes || {};
267
+ const allAttributes = resource.config?.attributes || resource.attributes || {};
268
+
269
+ // Filter out plugin attributes
270
+ const pluginAttrNames = resource.schema?._pluginAttributes
271
+ ? Object.values(resource.schema._pluginAttributes).flat()
272
+ : [];
273
+
274
+ const attributes = Object.fromEntries(
275
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
276
+ );
258
277
 
259
278
  for (const fieldName of partitionFieldsSet) {
260
279
  const fieldDef = attributes[fieldName];
@@ -186,7 +186,15 @@ class BigqueryReplicator extends BaseReplicator {
186
186
  continue;
187
187
  }
188
188
 
189
- const attributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
189
+ const allAttributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
190
+
191
+ // Filter out plugin attributes - they are internal and should not be replicated
192
+ const pluginAttrNames = resource.schema?._pluginAttributes
193
+ ? Object.values(resource.schema._pluginAttributes).flat()
194
+ : [];
195
+ const attributes = Object.fromEntries(
196
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
197
+ );
190
198
 
191
199
  for (const tableConfig of tableConfigs) {
192
200
  const tableName = tableConfig.table;
@@ -221,7 +221,15 @@ class MySQLReplicator extends BaseReplicator {
221
221
  continue;
222
222
  }
223
223
 
224
- const attributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
224
+ const allAttributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
225
+
226
+ // Filter out plugin attributes - they are internal and should not be replicated
227
+ const pluginAttrNames = resource.schema?._pluginAttributes
228
+ ? Object.values(resource.schema._pluginAttributes).flat()
229
+ : [];
230
+ const attributes = Object.fromEntries(
231
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
232
+ );
225
233
 
226
234
  for (const tableConfig of tableConfigs) {
227
235
  const tableName = tableConfig.table;
@@ -183,7 +183,15 @@ class PlanetScaleReplicator extends BaseReplicator {
183
183
  continue;
184
184
  }
185
185
 
186
- const attributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
186
+ const allAttributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
187
+
188
+ // Filter out plugin attributes - they are internal and should not be replicated
189
+ const pluginAttrNames = resource.schema?._pluginAttributes
190
+ ? Object.values(resource.schema._pluginAttributes).flat()
191
+ : [];
192
+ const attributes = Object.fromEntries(
193
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
194
+ );
187
195
 
188
196
  for (const tableConfig of tableConfigs) {
189
197
  const tableName = tableConfig.table;
@@ -225,7 +225,15 @@ class PostgresReplicator extends BaseReplicator {
225
225
  }
226
226
 
227
227
  // Get resource attributes from current version
228
- const attributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
228
+ const allAttributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
229
+
230
+ // Filter out plugin attributes - they are internal and should not be replicated
231
+ const pluginAttrNames = resource.schema?._pluginAttributes
232
+ ? Object.values(resource.schema._pluginAttributes).flat()
233
+ : [];
234
+ const attributes = Object.fromEntries(
235
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
236
+ );
229
237
 
230
238
  // Sync each table configured for this resource
231
239
  for (const tableConfig of tableConfigs) {
@@ -7,6 +7,25 @@
7
7
 
8
8
  import tryFn from "#src/concerns/try-fn.js";
9
9
 
10
+ /**
11
+ * Filter out plugin attributes from attributes object
12
+ * Plugin attributes are internal implementation details and should not be replicated
13
+ * @param {Object} attributes - All attributes including plugin attributes
14
+ * @param {Object} resource - Resource instance with schema._pluginAttributes
15
+ * @returns {Object} Filtered attributes (user attributes only)
16
+ */
17
+ function filterPluginAttributes(attributes, resource) {
18
+ if (!resource?.schema?._pluginAttributes) {
19
+ return attributes;
20
+ }
21
+
22
+ const pluginAttrNames = Object.values(resource.schema._pluginAttributes).flat();
23
+
24
+ return Object.fromEntries(
25
+ Object.entries(attributes).filter(([name]) => !pluginAttrNames.includes(name))
26
+ );
27
+ }
28
+
10
29
  /**
11
30
  * Parse s3db field type notation (e.g., 'string|required|maxlength:50')
12
31
  */
@@ -177,7 +177,15 @@ class TursoReplicator extends BaseReplicator {
177
177
  continue;
178
178
  }
179
179
 
180
- const attributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
180
+ const allAttributes = resource.config.versions[resource.config.currentVersion]?.attributes || {};
181
+
182
+ // Filter out plugin attributes - they are internal and should not be replicated
183
+ const pluginAttrNames = resource.schema?._pluginAttributes
184
+ ? Object.values(resource.schema._pluginAttributes).flat()
185
+ : [];
186
+ const attributes = Object.fromEntries(
187
+ Object.entries(allAttributes).filter(([name]) => !pluginAttrNames.includes(name))
188
+ );
181
189
 
182
190
  for (const tableConfig of tableConfigs) {
183
191
  const tableName = tableConfig.table;