s3db.js 4.0.2 → 4.1.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 +463 -7
- package/dist/s3db.cjs.js +127 -15
- package/dist/s3db.cjs.min.js +7 -7
- package/dist/s3db.es.js +128 -16
- package/dist/s3db.es.min.js +7 -7
- package/dist/s3db.iife.js +127 -15
- package/dist/s3db.iife.min.js +10 -10
- package/package.json +1 -1
package/dist/s3db.es.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* istanbul ignore file */
|
|
2
2
|
import { customAlphabet, urlAlphabet } from 'nanoid';
|
|
3
|
-
import { chunk, merge,
|
|
3
|
+
import { chunk, merge, isEmpty, invert, uniq, cloneDeep, isString as isString$1, get as get$1, set, isFunction as isFunction$1 } from 'lodash-es';
|
|
4
4
|
import { PromisePool } from '@supercharge/promise-pool';
|
|
5
5
|
import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, CopyObjectCommand, DeleteObjectCommand, DeleteObjectsCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
|
|
6
6
|
import { createHash } from 'crypto';
|
|
@@ -936,7 +936,7 @@ class Client extends EventEmitter {
|
|
|
936
936
|
Key: this.config.keyPrefix ? path.join(this.config.keyPrefix, key) : key
|
|
937
937
|
};
|
|
938
938
|
try {
|
|
939
|
-
const response = await this.
|
|
939
|
+
const response = await this.sendCommand(new HeadObjectCommand(options2));
|
|
940
940
|
this.emit("headObject", response, options2);
|
|
941
941
|
return response;
|
|
942
942
|
} catch (error) {
|
|
@@ -3480,9 +3480,10 @@ class Schema {
|
|
|
3480
3480
|
this.attributes = attributes || {};
|
|
3481
3481
|
this.passphrase = passphrase ?? "secret";
|
|
3482
3482
|
this.options = merge({}, this.defaultOptions(), options);
|
|
3483
|
+
const processedAttributes = this.preprocessAttributesForValidation(this.attributes);
|
|
3483
3484
|
this.validator = new ValidatorManager({ autoEncrypt: false }).compile(merge(
|
|
3484
3485
|
{ $$async: true },
|
|
3485
|
-
|
|
3486
|
+
processedAttributes
|
|
3486
3487
|
));
|
|
3487
3488
|
if (this.options.generateAutoHooks) this.generateAutoHooks();
|
|
3488
3489
|
if (!isEmpty(map)) {
|
|
@@ -3563,6 +3564,7 @@ class Schema {
|
|
|
3563
3564
|
version,
|
|
3564
3565
|
attributes
|
|
3565
3566
|
} = isString$1(data) ? JSON.parse(data) : data;
|
|
3567
|
+
attributes = Schema._importAttributes(attributes);
|
|
3566
3568
|
const schema = new Schema({
|
|
3567
3569
|
map,
|
|
3568
3570
|
name,
|
|
@@ -3572,22 +3574,60 @@ class Schema {
|
|
|
3572
3574
|
});
|
|
3573
3575
|
return schema;
|
|
3574
3576
|
}
|
|
3577
|
+
/**
|
|
3578
|
+
* Recursively import attributes, parsing only stringified objects (legacy)
|
|
3579
|
+
*/
|
|
3580
|
+
static _importAttributes(attrs) {
|
|
3581
|
+
if (typeof attrs === "string") {
|
|
3582
|
+
try {
|
|
3583
|
+
const parsed = JSON.parse(attrs);
|
|
3584
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
3585
|
+
return Schema._importAttributes(parsed);
|
|
3586
|
+
}
|
|
3587
|
+
} catch (e) {
|
|
3588
|
+
}
|
|
3589
|
+
return attrs;
|
|
3590
|
+
}
|
|
3591
|
+
if (Array.isArray(attrs)) {
|
|
3592
|
+
return attrs.map((a) => Schema._importAttributes(a));
|
|
3593
|
+
}
|
|
3594
|
+
if (typeof attrs === "object" && attrs !== null) {
|
|
3595
|
+
const out = {};
|
|
3596
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
3597
|
+
out[k] = Schema._importAttributes(v);
|
|
3598
|
+
}
|
|
3599
|
+
return out;
|
|
3600
|
+
}
|
|
3601
|
+
return attrs;
|
|
3602
|
+
}
|
|
3575
3603
|
export() {
|
|
3576
3604
|
const data = {
|
|
3577
3605
|
version: this.version,
|
|
3578
3606
|
name: this.name,
|
|
3579
3607
|
options: this.options,
|
|
3580
|
-
attributes:
|
|
3608
|
+
attributes: this._exportAttributes(this.attributes),
|
|
3581
3609
|
map: this.map
|
|
3582
3610
|
};
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3611
|
+
return data;
|
|
3612
|
+
}
|
|
3613
|
+
/**
|
|
3614
|
+
* Recursively export attributes, keeping objects as objects and only serializing leaves as string
|
|
3615
|
+
*/
|
|
3616
|
+
_exportAttributes(attrs) {
|
|
3617
|
+
if (typeof attrs === "string") {
|
|
3618
|
+
return attrs;
|
|
3619
|
+
}
|
|
3620
|
+
if (Array.isArray(attrs)) {
|
|
3621
|
+
return attrs.map((a) => this._exportAttributes(a));
|
|
3622
|
+
}
|
|
3623
|
+
if (typeof attrs === "object" && attrs !== null) {
|
|
3624
|
+
const out = {};
|
|
3625
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
3626
|
+
out[k] = this._exportAttributes(v);
|
|
3588
3627
|
}
|
|
3628
|
+
return out;
|
|
3589
3629
|
}
|
|
3590
|
-
return
|
|
3630
|
+
return attrs;
|
|
3591
3631
|
}
|
|
3592
3632
|
async applyHooksActions(resourceItem, hook) {
|
|
3593
3633
|
for (const [attribute, actions] of Object.entries(this.options.hooks[hook])) {
|
|
@@ -3628,6 +3668,26 @@ class Schema {
|
|
|
3628
3668
|
await this.applyHooksActions(rest, "afterUnmap");
|
|
3629
3669
|
return unflatten(rest);
|
|
3630
3670
|
}
|
|
3671
|
+
/**
|
|
3672
|
+
* Preprocess attributes to convert nested objects into validator-compatible format
|
|
3673
|
+
* @param {Object} attributes - Original attributes
|
|
3674
|
+
* @returns {Object} Processed attributes for validator
|
|
3675
|
+
*/
|
|
3676
|
+
preprocessAttributesForValidation(attributes) {
|
|
3677
|
+
const processed = {};
|
|
3678
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
3679
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3680
|
+
processed[key] = {
|
|
3681
|
+
type: "object",
|
|
3682
|
+
properties: this.preprocessAttributesForValidation(value),
|
|
3683
|
+
strict: false
|
|
3684
|
+
};
|
|
3685
|
+
} else {
|
|
3686
|
+
processed[key] = value;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
return processed;
|
|
3690
|
+
}
|
|
3631
3691
|
}
|
|
3632
3692
|
|
|
3633
3693
|
var global$1 = (typeof global !== "undefined" ? global :
|
|
@@ -8943,7 +9003,7 @@ class Resource extends EventEmitter {
|
|
|
8943
9003
|
continue;
|
|
8944
9004
|
}
|
|
8945
9005
|
for (const fieldName of Object.keys(partitionDef.fields)) {
|
|
8946
|
-
if (!
|
|
9006
|
+
if (!this.fieldExistsInAttributes(fieldName)) {
|
|
8947
9007
|
throw new Error(
|
|
8948
9008
|
`Partition '${partitionName}' uses field '${fieldName}' which does not exist in resource version '${this.version}'. Available fields: ${currentAttributes.join(", ")}. This version of resource does not have support for this partition.`
|
|
8949
9009
|
);
|
|
@@ -8951,6 +9011,25 @@ class Resource extends EventEmitter {
|
|
|
8951
9011
|
}
|
|
8952
9012
|
}
|
|
8953
9013
|
}
|
|
9014
|
+
/**
|
|
9015
|
+
* Check if a field (including nested fields) exists in the current attributes
|
|
9016
|
+
* @param {string} fieldName - Field name (can be nested like 'utm.source')
|
|
9017
|
+
* @returns {boolean} True if field exists
|
|
9018
|
+
*/
|
|
9019
|
+
fieldExistsInAttributes(fieldName) {
|
|
9020
|
+
if (!fieldName.includes(".")) {
|
|
9021
|
+
return Object.keys(this.attributes || {}).includes(fieldName);
|
|
9022
|
+
}
|
|
9023
|
+
const keys = fieldName.split(".");
|
|
9024
|
+
let currentLevel = this.attributes || {};
|
|
9025
|
+
for (const key of keys) {
|
|
9026
|
+
if (!currentLevel || typeof currentLevel !== "object" || !(key in currentLevel)) {
|
|
9027
|
+
return false;
|
|
9028
|
+
}
|
|
9029
|
+
currentLevel = currentLevel[key];
|
|
9030
|
+
}
|
|
9031
|
+
return true;
|
|
9032
|
+
}
|
|
8954
9033
|
/**
|
|
8955
9034
|
* Apply a single partition rule to a field value
|
|
8956
9035
|
* @param {*} value - The field value
|
|
@@ -9013,17 +9092,38 @@ class Resource extends EventEmitter {
|
|
|
9013
9092
|
const partitionSegments = [];
|
|
9014
9093
|
const sortedFields = Object.entries(partition.fields).sort(([a], [b]) => a.localeCompare(b));
|
|
9015
9094
|
for (const [fieldName, rule] of sortedFields) {
|
|
9016
|
-
const fieldValue = this.
|
|
9017
|
-
|
|
9095
|
+
const fieldValue = this.getNestedFieldValue(data, fieldName);
|
|
9096
|
+
const transformedValue = this.applyPartitionRule(fieldValue, rule);
|
|
9097
|
+
if (transformedValue === void 0 || transformedValue === null) {
|
|
9018
9098
|
return null;
|
|
9019
9099
|
}
|
|
9020
|
-
partitionSegments.push(`${fieldName}=${
|
|
9100
|
+
partitionSegments.push(`${fieldName}=${transformedValue}`);
|
|
9021
9101
|
}
|
|
9022
9102
|
if (partitionSegments.length === 0) {
|
|
9023
9103
|
return null;
|
|
9024
9104
|
}
|
|
9025
9105
|
return join(`resource=${this.name}`, `partition=${partitionName}`, ...partitionSegments, `id=${id}`);
|
|
9026
9106
|
}
|
|
9107
|
+
/**
|
|
9108
|
+
* Get nested field value from data object using dot notation
|
|
9109
|
+
* @param {Object} data - Data object
|
|
9110
|
+
* @param {string} fieldPath - Field path (e.g., "utm.source", "address.city")
|
|
9111
|
+
* @returns {*} Field value
|
|
9112
|
+
*/
|
|
9113
|
+
getNestedFieldValue(data, fieldPath) {
|
|
9114
|
+
if (!fieldPath.includes(".")) {
|
|
9115
|
+
return data[fieldPath];
|
|
9116
|
+
}
|
|
9117
|
+
const keys = fieldPath.split(".");
|
|
9118
|
+
let value = data;
|
|
9119
|
+
for (const key of keys) {
|
|
9120
|
+
if (value === null || value === void 0 || typeof value !== "object") {
|
|
9121
|
+
return void 0;
|
|
9122
|
+
}
|
|
9123
|
+
value = value[key];
|
|
9124
|
+
}
|
|
9125
|
+
return value;
|
|
9126
|
+
}
|
|
9027
9127
|
async insert({ id, ...attributes }) {
|
|
9028
9128
|
if (this.options.timestamps) {
|
|
9029
9129
|
attributes.createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -9505,7 +9605,19 @@ class Resource extends EventEmitter {
|
|
|
9505
9605
|
*/
|
|
9506
9606
|
getDefinitionHash() {
|
|
9507
9607
|
const exportedSchema = this.schema.export();
|
|
9508
|
-
const
|
|
9608
|
+
const stableSchema = {
|
|
9609
|
+
...exportedSchema,
|
|
9610
|
+
attributes: { ...exportedSchema.attributes }
|
|
9611
|
+
};
|
|
9612
|
+
if (this.options.timestamps) {
|
|
9613
|
+
delete stableSchema.attributes.createdAt;
|
|
9614
|
+
delete stableSchema.attributes.updatedAt;
|
|
9615
|
+
if (stableSchema.options && stableSchema.options.partitions) {
|
|
9616
|
+
delete stableSchema.options.partitions.byCreatedDate;
|
|
9617
|
+
delete stableSchema.options.partitions.byUpdatedDate;
|
|
9618
|
+
}
|
|
9619
|
+
}
|
|
9620
|
+
const stableString = jsonStableStringify(stableSchema);
|
|
9509
9621
|
return `sha256:${createHash("sha256").update(stableString).digest("hex")}`;
|
|
9510
9622
|
}
|
|
9511
9623
|
/**
|
|
@@ -9699,7 +9811,7 @@ class Database extends EventEmitter {
|
|
|
9699
9811
|
this.version = "1";
|
|
9700
9812
|
this.s3dbVersion = (() => {
|
|
9701
9813
|
try {
|
|
9702
|
-
return true ? "4.0
|
|
9814
|
+
return true ? "4.1.0" : "latest";
|
|
9703
9815
|
} catch (e) {
|
|
9704
9816
|
return "latest";
|
|
9705
9817
|
}
|