s3db.js 4.1.2 → 4.1.4
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 +42 -0
- package/dist/s3db.cjs.js +95 -6
- package/dist/s3db.cjs.min.js +8 -8
- package/dist/s3db.es.js +95 -6
- package/dist/s3db.es.min.js +8 -8
- package/dist/s3db.iife.js +95 -6
- package/dist/s3db.iife.min.js +9 -9
- package/package.json +1 -1
package/dist/s3db.iife.js
CHANGED
|
@@ -3474,6 +3474,7 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
3474
3474
|
this.attributes = attributes || {};
|
|
3475
3475
|
this.passphrase = passphrase ?? "secret";
|
|
3476
3476
|
this.options = lodashEs.merge({}, this.defaultOptions(), options);
|
|
3477
|
+
this.allNestedObjectsOptional = this.options.allNestedObjectsOptional ?? false;
|
|
3477
3478
|
const processedAttributes = this.preprocessAttributesForValidation(this.attributes);
|
|
3478
3479
|
this.validator = new ValidatorManager({ autoEncrypt: false }).compile(lodashEs.merge(
|
|
3479
3480
|
{ $$async: true },
|
|
@@ -3671,11 +3672,17 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
3671
3672
|
const processed = {};
|
|
3672
3673
|
for (const [key, value] of Object.entries(attributes)) {
|
|
3673
3674
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
3674
|
-
|
|
3675
|
+
const isExplicitRequired = value.$$type && value.$$type.includes("required");
|
|
3676
|
+
const isExplicitOptional = value.$$type && value.$$type.includes("optional");
|
|
3677
|
+
const objectConfig = {
|
|
3675
3678
|
type: "object",
|
|
3676
3679
|
properties: this.preprocessAttributesForValidation(value),
|
|
3677
3680
|
strict: false
|
|
3678
3681
|
};
|
|
3682
|
+
if (isExplicitRequired) ; else if (isExplicitOptional || this.allNestedObjectsOptional) {
|
|
3683
|
+
objectConfig.optional = true;
|
|
3684
|
+
}
|
|
3685
|
+
processed[key] = objectConfig;
|
|
3679
3686
|
} else {
|
|
3680
3687
|
processed[key] = value;
|
|
3681
3688
|
}
|
|
@@ -8837,6 +8844,7 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
8837
8844
|
partitions: {},
|
|
8838
8845
|
paranoid: true,
|
|
8839
8846
|
// Security flag for dangerous operations
|
|
8847
|
+
allNestedObjectsOptional: options.allNestedObjectsOptional ?? false,
|
|
8840
8848
|
...options
|
|
8841
8849
|
};
|
|
8842
8850
|
this.hooks = {
|
|
@@ -8871,7 +8879,10 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
8871
8879
|
attributes: this.attributes,
|
|
8872
8880
|
passphrase,
|
|
8873
8881
|
version: this.version,
|
|
8874
|
-
options:
|
|
8882
|
+
options: {
|
|
8883
|
+
...this.options,
|
|
8884
|
+
allNestedObjectsOptional: this.options.allNestedObjectsOptional ?? false
|
|
8885
|
+
}
|
|
8875
8886
|
});
|
|
8876
8887
|
this.validatePartitions();
|
|
8877
8888
|
this.setupPartitionHooks();
|
|
@@ -9798,7 +9809,7 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
9798
9809
|
this.version = "1";
|
|
9799
9810
|
this.s3dbVersion = (() => {
|
|
9800
9811
|
try {
|
|
9801
|
-
return true ? "4.1.
|
|
9812
|
+
return true ? "4.1.3" : "latest";
|
|
9802
9813
|
} catch (e) {
|
|
9803
9814
|
return "latest";
|
|
9804
9815
|
}
|
|
@@ -9910,16 +9921,21 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
9910
9921
|
/**
|
|
9911
9922
|
* Generate a consistent hash for a resource definition
|
|
9912
9923
|
* @param {Object} definition - Resource definition to hash
|
|
9924
|
+
* @param {string} behavior - Resource behavior
|
|
9913
9925
|
* @returns {string} SHA256 hash
|
|
9914
9926
|
*/
|
|
9915
|
-
generateDefinitionHash(definition) {
|
|
9927
|
+
generateDefinitionHash(definition, behavior = void 0) {
|
|
9916
9928
|
const attributes = definition.attributes;
|
|
9917
9929
|
const stableAttributes = { ...attributes };
|
|
9918
9930
|
if (definition.options?.timestamps) {
|
|
9919
9931
|
delete stableAttributes.createdAt;
|
|
9920
9932
|
delete stableAttributes.updatedAt;
|
|
9921
9933
|
}
|
|
9922
|
-
const
|
|
9934
|
+
const hashObj = {
|
|
9935
|
+
attributes: stableAttributes,
|
|
9936
|
+
behavior: behavior || definition.behavior || "user-management"
|
|
9937
|
+
};
|
|
9938
|
+
const stableString = jsonStableStringify(hashObj);
|
|
9923
9939
|
return `sha256:${crypto.createHash("sha256").update(stableString).digest("hex")}`;
|
|
9924
9940
|
}
|
|
9925
9941
|
/**
|
|
@@ -10006,6 +10022,73 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
10006
10022
|
resources: {}
|
|
10007
10023
|
};
|
|
10008
10024
|
}
|
|
10025
|
+
/**
|
|
10026
|
+
* Check if a resource exists by name
|
|
10027
|
+
* @param {string} name - Resource name
|
|
10028
|
+
* @returns {boolean} True if resource exists, false otherwise
|
|
10029
|
+
*/
|
|
10030
|
+
resourceExists(name) {
|
|
10031
|
+
return !!this.resources[name];
|
|
10032
|
+
}
|
|
10033
|
+
/**
|
|
10034
|
+
* Check if a resource exists with the same definition hash
|
|
10035
|
+
* @param {string} name - Resource name
|
|
10036
|
+
* @param {Object} attributes - Resource attributes
|
|
10037
|
+
* @param {Object} options - Resource options
|
|
10038
|
+
* @param {string} behavior - Resource behavior
|
|
10039
|
+
* @returns {Object} Object with exists flag and hash information
|
|
10040
|
+
*/
|
|
10041
|
+
resourceExistsWithSameHash({ name, attributes, options = {}, behavior = "user-management" }) {
|
|
10042
|
+
if (!this.resources[name]) {
|
|
10043
|
+
return { exists: false, sameHash: false, hash: null };
|
|
10044
|
+
}
|
|
10045
|
+
const tempResource = new Resource({
|
|
10046
|
+
name,
|
|
10047
|
+
attributes,
|
|
10048
|
+
behavior,
|
|
10049
|
+
observers: [],
|
|
10050
|
+
client: this.client,
|
|
10051
|
+
version: "temp",
|
|
10052
|
+
options: {
|
|
10053
|
+
cache: this.cache,
|
|
10054
|
+
...options
|
|
10055
|
+
}
|
|
10056
|
+
});
|
|
10057
|
+
const newHash = this.generateDefinitionHash(tempResource.export(), behavior);
|
|
10058
|
+
const existingHash = this.generateDefinitionHash(this.resources[name].export(), this.resources[name].behavior);
|
|
10059
|
+
return {
|
|
10060
|
+
exists: true,
|
|
10061
|
+
sameHash: newHash === existingHash,
|
|
10062
|
+
hash: newHash,
|
|
10063
|
+
existingHash
|
|
10064
|
+
};
|
|
10065
|
+
}
|
|
10066
|
+
/**
|
|
10067
|
+
* Create a resource only if it doesn't exist with the same definition hash
|
|
10068
|
+
* @param {Object} params - Resource parameters
|
|
10069
|
+
* @param {string} params.name - Resource name
|
|
10070
|
+
* @param {Object} params.attributes - Resource attributes
|
|
10071
|
+
* @param {Object} params.options - Resource options
|
|
10072
|
+
* @param {string} params.behavior - Resource behavior
|
|
10073
|
+
* @returns {Object} Object with resource and created flag
|
|
10074
|
+
*/
|
|
10075
|
+
async createResourceIfNotExists({ name, attributes, options = {}, behavior = "user-management" }) {
|
|
10076
|
+
const alreadyExists = !!this.resources[name];
|
|
10077
|
+
const hashCheck = this.resourceExistsWithSameHash({ name, attributes, options, behavior });
|
|
10078
|
+
if (hashCheck.exists && hashCheck.sameHash) {
|
|
10079
|
+
return {
|
|
10080
|
+
resource: this.resources[name],
|
|
10081
|
+
created: false,
|
|
10082
|
+
reason: "Resource already exists with same definition hash"
|
|
10083
|
+
};
|
|
10084
|
+
}
|
|
10085
|
+
const resource = await this.createResource({ name, attributes, options, behavior });
|
|
10086
|
+
return {
|
|
10087
|
+
resource,
|
|
10088
|
+
created: !alreadyExists,
|
|
10089
|
+
reason: alreadyExists ? "Resource updated with new definition" : "New resource created"
|
|
10090
|
+
};
|
|
10091
|
+
}
|
|
10009
10092
|
async createResource({ name, attributes, options = {}, behavior = "user-management" }) {
|
|
10010
10093
|
if (this.resources[name]) {
|
|
10011
10094
|
const existingResource = this.resources[name];
|
|
@@ -10017,7 +10100,13 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
10017
10100
|
existingResource.behavior = behavior;
|
|
10018
10101
|
}
|
|
10019
10102
|
existingResource.updateAttributes(attributes);
|
|
10020
|
-
|
|
10103
|
+
const newHash = this.generateDefinitionHash(existingResource.export(), existingResource.behavior);
|
|
10104
|
+
const existingMetadata2 = this.savedMetadata?.resources?.[name];
|
|
10105
|
+
const currentVersion = existingMetadata2?.currentVersion || "v0";
|
|
10106
|
+
const existingVersionData = existingMetadata2?.versions?.[currentVersion];
|
|
10107
|
+
if (!existingVersionData || existingVersionData.hash !== newHash) {
|
|
10108
|
+
await this.uploadMetadataFile();
|
|
10109
|
+
}
|
|
10021
10110
|
this.emit("s3db.resourceUpdated", name);
|
|
10022
10111
|
return existingResource;
|
|
10023
10112
|
}
|