s3db.js 3.2.4 → 3.2.5
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/dist/s3db.cjs.js +2 -2
- package/dist/s3db.cjs.min.js +1 -1
- package/dist/s3db.es.js +2 -2
- package/dist/s3db.es.min.js +1 -1
- package/dist/s3db.iife.js +2 -2
- package/dist/s3db.iife.min.js +1 -1
- package/package.json +1 -1
package/dist/s3db.cjs.js
CHANGED
|
@@ -1347,7 +1347,7 @@ const SchemaActions = {
|
|
|
1347
1347
|
toJSON: (value) => JSON.stringify(value),
|
|
1348
1348
|
fromJSON: (value) => JSON.parse(value),
|
|
1349
1349
|
toNumber: (value) => lodashEs.isString(value) ? value.includes(".") ? parseFloat(value) : parseInt(value) : value,
|
|
1350
|
-
toBool: (value) => ["
|
|
1350
|
+
toBool: (value) => ["1", "true", "yes", true, "y"].includes(value),
|
|
1351
1351
|
fromBool: (value) => value ? "1" : "0"
|
|
1352
1352
|
};
|
|
1353
1353
|
class Schema {
|
|
@@ -1457,7 +1457,7 @@ class Schema {
|
|
|
1457
1457
|
for (const [attribute, actions] of Object.entries(this.options.hooks[hook])) {
|
|
1458
1458
|
for (const action of actions) {
|
|
1459
1459
|
const value = lodashEs.get(resourceItem, attribute);
|
|
1460
|
-
if (value) {
|
|
1460
|
+
if (value !== void 0) {
|
|
1461
1461
|
lodashEs.set(resourceItem, attribute, await SchemaActions[action](value, {
|
|
1462
1462
|
passphrase: this.passphrase,
|
|
1463
1463
|
separator: this.options.arraySeparator
|
package/dist/s3db.cjs.min.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
Verbose:
|
|
5
5
|
|
|
6
6
|
${JSON.stringify(n,null,2)}`),super(i),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(i).stack,super.name=this.constructor.name,this.name=this.constructor.name,this.bucket=r,this.thrownAt=new Date}toJson(){return{...this}}toString(){return`${this.name} | ${this.message}`}}class di extends We{constructor({bucket:t,...r}){super({...r,bucket:t,message:`Bucket does not exists [bucket:${t}]`})}}class or extends We{constructor({bucket:t,key:r,...i}){super({...i,bucket:t,message:`Key [${r}] does not exists [bucket:${t}/${r}]`}),this.key=r}}class pi extends or{}class _i extends We{constructor({bucket:t,...r}){super({...r,bucket:t,message:`Missing metadata for bucket [bucket:${t}]`})}}class Tt extends We{constructor({bucket:t,resourceName:r,attributes:i,validation:n}){super({bucket:t,message:`This item is not valid. Resource=${r} [bucket:${t}].
|
|
7
|
-
${JSON.stringify(n,null,2)}`}),this.resourceName=r,this.attributes=i,this.validation=n}}class wi extends We{}const gi={NotFound:pi,NoSuchKey:or,UnknownError:wi,NoSuchBucket:di,MissingMetadata:_i,InvalidResourceItem:Tt},vi="us-east-1",mi="https://s3.us-east-1.amazonaws.com";class bi{constructor(t){let r;try{r=new URL(t)}catch{throw new Error("Invalid connection string: "+t)}this.region=vi,r.protocol==="s3:"?this.defineS3(r):this.defineMinio(r);for(const[i,n]of r.searchParams.entries())this[i]=n}defineS3(t){if(this.bucket=t.hostname,this.accessKeyId=t.username,this.secretAccessKey=t.password,this.endpoint=mi,["/","",null].includes(t.pathname))this.keyPrefix="";else{let[,...r]=t.pathname.split("/");this.keyPrefix=[...r||[]].join("/")}}defineMinio(t){if(this.forcePathStyle=!0,this.endpoint=t.origin,this.accessKeyId=t.username,this.secretAccessKey=t.password,["/","",null].includes(t.pathname))this.bucket="s3db",this.keyPrefix="";else{let[,r,...i]=t.pathname.split("/");this.bucket=r,this.keyPrefix=[...i||[]].join("/")}}}class yi extends b{constructor({verbose:t=!1,id:r=null,AwsS3Client:i,connectionString:n,parallelism:a=10}){super(),this.verbose=t,this.id=r??ai.nanoid(7),this.parallelism=a,this.config=new bi(n),this.client=i||this.createClient()}createClient(){let t={region:this.config.region,endpoint:this.config.endpoint};return this.config.forcePathStyle&&(t.forcePathStyle=!0),this.config.accessKeyId&&(t.credentials={accessKeyId:this.config.accessKeyId,secretAccessKey:this.config.secretAccessKey}),new de.S3Client(t)}async sendCommand(t){this.emit("command.request",t.constructor.name,t.input);const r=console.warn;try{console.warn=n=>{n.includes("Stream of unknown length")||r(n)}}catch(n){console.error(n)}const i=await this.client.send(t);this.emit("command.response",t.constructor.name,i,t.input);try{console.warn=r}catch(n){console.error(n)}return i}errorProxy(t,r){this.verbose&&(r.bucket=this.config.bucket,r.config=this.config,r.verbose=this.verbose),t.data=r;const i=gi[t.name];return i?new i(r):t}async putObject({key:t,metadata:r,contentType:i,body:n,contentEncoding:a}){const o={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t,Metadata:{...r},Body:n||"",ContentType:i,ContentEncoding:a};try{const s=await this.sendCommand(new de.PutObjectCommand(o));return this.emit("putObject",s,o),s}catch(s){throw this.errorProxy(s,{key:t,command:o})}}async getObject(t){const r={Bucket:this.config.bucket,Key:ne.join(this.config.keyPrefix,t)};try{const i=await this.sendCommand(new de.GetObjectCommand(r));return this.emit("getObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async headObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t};try{const i=await this.client.send(new de.HeadObjectCommand(r));return this.emit("headObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async copyObject({from:t,to:r}){const i={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,r):r,CopySource:ne.join(this.config.bucket,this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t)};try{const n=await this.client.send(new de.CopyObjectCommand(i));return this.emit("copyObject",n,i),n}catch(n){throw this.errorProxy(n,{from:t,to:r,command:i})}}async exists(t){try{return await this.headObject(t),!0}catch(r){if(r.name==="NoSuchKey")return!1;if(r.name==="NotFound")return!1;throw r}}async deleteObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t};try{const i=await this.sendCommand(new de.DeleteObjectCommand(r));return this.emit("deleteObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async deleteObjects(t){const r=I.chunk(t,1e3),{results:i,errors:n}=await ue.PromisePool.for(r).withConcurrency(this.parallelism).process(async o=>{const s={Bucket:this.config.bucket,Delete:{Objects:o.map(h=>({Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,h):h}))}};try{return await this.sendCommand(new de.DeleteObjectsCommand(s))}catch(h){throw this.errorProxy(h,{key,command:s})}}),a={deleted:i,notFound:n};return this.emit("deleteObjects",a,t),a}async deleteAll({prefix:t}={}){const r=await this.getAllKeys({prefix:t}),i=await this.deleteObjects(r);return this.emit("deleteAll",{prefix:t,report:i}),i}async moveObject({from:t,to:r}){try{return await this.copyObject({from:t,to:r}),await this.deleteObject(t),!0}catch(i){throw this.errorProxy(i,{from:t,to:r,command:options})}}async listObjects({prefix:t,maxKeys:r=1e3,continuationToken:i}={}){const n={Bucket:this.config.bucket,MaxKeys:r,ContinuationToken:i,Prefix:this.config.keyPrefix?ne.join(this.config.keyPrefix,t||""):t||""};try{const a=await this.sendCommand(new de.ListObjectsV2Command(n));return this.emit("listObjects",a,n),a}catch(a){throw this.errorProxy(a,{command:n})}}async count({prefix:t}={}){let r=0,i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);r+=o.KeyCount||0,i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.emit("count",r,{prefix:t}),r}async getAllKeys({prefix:t}={}){let r=[],i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);o.Contents&&(r=r.concat(o.Contents.map(s=>s.Key))),i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.config.keyPrefix&&(r=r.map(a=>a.replace(this.config.keyPrefix,"")).map(a=>a.startsWith("/")?a.replace("/",""):a)),this.emit("getAllKeys",r,{prefix:t}),r}async getContinuationTokenAfterOffset(t={}){const{prefix:r,offset:i=1e3}=t;if(i===0)return null;let n=!0,a,o=0;for(;n;){let s=i<1e3?i:i-o>1e3?1e3:i-o;const h={prefix:r,maxKeys:s,continuationToken:a},f=await this.listObjects(h);if(f.Contents&&(o+=f.Contents.length),n=f.IsTruncated||!1,a=f.NextContinuationToken,o>=i)break}return this.emit("getContinuationTokenAfterOffset",a,t),a}async getKeysPage(t={}){const{prefix:r,offset:i=0,amount:n=100}=t;let a=[],o=!0,s;for(i>0&&(s=await this.getContinuationTokenAfterOffset({prefix:r,offset:i}));o;){const h={prefix:r,continuationToken:s},f=await this.listObjects(h);if(f.Contents&&(a=a.concat(f.Contents.map(l=>l.Key))),o=f.IsTruncated||!1,s=f.NextContinuationToken,a.length>n){a=a.splice(0,n);break}}return this.config.keyPrefix&&(a=a.map(h=>h.replace(this.config.keyPrefix,"")).map(h=>h.startsWith("/")?h.replace("/",""):h)),this.emit("getKeysPage",a,t),a}async moveAllObjects({prefixFrom:t,prefixTo:r}){const i=await this.getAllKeys({prefix:t}),{results:n,errors:a}=await ue.PromisePool.for(i).withConcurrency(this.parallelism).process(async o=>{const s=o.replace(t,r);try{return await this.moveObject({from:o,to:s}),s}catch(h){throw this.errorProxy(h,{from:o,to:s})}});if(this.emit("moveAllObjects",{results:n,errors:a},{prefixFrom:t,prefixTo:r}),a.length>0)throw console.log({errors:a}),new Error("Some objects could not be moved");return n}}async function Lt(){let e;if(process)try{const{webcrypto:t}=await import("crypto");e=t}catch{throw new Error("Crypto API not available")}else window&&(e=window.crypto);if(!e)throw new Error("Could not load any crypto library");return e}async function xi(e){const t=await Lt(),i=new TextEncoder().encode(e),n=await t.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(n)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function sr(e,t){const r=await Lt(),i=r.getRandomValues(new Uint8Array(16)),n=await ki(t,i),a=r.getRandomValues(new Uint8Array(12)),s=new TextEncoder().encode(e),h=await r.subtle.encrypt({name:"AES-GCM",iv:a},n,s),f=new Uint8Array(i.length+a.length+h.byteLength);return f.set(i),f.set(a,i.length),f.set(new Uint8Array(h),i.length+a.length),po(f)}async function Ei(e,t){const r=await Lt(),i=_o(e),n=i.slice(0,16),a=i.slice(16,28),o=i.slice(28),s=await ki(t,n),h=await r.subtle.decrypt({name:"AES-GCM",iv:a},s,o);return new TextDecoder().decode(h)}async function ki(e,t){const r=await Lt(),n=new TextEncoder().encode(e),a=await r.subtle.importKey("raw",n,{name:"PBKDF2"},!1,["deriveKey"]);return await r.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},a,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}function po(e){if(I.isObject(process))return Buffer.from(e).toString("base64");{const t=String.fromCharCode.apply(null,new Uint8Array(e));return window.btoa(t)}}function _o(e){if(I.isObject(process))return new Uint8Array(Buffer.from(e,"base64"));{const t=window.atob(e),r=t.length,i=new Uint8Array(r);for(let n=0;n<r;n++)i[n]=t.charCodeAt(n);return i}}async function fr(e,t,r){if(!this.passphrase)return t.push({actual:e,type:"encryptionKeyMissing"}),e;try{return await sr(String(e),this.passphrase)}catch(i){t.push({actual:e,type:"encryptionProblem",error:i})}return e}class Si extends Ga{constructor({options:t,passphrase:r,autoEncrypt:i=!0}={}){super(I.merge({},{useNewCustomCheckerFunction:!0,messages:{encryptionKeyMissing:"Missing configuration for secrets encryption.",encryptionProblem:"Problem encrypting secret. Actual: {actual}. Error: {error}"},defaults:{string:{trim:!0},object:{strict:"remove"}}},t)),this.passphrase=r,this.autoEncrypt=i,this.alias("secret",{type:"string",custom:this.autoEncrypt?fr:void 0,messages:{string:"The '{field}' field must be a string.",stringMin:"This secret '{field}' field length must be at least {expected} long."}}),this.alias("secretAny",{type:"any",custom:this.autoEncrypt?fr:void 0}),this.alias("secretNumber",{type:"number",custom:this.autoEncrypt?fr:void 0})}}const Ri=new Proxy(Si,{instance:null,construct(e,t){return this.instance||(this.instance=new e(...t)),this.instance}}),wo={trim:e=>e.trim(),encrypt:(e,{passphrase:t})=>sr(e,t),decrypt:(e,{passphrase:t})=>Ei(e,t),toString:e=>String(e),fromArray:(e,{separator:t})=>(e||[]).join(t),toArray:(e,{separator:t})=>(e||"").split(t),toJSON:e=>JSON.stringify(e),fromJSON:e=>JSON.parse(e),toNumber:e=>I.isString(e)?e.includes(".")?parseFloat(e):parseInt(e):e,toBool:e=>["true","1","yes"].includes(e),fromBool:e=>e?"1":"0"};class ti{constructor(t){const{map:r,name:i,attributes:n,passphrase:a,version:o=1,options:s={}}=t;if(this.name=i,this.version=o,this.attributes=n,this.passphrase=a??"secret",this.options=I.merge({},this.defaultOptions(),s),this.validator=new Ri({autoEncrypt:!1}).compile(I.merge({$$async:!0},I.cloneDeep(this.attributes))),this.options.generateAutoHooks&&this.generateAutoHooks(),!I.isEmpty(r))this.map=r,this.reversedMap=I.invert(r);else{const h=At.flatten(this.attributes,{safe:!0});this.reversedMap={...Object.keys(h).filter(f=>!f.includes("$$"))},this.map=I.invert(this.reversedMap)}}defaultOptions(){return{autoEncrypt:!0,autoDecrypt:!0,arraySeparator:"|",generateAutoHooks:!0,hooks:{beforeMap:{},afterMap:{},beforeUnmap:{},afterUnmap:{}}}}addHook(t,r,i){this.options.hooks[t][r]||(this.options.hooks[t][r]=[]),this.options.hooks[t][r]=I.uniq([...this.options.hooks[t][r],i])}generateAutoHooks(){const t=At.flatten(I.cloneDeep(this.attributes),{safe:!0});for(const[r,i]of Object.entries(t))i.includes("array")?(this.addHook("beforeMap",r,"fromArray"),this.addHook("afterUnmap",r,"toArray")):(i.includes("secret")&&(this.options.autoEncrypt&&this.addHook("beforeMap",r,"encrypt"),this.options.autoDecrypt&&this.addHook("afterUnmap",r,"decrypt")),i.includes("number")&&(this.addHook("beforeMap",r,"toString"),this.addHook("afterUnmap",r,"toNumber")),i.includes("boolean")&&(this.addHook("beforeMap",r,"fromBool"),this.addHook("afterUnmap",r,"toBool")))}static import(t){let{map:r,name:i,options:n,version:a,attributes:o}=I.isString(t)?JSON.parse(t):t;return new ti({map:r,name:i,options:n,version:a,attributes:o})}export(){const t={version:this.version,name:this.name,options:this.options,attributes:I.cloneDeep(this.attributes),map:this.map};for(const[r,i]of Object.entries(this.attributes))t.attributes[r]=JSON.stringify(i);return t}async applyHooksActions(t,r){for(const[i,n]of Object.entries(this.options.hooks[r]))for(const a of n){const o=I.get(t,i);o&&I.set(t,i,await wo[a](o,{passphrase:this.passphrase,separator:this.options.arraySeparator}))}}async validate(t,{mutateOriginal:r=!1}={}){let i=r?t:I.cloneDeep(t);return await this.validator(i)}async mapper(t){const r=At.flatten(I.cloneDeep(t),{safe:!0});await this.applyHooksActions(r,"beforeMap");const i={_v:this.version+""};for(const[n,a]of Object.entries(r))i[this.map[n]]=a;return await this.applyHooksActions(i,"afterMap"),i}async unmapper(t){const r=I.cloneDeep(t);delete r._v,await this.applyHooksActions(r,"beforeUnmap");const i={};for(const[n,a]of Object.entries(r))i[this.reversedMap[n]]=a;return await this.applyHooksActions(i,"afterUnmap"),At.unflatten(i)}}class Ai extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new rr.ReadableStream({highWaterMark:this.client.parallelism*3,start:this._start.bind(this),pull:this._pull.bind(this),cancel:this._cancel.bind(this)})}build(){return this.stream.getReader()}async _start(t){this.controller=t,this.continuationToken=null,this.closeNextIteration=!1}async _pull(t){if(this.closeNextIteration){t.close();return}const r=await this.client.listObjects({prefix:`resource=${this.resource.name}`,continuationToken:this.continuationToken}),i=r?.Contents.map(n=>n.Key).map(n=>n.replace(this.client.config.keyPrefix,"")).map(n=>n.startsWith("/")?n.replace("/",""):n).map(n=>n.replace(`resource=${this.resource.name}/id=`,""));this.continuationToken=r.NextContinuationToken,this.enqueue(i),r.IsTruncated||(this.closeNextIteration=!0)}enqueue(t){t.forEach(r=>{this.controller.enqueue(r),this.emit("id",r)})}_cancel(t){console.warn("Stream cancelled",t)}}class Ti extends Ai{enqueue(t){this.controller.enqueue(t),this.emit("page",t)}}class Li extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.input=new Ti({resource:this.resource}),this.output=new rr.TransformStream({transform:this._transform.bind(this)},{highWaterMark:this.client.parallelism*2},{highWaterMark:1}),this.stream=this.input.stream.pipeThrough(this.output)}build(){return this.stream.getReader()}async _transform(t,r){await ue.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{const n=await this.resource.get(i);return r.enqueue(n),n})}}class Ci extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new rr.WritableStream({start:this._start.bind(this),write:this._write.bind(this),close:this._close.bind(this),abort:this._abort.bind(this)})}build(){return this.stream.getWriter()}async _start(t){this.controller=t}async _write(t,r){const i=this.resource;await ue.PromisePool.for([].concat(t)).withConcurrency(this.client.parallelism).process(async n=>{await i.insert(n)})}async _close(t){}async _abort(t){console.error("Stream aborted:",t)}}function lr(e){return new Promise((t,r)=>{const i=[];e.on("data",n=>i.push(n)),e.on("error",r),e.on("end",()=>t(Buffer.concat(i).toString("utf-8")))})}class Oi extends b{constructor({name:t,client:r,options:i={},attributes:n={},parallelism:a=10,passphrase:o="secret",observers:s=[]}){super(),this.name=t,this.client=r,this.options=i,this.observers=s,this.parallelism=a,this.passphrase=o??"secret",this.schema=new ti({name:t,attributes:n,passphrase:o})}export(){return this.schema.export()}async validate(t){const r={original:I.cloneDeep(t),isValid:!1,errors:[]},i=await this.schema.validate(t,{mutateOriginal:!0});return i===!0?r.isValid=!0:r.errors=i,r.data=t,r}async insert({id:t,...r}){const{errors:i,isValid:n,data:a}=await this.validate(r);if(!n)throw new Tt({bucket:this.client.config.bucket,resourceName:this.name,attributes:r,validation:i});!t&&t!==0&&(t=ai.nanoid());const o=await this.schema.mapper(a);await this.client.putObject({metadata:o,key:W(`resource=${this.name}`,`id=${t}`)});const s=I.merge({id:t},a);return this.emit("insert",s),s}async get(t){const r=await this.client.headObject(W(`resource=${this.name}`,`id=${t}`));let i=await this.schema.unmapper(r.Metadata);return i.id=t,i._length=r.ContentLength,i._createdAt=r.LastModified,r.Expiration&&(i._expiresAt=r.Expiration),this.emit("get",i),i}async exists(t){try{return await this.client.headObject(W(`resource=${this.name}`,`id=${t}`)),!0}catch{return!1}}async update(t,r){const i=await this.get(t),n=I.merge(i,r);delete n.id;const{isValid:a,errors:o,data:s}=await this.validate(n);if(!a)throw new Tt({bucket:this.client.bucket,resourceName:this.name,attributes:r,validation:o});return await this.client.putObject({key:W(`resource=${this.name}`,`id=${t}`),body:"",metadata:await this.schema.mapper(s)}),s.id=t,this.emit("update",r,s),s}async delete(t){const r=W(`resource=${this.name}`,`id=${t}`),i=await this.client.deleteObject(r);return this.emit("delete",t),i}async count(){const t=await this.client.count({prefix:`resource=${this.name}`});return this.emit("count",t),t}async insertMany(t){const{results:r}=await ue.PromisePool.for(t).withConcurrency(this.parallelism).handleError(async(i,n)=>{this.emit("error",i,n),this.observers.map(a=>a.emit("error",this.name,i,n))}).process(async i=>await this.insert(i));return this.emit("insertMany",t.length),r}async deleteMany(t){const r=I.chunk(t.map(n=>W(`resource=${this.name}`,`id=${n}`)),1e3),{results:i}=await ue.PromisePool.for(r).withConcurrency(this.parallelism).handleError(async(n,a)=>{this.emit("error",n,a),this.observers.map(o=>o.emit("error",this.name,n,a))}).process(async n=>{const a=await this.client.deleteObjects(n);return n.forEach(o=>{const s=o.split("=").pop();this.emit("deleted",s),this.observers.map(h=>h.emit("deleted",this.name,s))}),a});return this.emit("deleteMany",t.length),i}async deleteAll(){const t=await this.listIds();this.emit("deleteAll",t.length),await this.deleteMany(t)}async listIds(){const r=(await this.client.getAllKeys({prefix:`resource=${this.name}`})).map(i=>i.replace(`resource=${this.name}/id=`,""));return this.emit("listIds",r.length),r}async getMany(t){const{results:r}=await ue.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{this.emit("id",i);const n=await this.get(i);return this.emit("data",n),n});return this.emit("getMany",t.length),r}async getAll(){let t=await this.listIds();if(t.length===0)return[];const{results:r}=await ue.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>await this.get(i));return this.emit("getAll",r.length),r}async page({offset:t=0,size:r=100}){const n=(await this.client.getKeysPage({offset:t,amount:r,prefix:`resource=${this.name}`})).map(o=>o.replace(`resource=${this.name}/id=`,""));return await this.getMany(n)}readable(){return new Li({resource:this}).build()}writable(){return new Ci({resource:this}).build()}}class Ii extends b{constructor(t){super(),this.version="1",this.resources={},this.options=t,this.verbose=t.verbose||!1,this.parallelism=parseInt(t.parallelism+"")||10,this.plugins=t.plugins||[],this.cache=t.cache,this.passphrase=t.passphrase||"secret",this.client=t.client||new yi({verbose:this.verbose,parallelism:this.parallelism,connectionString:t.connectionString}),this.bucket=this.client.bucket,this.keyPrefix=this.client.keyPrefix}async connect(){await this.startPlugins();let t=null;if(await this.client.exists("s3db.json")){const r=await this.client.getObject("s3db.json");t=JSON.parse(await lr(r?.Body)),t=this.unserializeMetadata(t)}else t=this.blankMetadataStructure(),await this.uploadMetadataFile();for(const r of Object.entries(t.resources)){const[i,n]=r;this.resources[i]=new Oi({name:i,client:this.client,options:n.options,attributes:n.schema,parallelism:this.parallelism,passphrase:this.passphrase,observers:[this]})}this.emit("connected",new Date)}async startPlugins(){const t=this;if(!I.isEmpty(this.plugins)){const r=this.plugins.map(a=>I.isFunction(a)?new a(this):a),i=r.map(async a=>{a.beforeSetup&&await a.beforeSetup(),await a.setup(t),a.afterSetup&&await a.afterSetup()});await Promise.all(i);const n=r.map(async a=>{a.beforeStart&&await a.beforeStart(),await a.start(),a.afterStart&&await a.afterStart()});await Promise.all(n)}}unserializeMetadata(t){const r={...t};if(I.isEmpty(r.resources))return r;for(const[i,n]of Object.entries(r.resources))for(const[a,o]of Object.entries(n.attributes))r.resources[i].attributes[a]=JSON.parse(o);return r}async uploadMetadataFile(){const t={version:this.version,resources:Object.entries(this.resources).reduce((r,i)=>{const[n,a]=i;return r[n]=a.export(),r},{})};await this.client.putObject({key:"s3db.json",contentType:"application/json",body:JSON.stringify(t,null,2)})}blankMetadataStructure(){return{version:"1",resources:{}}}async createResource({name:t,attributes:r,options:i={}}){const n=new Oi({name:t,attributes:r,observers:[this],client:this.client,options:{autoDecrypt:!0,cache:this.cache,...i}});return this.resources[t]=n,await this.uploadMetadataFile(),this.emit("s3db.resourceCreated",t),n}resource(t){return this.resources[t]?this.resources[t]:Promise.reject(`resource ${t} does not exist`)}}class go extends Ii{}class hr extends b{async _set(t,r){}async _get(t){}async _del(t){}async _clear(t){}async set(t,r){return await this._set(t,r),this.emit("set",r),r}async get(t){const r=await this._get(t);return this.emit("get",r),r}async del(t){const r=await this._del(t);return this.emit("delete",r),r}async clear(){const t=await this._clear();return this.emit("clear",t),t}}class vo extends hr{constructor(){super(),this.cache={}}async _set(t,r){return this.cache[t]=r,r}async _get(t){return this.cache[t]}async _del(t){return delete this.cache[t],!0}async _clear(){return this.cache={},!0}}var Ye=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{},ee=[],q=[],mo=typeof Uint8Array<"u"?Uint8Array:Array,cr=!1;function Mi(){cr=!0;for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=0,r=e.length;t<r;++t)ee[t]=e[t],q[e.charCodeAt(t)]=t;q[45]=62,q[95]=63}function bo(e){cr||Mi();var t,r,i,n,a,o,s=e.length;if(s%4>0)throw new Error("Invalid string. Length must be a multiple of 4");a=e[s-2]==="="?2:e[s-1]==="="?1:0,o=new mo(s*3/4-a),i=a>0?s-4:s;var h=0;for(t=0,r=0;t<i;t+=4,r+=3)n=q[e.charCodeAt(t)]<<18|q[e.charCodeAt(t+1)]<<12|q[e.charCodeAt(t+2)]<<6|q[e.charCodeAt(t+3)],o[h++]=n>>16&255,o[h++]=n>>8&255,o[h++]=n&255;return a===2?(n=q[e.charCodeAt(t)]<<2|q[e.charCodeAt(t+1)]>>4,o[h++]=n&255):a===1&&(n=q[e.charCodeAt(t)]<<10|q[e.charCodeAt(t+1)]<<4|q[e.charCodeAt(t+2)]>>2,o[h++]=n>>8&255,o[h++]=n&255),o}function yo(e){return ee[e>>18&63]+ee[e>>12&63]+ee[e>>6&63]+ee[e&63]}function xo(e,t,r){for(var i,n=[],a=t;a<r;a+=3)i=(e[a]<<16)+(e[a+1]<<8)+e[a+2],n.push(yo(i));return n.join("")}function Di(e){cr||Mi();for(var t,r=e.length,i=r%3,n="",a=[],o=16383,s=0,h=r-i;s<h;s+=o)a.push(xo(e,s,s+o>h?h:s+o));return i===1?(t=e[r-1],n+=ee[t>>2],n+=ee[t<<4&63],n+="=="):i===2&&(t=(e[r-2]<<8)+e[r-1],n+=ee[t>>10],n+=ee[t>>4&63],n+=ee[t<<2&63],n+="="),a.push(n),a.join("")}function Ct(e,t,r,i,n){var a,o,s=n*8-i-1,h=(1<<s)-1,f=h>>1,l=-7,d=r?n-1:0,p=r?-1:1,u=e[t+d];for(d+=p,a=u&(1<<-l)-1,u>>=-l,l+=s;l>0;a=a*256+e[t+d],d+=p,l-=8);for(o=a&(1<<-l)-1,a>>=-l,l+=i;l>0;o=o*256+e[t+d],d+=p,l-=8);if(a===0)a=1-f;else{if(a===h)return o?NaN:(u?-1:1)*(1/0);o=o+Math.pow(2,i),a=a-f}return(u?-1:1)*o*Math.pow(2,a-i)}function Fi(e,t,r,i,n,a){var o,s,h,f=a*8-n-1,l=(1<<f)-1,d=l>>1,p=n===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=i?0:a-1,g=i?1:-1,S=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=l):(o=Math.floor(Math.log(t)/Math.LN2),t*(h=Math.pow(2,-o))<1&&(o--,h*=2),o+d>=1?t+=p/h:t+=p*Math.pow(2,1-d),t*h>=2&&(o++,h/=2),o+d>=l?(s=0,o=l):o+d>=1?(s=(t*h-1)*Math.pow(2,n),o=o+d):(s=t*Math.pow(2,d-1)*Math.pow(2,n),o=0));n>=8;e[r+u]=s&255,u+=g,s/=256,n-=8);for(o=o<<n|s,f+=n;f>0;e[r+u]=o&255,u+=g,o/=256,f-=8);e[r+u-g]|=S*128}var Eo={}.toString,Ni=Array.isArray||function(e){return Eo.call(e)=="[object Array]"};/*!
|
|
7
|
+
${JSON.stringify(n,null,2)}`}),this.resourceName=r,this.attributes=i,this.validation=n}}class wi extends We{}const gi={NotFound:pi,NoSuchKey:or,UnknownError:wi,NoSuchBucket:di,MissingMetadata:_i,InvalidResourceItem:Tt},vi="us-east-1",mi="https://s3.us-east-1.amazonaws.com";class bi{constructor(t){let r;try{r=new URL(t)}catch{throw new Error("Invalid connection string: "+t)}this.region=vi,r.protocol==="s3:"?this.defineS3(r):this.defineMinio(r);for(const[i,n]of r.searchParams.entries())this[i]=n}defineS3(t){if(this.bucket=t.hostname,this.accessKeyId=t.username,this.secretAccessKey=t.password,this.endpoint=mi,["/","",null].includes(t.pathname))this.keyPrefix="";else{let[,...r]=t.pathname.split("/");this.keyPrefix=[...r||[]].join("/")}}defineMinio(t){if(this.forcePathStyle=!0,this.endpoint=t.origin,this.accessKeyId=t.username,this.secretAccessKey=t.password,["/","",null].includes(t.pathname))this.bucket="s3db",this.keyPrefix="";else{let[,r,...i]=t.pathname.split("/");this.bucket=r,this.keyPrefix=[...i||[]].join("/")}}}class yi extends b{constructor({verbose:t=!1,id:r=null,AwsS3Client:i,connectionString:n,parallelism:a=10}){super(),this.verbose=t,this.id=r??ai.nanoid(7),this.parallelism=a,this.config=new bi(n),this.client=i||this.createClient()}createClient(){let t={region:this.config.region,endpoint:this.config.endpoint};return this.config.forcePathStyle&&(t.forcePathStyle=!0),this.config.accessKeyId&&(t.credentials={accessKeyId:this.config.accessKeyId,secretAccessKey:this.config.secretAccessKey}),new de.S3Client(t)}async sendCommand(t){this.emit("command.request",t.constructor.name,t.input);const r=console.warn;try{console.warn=n=>{n.includes("Stream of unknown length")||r(n)}}catch(n){console.error(n)}const i=await this.client.send(t);this.emit("command.response",t.constructor.name,i,t.input);try{console.warn=r}catch(n){console.error(n)}return i}errorProxy(t,r){this.verbose&&(r.bucket=this.config.bucket,r.config=this.config,r.verbose=this.verbose),t.data=r;const i=gi[t.name];return i?new i(r):t}async putObject({key:t,metadata:r,contentType:i,body:n,contentEncoding:a}){const o={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t,Metadata:{...r},Body:n||"",ContentType:i,ContentEncoding:a};try{const s=await this.sendCommand(new de.PutObjectCommand(o));return this.emit("putObject",s,o),s}catch(s){throw this.errorProxy(s,{key:t,command:o})}}async getObject(t){const r={Bucket:this.config.bucket,Key:ne.join(this.config.keyPrefix,t)};try{const i=await this.sendCommand(new de.GetObjectCommand(r));return this.emit("getObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async headObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t};try{const i=await this.client.send(new de.HeadObjectCommand(r));return this.emit("headObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async copyObject({from:t,to:r}){const i={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,r):r,CopySource:ne.join(this.config.bucket,this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t)};try{const n=await this.client.send(new de.CopyObjectCommand(i));return this.emit("copyObject",n,i),n}catch(n){throw this.errorProxy(n,{from:t,to:r,command:i})}}async exists(t){try{return await this.headObject(t),!0}catch(r){if(r.name==="NoSuchKey")return!1;if(r.name==="NotFound")return!1;throw r}}async deleteObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,t):t};try{const i=await this.sendCommand(new de.DeleteObjectCommand(r));return this.emit("deleteObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async deleteObjects(t){const r=I.chunk(t,1e3),{results:i,errors:n}=await ue.PromisePool.for(r).withConcurrency(this.parallelism).process(async o=>{const s={Bucket:this.config.bucket,Delete:{Objects:o.map(h=>({Key:this.config.keyPrefix?ne.join(this.config.keyPrefix,h):h}))}};try{return await this.sendCommand(new de.DeleteObjectsCommand(s))}catch(h){throw this.errorProxy(h,{key,command:s})}}),a={deleted:i,notFound:n};return this.emit("deleteObjects",a,t),a}async deleteAll({prefix:t}={}){const r=await this.getAllKeys({prefix:t}),i=await this.deleteObjects(r);return this.emit("deleteAll",{prefix:t,report:i}),i}async moveObject({from:t,to:r}){try{return await this.copyObject({from:t,to:r}),await this.deleteObject(t),!0}catch(i){throw this.errorProxy(i,{from:t,to:r,command:options})}}async listObjects({prefix:t,maxKeys:r=1e3,continuationToken:i}={}){const n={Bucket:this.config.bucket,MaxKeys:r,ContinuationToken:i,Prefix:this.config.keyPrefix?ne.join(this.config.keyPrefix,t||""):t||""};try{const a=await this.sendCommand(new de.ListObjectsV2Command(n));return this.emit("listObjects",a,n),a}catch(a){throw this.errorProxy(a,{command:n})}}async count({prefix:t}={}){let r=0,i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);r+=o.KeyCount||0,i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.emit("count",r,{prefix:t}),r}async getAllKeys({prefix:t}={}){let r=[],i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);o.Contents&&(r=r.concat(o.Contents.map(s=>s.Key))),i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.config.keyPrefix&&(r=r.map(a=>a.replace(this.config.keyPrefix,"")).map(a=>a.startsWith("/")?a.replace("/",""):a)),this.emit("getAllKeys",r,{prefix:t}),r}async getContinuationTokenAfterOffset(t={}){const{prefix:r,offset:i=1e3}=t;if(i===0)return null;let n=!0,a,o=0;for(;n;){let s=i<1e3?i:i-o>1e3?1e3:i-o;const h={prefix:r,maxKeys:s,continuationToken:a},f=await this.listObjects(h);if(f.Contents&&(o+=f.Contents.length),n=f.IsTruncated||!1,a=f.NextContinuationToken,o>=i)break}return this.emit("getContinuationTokenAfterOffset",a,t),a}async getKeysPage(t={}){const{prefix:r,offset:i=0,amount:n=100}=t;let a=[],o=!0,s;for(i>0&&(s=await this.getContinuationTokenAfterOffset({prefix:r,offset:i}));o;){const h={prefix:r,continuationToken:s},f=await this.listObjects(h);if(f.Contents&&(a=a.concat(f.Contents.map(l=>l.Key))),o=f.IsTruncated||!1,s=f.NextContinuationToken,a.length>n){a=a.splice(0,n);break}}return this.config.keyPrefix&&(a=a.map(h=>h.replace(this.config.keyPrefix,"")).map(h=>h.startsWith("/")?h.replace("/",""):h)),this.emit("getKeysPage",a,t),a}async moveAllObjects({prefixFrom:t,prefixTo:r}){const i=await this.getAllKeys({prefix:t}),{results:n,errors:a}=await ue.PromisePool.for(i).withConcurrency(this.parallelism).process(async o=>{const s=o.replace(t,r);try{return await this.moveObject({from:o,to:s}),s}catch(h){throw this.errorProxy(h,{from:o,to:s})}});if(this.emit("moveAllObjects",{results:n,errors:a},{prefixFrom:t,prefixTo:r}),a.length>0)throw console.log({errors:a}),new Error("Some objects could not be moved");return n}}async function Lt(){let e;if(process)try{const{webcrypto:t}=await import("crypto");e=t}catch{throw new Error("Crypto API not available")}else window&&(e=window.crypto);if(!e)throw new Error("Could not load any crypto library");return e}async function xi(e){const t=await Lt(),i=new TextEncoder().encode(e),n=await t.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(n)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function sr(e,t){const r=await Lt(),i=r.getRandomValues(new Uint8Array(16)),n=await ki(t,i),a=r.getRandomValues(new Uint8Array(12)),s=new TextEncoder().encode(e),h=await r.subtle.encrypt({name:"AES-GCM",iv:a},n,s),f=new Uint8Array(i.length+a.length+h.byteLength);return f.set(i),f.set(a,i.length),f.set(new Uint8Array(h),i.length+a.length),po(f)}async function Ei(e,t){const r=await Lt(),i=_o(e),n=i.slice(0,16),a=i.slice(16,28),o=i.slice(28),s=await ki(t,n),h=await r.subtle.decrypt({name:"AES-GCM",iv:a},s,o);return new TextDecoder().decode(h)}async function ki(e,t){const r=await Lt(),n=new TextEncoder().encode(e),a=await r.subtle.importKey("raw",n,{name:"PBKDF2"},!1,["deriveKey"]);return await r.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},a,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}function po(e){if(I.isObject(process))return Buffer.from(e).toString("base64");{const t=String.fromCharCode.apply(null,new Uint8Array(e));return window.btoa(t)}}function _o(e){if(I.isObject(process))return new Uint8Array(Buffer.from(e,"base64"));{const t=window.atob(e),r=t.length,i=new Uint8Array(r);for(let n=0;n<r;n++)i[n]=t.charCodeAt(n);return i}}async function fr(e,t,r){if(!this.passphrase)return t.push({actual:e,type:"encryptionKeyMissing"}),e;try{return await sr(String(e),this.passphrase)}catch(i){t.push({actual:e,type:"encryptionProblem",error:i})}return e}class Si extends Ga{constructor({options:t,passphrase:r,autoEncrypt:i=!0}={}){super(I.merge({},{useNewCustomCheckerFunction:!0,messages:{encryptionKeyMissing:"Missing configuration for secrets encryption.",encryptionProblem:"Problem encrypting secret. Actual: {actual}. Error: {error}"},defaults:{string:{trim:!0},object:{strict:"remove"}}},t)),this.passphrase=r,this.autoEncrypt=i,this.alias("secret",{type:"string",custom:this.autoEncrypt?fr:void 0,messages:{string:"The '{field}' field must be a string.",stringMin:"This secret '{field}' field length must be at least {expected} long."}}),this.alias("secretAny",{type:"any",custom:this.autoEncrypt?fr:void 0}),this.alias("secretNumber",{type:"number",custom:this.autoEncrypt?fr:void 0})}}const Ri=new Proxy(Si,{instance:null,construct(e,t){return this.instance||(this.instance=new e(...t)),this.instance}}),wo={trim:e=>e.trim(),encrypt:(e,{passphrase:t})=>sr(e,t),decrypt:(e,{passphrase:t})=>Ei(e,t),toString:e=>String(e),fromArray:(e,{separator:t})=>(e||[]).join(t),toArray:(e,{separator:t})=>(e||"").split(t),toJSON:e=>JSON.stringify(e),fromJSON:e=>JSON.parse(e),toNumber:e=>I.isString(e)?e.includes(".")?parseFloat(e):parseInt(e):e,toBool:e=>["1","true","yes",!0,"y"].includes(e),fromBool:e=>e?"1":"0"};class ti{constructor(t){const{map:r,name:i,attributes:n,passphrase:a,version:o=1,options:s={}}=t;if(this.name=i,this.version=o,this.attributes=n,this.passphrase=a??"secret",this.options=I.merge({},this.defaultOptions(),s),this.validator=new Ri({autoEncrypt:!1}).compile(I.merge({$$async:!0},I.cloneDeep(this.attributes))),this.options.generateAutoHooks&&this.generateAutoHooks(),!I.isEmpty(r))this.map=r,this.reversedMap=I.invert(r);else{const h=At.flatten(this.attributes,{safe:!0});this.reversedMap={...Object.keys(h).filter(f=>!f.includes("$$"))},this.map=I.invert(this.reversedMap)}}defaultOptions(){return{autoEncrypt:!0,autoDecrypt:!0,arraySeparator:"|",generateAutoHooks:!0,hooks:{beforeMap:{},afterMap:{},beforeUnmap:{},afterUnmap:{}}}}addHook(t,r,i){this.options.hooks[t][r]||(this.options.hooks[t][r]=[]),this.options.hooks[t][r]=I.uniq([...this.options.hooks[t][r],i])}generateAutoHooks(){const t=At.flatten(I.cloneDeep(this.attributes),{safe:!0});for(const[r,i]of Object.entries(t))i.includes("array")?(this.addHook("beforeMap",r,"fromArray"),this.addHook("afterUnmap",r,"toArray")):(i.includes("secret")&&(this.options.autoEncrypt&&this.addHook("beforeMap",r,"encrypt"),this.options.autoDecrypt&&this.addHook("afterUnmap",r,"decrypt")),i.includes("number")&&(this.addHook("beforeMap",r,"toString"),this.addHook("afterUnmap",r,"toNumber")),i.includes("boolean")&&(this.addHook("beforeMap",r,"fromBool"),this.addHook("afterUnmap",r,"toBool")))}static import(t){let{map:r,name:i,options:n,version:a,attributes:o}=I.isString(t)?JSON.parse(t):t;return new ti({map:r,name:i,options:n,version:a,attributes:o})}export(){const t={version:this.version,name:this.name,options:this.options,attributes:I.cloneDeep(this.attributes),map:this.map};for(const[r,i]of Object.entries(this.attributes))t.attributes[r]=JSON.stringify(i);return t}async applyHooksActions(t,r){for(const[i,n]of Object.entries(this.options.hooks[r]))for(const a of n){const o=I.get(t,i);o!==void 0&&I.set(t,i,await wo[a](o,{passphrase:this.passphrase,separator:this.options.arraySeparator}))}}async validate(t,{mutateOriginal:r=!1}={}){let i=r?t:I.cloneDeep(t);return await this.validator(i)}async mapper(t){const r=At.flatten(I.cloneDeep(t),{safe:!0});await this.applyHooksActions(r,"beforeMap");const i={_v:this.version+""};for(const[n,a]of Object.entries(r))i[this.map[n]]=a;return await this.applyHooksActions(i,"afterMap"),i}async unmapper(t){const r=I.cloneDeep(t);delete r._v,await this.applyHooksActions(r,"beforeUnmap");const i={};for(const[n,a]of Object.entries(r))i[this.reversedMap[n]]=a;return await this.applyHooksActions(i,"afterUnmap"),At.unflatten(i)}}class Ai extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new rr.ReadableStream({highWaterMark:this.client.parallelism*3,start:this._start.bind(this),pull:this._pull.bind(this),cancel:this._cancel.bind(this)})}build(){return this.stream.getReader()}async _start(t){this.controller=t,this.continuationToken=null,this.closeNextIteration=!1}async _pull(t){if(this.closeNextIteration){t.close();return}const r=await this.client.listObjects({prefix:`resource=${this.resource.name}`,continuationToken:this.continuationToken}),i=r?.Contents.map(n=>n.Key).map(n=>n.replace(this.client.config.keyPrefix,"")).map(n=>n.startsWith("/")?n.replace("/",""):n).map(n=>n.replace(`resource=${this.resource.name}/id=`,""));this.continuationToken=r.NextContinuationToken,this.enqueue(i),r.IsTruncated||(this.closeNextIteration=!0)}enqueue(t){t.forEach(r=>{this.controller.enqueue(r),this.emit("id",r)})}_cancel(t){console.warn("Stream cancelled",t)}}class Ti extends Ai{enqueue(t){this.controller.enqueue(t),this.emit("page",t)}}class Li extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.input=new Ti({resource:this.resource}),this.output=new rr.TransformStream({transform:this._transform.bind(this)},{highWaterMark:this.client.parallelism*2},{highWaterMark:1}),this.stream=this.input.stream.pipeThrough(this.output)}build(){return this.stream.getReader()}async _transform(t,r){await ue.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{const n=await this.resource.get(i);return r.enqueue(n),n})}}class Ci extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new rr.WritableStream({start:this._start.bind(this),write:this._write.bind(this),close:this._close.bind(this),abort:this._abort.bind(this)})}build(){return this.stream.getWriter()}async _start(t){this.controller=t}async _write(t,r){const i=this.resource;await ue.PromisePool.for([].concat(t)).withConcurrency(this.client.parallelism).process(async n=>{await i.insert(n)})}async _close(t){}async _abort(t){console.error("Stream aborted:",t)}}function lr(e){return new Promise((t,r)=>{const i=[];e.on("data",n=>i.push(n)),e.on("error",r),e.on("end",()=>t(Buffer.concat(i).toString("utf-8")))})}class Oi extends b{constructor({name:t,client:r,options:i={},attributes:n={},parallelism:a=10,passphrase:o="secret",observers:s=[]}){super(),this.name=t,this.client=r,this.options=i,this.observers=s,this.parallelism=a,this.passphrase=o??"secret",this.schema=new ti({name:t,attributes:n,passphrase:o})}export(){return this.schema.export()}async validate(t){const r={original:I.cloneDeep(t),isValid:!1,errors:[]},i=await this.schema.validate(t,{mutateOriginal:!0});return i===!0?r.isValid=!0:r.errors=i,r.data=t,r}async insert({id:t,...r}){const{errors:i,isValid:n,data:a}=await this.validate(r);if(!n)throw new Tt({bucket:this.client.config.bucket,resourceName:this.name,attributes:r,validation:i});!t&&t!==0&&(t=ai.nanoid());const o=await this.schema.mapper(a);await this.client.putObject({metadata:o,key:W(`resource=${this.name}`,`id=${t}`)});const s=I.merge({id:t},a);return this.emit("insert",s),s}async get(t){const r=await this.client.headObject(W(`resource=${this.name}`,`id=${t}`));let i=await this.schema.unmapper(r.Metadata);return i.id=t,i._length=r.ContentLength,i._createdAt=r.LastModified,r.Expiration&&(i._expiresAt=r.Expiration),this.emit("get",i),i}async exists(t){try{return await this.client.headObject(W(`resource=${this.name}`,`id=${t}`)),!0}catch{return!1}}async update(t,r){const i=await this.get(t),n=I.merge(i,r);delete n.id;const{isValid:a,errors:o,data:s}=await this.validate(n);if(!a)throw new Tt({bucket:this.client.bucket,resourceName:this.name,attributes:r,validation:o});return await this.client.putObject({key:W(`resource=${this.name}`,`id=${t}`),body:"",metadata:await this.schema.mapper(s)}),s.id=t,this.emit("update",r,s),s}async delete(t){const r=W(`resource=${this.name}`,`id=${t}`),i=await this.client.deleteObject(r);return this.emit("delete",t),i}async count(){const t=await this.client.count({prefix:`resource=${this.name}`});return this.emit("count",t),t}async insertMany(t){const{results:r}=await ue.PromisePool.for(t).withConcurrency(this.parallelism).handleError(async(i,n)=>{this.emit("error",i,n),this.observers.map(a=>a.emit("error",this.name,i,n))}).process(async i=>await this.insert(i));return this.emit("insertMany",t.length),r}async deleteMany(t){const r=I.chunk(t.map(n=>W(`resource=${this.name}`,`id=${n}`)),1e3),{results:i}=await ue.PromisePool.for(r).withConcurrency(this.parallelism).handleError(async(n,a)=>{this.emit("error",n,a),this.observers.map(o=>o.emit("error",this.name,n,a))}).process(async n=>{const a=await this.client.deleteObjects(n);return n.forEach(o=>{const s=o.split("=").pop();this.emit("deleted",s),this.observers.map(h=>h.emit("deleted",this.name,s))}),a});return this.emit("deleteMany",t.length),i}async deleteAll(){const t=await this.listIds();this.emit("deleteAll",t.length),await this.deleteMany(t)}async listIds(){const r=(await this.client.getAllKeys({prefix:`resource=${this.name}`})).map(i=>i.replace(`resource=${this.name}/id=`,""));return this.emit("listIds",r.length),r}async getMany(t){const{results:r}=await ue.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{this.emit("id",i);const n=await this.get(i);return this.emit("data",n),n});return this.emit("getMany",t.length),r}async getAll(){let t=await this.listIds();if(t.length===0)return[];const{results:r}=await ue.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>await this.get(i));return this.emit("getAll",r.length),r}async page({offset:t=0,size:r=100}){const n=(await this.client.getKeysPage({offset:t,amount:r,prefix:`resource=${this.name}`})).map(o=>o.replace(`resource=${this.name}/id=`,""));return await this.getMany(n)}readable(){return new Li({resource:this}).build()}writable(){return new Ci({resource:this}).build()}}class Ii extends b{constructor(t){super(),this.version="1",this.resources={},this.options=t,this.verbose=t.verbose||!1,this.parallelism=parseInt(t.parallelism+"")||10,this.plugins=t.plugins||[],this.cache=t.cache,this.passphrase=t.passphrase||"secret",this.client=t.client||new yi({verbose:this.verbose,parallelism:this.parallelism,connectionString:t.connectionString}),this.bucket=this.client.bucket,this.keyPrefix=this.client.keyPrefix}async connect(){await this.startPlugins();let t=null;if(await this.client.exists("s3db.json")){const r=await this.client.getObject("s3db.json");t=JSON.parse(await lr(r?.Body)),t=this.unserializeMetadata(t)}else t=this.blankMetadataStructure(),await this.uploadMetadataFile();for(const r of Object.entries(t.resources)){const[i,n]=r;this.resources[i]=new Oi({name:i,client:this.client,options:n.options,attributes:n.schema,parallelism:this.parallelism,passphrase:this.passphrase,observers:[this]})}this.emit("connected",new Date)}async startPlugins(){const t=this;if(!I.isEmpty(this.plugins)){const r=this.plugins.map(a=>I.isFunction(a)?new a(this):a),i=r.map(async a=>{a.beforeSetup&&await a.beforeSetup(),await a.setup(t),a.afterSetup&&await a.afterSetup()});await Promise.all(i);const n=r.map(async a=>{a.beforeStart&&await a.beforeStart(),await a.start(),a.afterStart&&await a.afterStart()});await Promise.all(n)}}unserializeMetadata(t){const r={...t};if(I.isEmpty(r.resources))return r;for(const[i,n]of Object.entries(r.resources))for(const[a,o]of Object.entries(n.attributes))r.resources[i].attributes[a]=JSON.parse(o);return r}async uploadMetadataFile(){const t={version:this.version,resources:Object.entries(this.resources).reduce((r,i)=>{const[n,a]=i;return r[n]=a.export(),r},{})};await this.client.putObject({key:"s3db.json",contentType:"application/json",body:JSON.stringify(t,null,2)})}blankMetadataStructure(){return{version:"1",resources:{}}}async createResource({name:t,attributes:r,options:i={}}){const n=new Oi({name:t,attributes:r,observers:[this],client:this.client,options:{autoDecrypt:!0,cache:this.cache,...i}});return this.resources[t]=n,await this.uploadMetadataFile(),this.emit("s3db.resourceCreated",t),n}resource(t){return this.resources[t]?this.resources[t]:Promise.reject(`resource ${t} does not exist`)}}class go extends Ii{}class hr extends b{async _set(t,r){}async _get(t){}async _del(t){}async _clear(t){}async set(t,r){return await this._set(t,r),this.emit("set",r),r}async get(t){const r=await this._get(t);return this.emit("get",r),r}async del(t){const r=await this._del(t);return this.emit("delete",r),r}async clear(){const t=await this._clear();return this.emit("clear",t),t}}class vo extends hr{constructor(){super(),this.cache={}}async _set(t,r){return this.cache[t]=r,r}async _get(t){return this.cache[t]}async _del(t){return delete this.cache[t],!0}async _clear(){return this.cache={},!0}}var Ye=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{},ee=[],q=[],mo=typeof Uint8Array<"u"?Uint8Array:Array,cr=!1;function Mi(){cr=!0;for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=0,r=e.length;t<r;++t)ee[t]=e[t],q[e.charCodeAt(t)]=t;q[45]=62,q[95]=63}function bo(e){cr||Mi();var t,r,i,n,a,o,s=e.length;if(s%4>0)throw new Error("Invalid string. Length must be a multiple of 4");a=e[s-2]==="="?2:e[s-1]==="="?1:0,o=new mo(s*3/4-a),i=a>0?s-4:s;var h=0;for(t=0,r=0;t<i;t+=4,r+=3)n=q[e.charCodeAt(t)]<<18|q[e.charCodeAt(t+1)]<<12|q[e.charCodeAt(t+2)]<<6|q[e.charCodeAt(t+3)],o[h++]=n>>16&255,o[h++]=n>>8&255,o[h++]=n&255;return a===2?(n=q[e.charCodeAt(t)]<<2|q[e.charCodeAt(t+1)]>>4,o[h++]=n&255):a===1&&(n=q[e.charCodeAt(t)]<<10|q[e.charCodeAt(t+1)]<<4|q[e.charCodeAt(t+2)]>>2,o[h++]=n>>8&255,o[h++]=n&255),o}function yo(e){return ee[e>>18&63]+ee[e>>12&63]+ee[e>>6&63]+ee[e&63]}function xo(e,t,r){for(var i,n=[],a=t;a<r;a+=3)i=(e[a]<<16)+(e[a+1]<<8)+e[a+2],n.push(yo(i));return n.join("")}function Di(e){cr||Mi();for(var t,r=e.length,i=r%3,n="",a=[],o=16383,s=0,h=r-i;s<h;s+=o)a.push(xo(e,s,s+o>h?h:s+o));return i===1?(t=e[r-1],n+=ee[t>>2],n+=ee[t<<4&63],n+="=="):i===2&&(t=(e[r-2]<<8)+e[r-1],n+=ee[t>>10],n+=ee[t>>4&63],n+=ee[t<<2&63],n+="="),a.push(n),a.join("")}function Ct(e,t,r,i,n){var a,o,s=n*8-i-1,h=(1<<s)-1,f=h>>1,l=-7,d=r?n-1:0,p=r?-1:1,u=e[t+d];for(d+=p,a=u&(1<<-l)-1,u>>=-l,l+=s;l>0;a=a*256+e[t+d],d+=p,l-=8);for(o=a&(1<<-l)-1,a>>=-l,l+=i;l>0;o=o*256+e[t+d],d+=p,l-=8);if(a===0)a=1-f;else{if(a===h)return o?NaN:(u?-1:1)*(1/0);o=o+Math.pow(2,i),a=a-f}return(u?-1:1)*o*Math.pow(2,a-i)}function Fi(e,t,r,i,n,a){var o,s,h,f=a*8-n-1,l=(1<<f)-1,d=l>>1,p=n===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=i?0:a-1,g=i?1:-1,S=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=l):(o=Math.floor(Math.log(t)/Math.LN2),t*(h=Math.pow(2,-o))<1&&(o--,h*=2),o+d>=1?t+=p/h:t+=p*Math.pow(2,1-d),t*h>=2&&(o++,h/=2),o+d>=l?(s=0,o=l):o+d>=1?(s=(t*h-1)*Math.pow(2,n),o=o+d):(s=t*Math.pow(2,d-1)*Math.pow(2,n),o=0));n>=8;e[r+u]=s&255,u+=g,s/=256,n-=8);for(o=o<<n|s,f+=n;f>0;e[r+u]=o&255,u+=g,o/=256,f-=8);e[r+u-g]|=S*128}var Eo={}.toString,Ni=Array.isArray||function(e){return Eo.call(e)=="[object Array]"};/*!
|
|
8
8
|
* The buffer module from node.js, for the browser.
|
|
9
9
|
*
|
|
10
10
|
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
package/dist/s3db.es.js
CHANGED
|
@@ -1345,7 +1345,7 @@ const SchemaActions = {
|
|
|
1345
1345
|
toJSON: (value) => JSON.stringify(value),
|
|
1346
1346
|
fromJSON: (value) => JSON.parse(value),
|
|
1347
1347
|
toNumber: (value) => isString$1(value) ? value.includes(".") ? parseFloat(value) : parseInt(value) : value,
|
|
1348
|
-
toBool: (value) => ["
|
|
1348
|
+
toBool: (value) => ["1", "true", "yes", true, "y"].includes(value),
|
|
1349
1349
|
fromBool: (value) => value ? "1" : "0"
|
|
1350
1350
|
};
|
|
1351
1351
|
class Schema {
|
|
@@ -1455,7 +1455,7 @@ class Schema {
|
|
|
1455
1455
|
for (const [attribute, actions] of Object.entries(this.options.hooks[hook])) {
|
|
1456
1456
|
for (const action of actions) {
|
|
1457
1457
|
const value = get(resourceItem, attribute);
|
|
1458
|
-
if (value) {
|
|
1458
|
+
if (value !== void 0) {
|
|
1459
1459
|
set(resourceItem, attribute, await SchemaActions[action](value, {
|
|
1460
1460
|
passphrase: this.passphrase,
|
|
1461
1461
|
separator: this.options.arraySeparator
|
package/dist/s3db.es.min.js
CHANGED
|
@@ -4,7 +4,7 @@ import{nanoid as ni}from"nanoid";import{chunk as ai,isObject as oi,merge as it,c
|
|
|
4
4
|
Verbose:
|
|
5
5
|
|
|
6
6
|
${JSON.stringify(n,null,2)}`),super(i),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(i).stack,super.name=this.constructor.name,this.name=this.constructor.name,this.bucket=r,this.thrownAt=new Date}toJson(){return{...this}}toString(){return`${this.name} | ${this.message}`}}class wi extends Ke{constructor({bucket:t,...r}){super({...r,bucket:t,message:`Bucket does not exists [bucket:${t}]`})}}class or extends Ke{constructor({bucket:t,key:r,...i}){super({...i,bucket:t,message:`Key [${r}] does not exists [bucket:${t}/${r}]`}),this.key=r}}class gi extends or{}class vi extends Ke{constructor({bucket:t,...r}){super({...r,bucket:t,message:`Missing metadata for bucket [bucket:${t}]`})}}class At extends Ke{constructor({bucket:t,resourceName:r,attributes:i,validation:n}){super({bucket:t,message:`This item is not valid. Resource=${r} [bucket:${t}].
|
|
7
|
-
${JSON.stringify(n,null,2)}`}),this.resourceName=r,this.attributes=i,this.validation=n}}class mi extends Ke{}const bi={NotFound:gi,NoSuchKey:or,UnknownError:mi,NoSuchBucket:wi,MissingMetadata:vi,InvalidResourceItem:At},yi="us-east-1",xi="https://s3.us-east-1.amazonaws.com";class Ei{constructor(t){let r;try{r=new URL(t)}catch{throw new Error("Invalid connection string: "+t)}this.region=yi,r.protocol==="s3:"?this.defineS3(r):this.defineMinio(r);for(const[i,n]of r.searchParams.entries())this[i]=n}defineS3(t){if(this.bucket=t.hostname,this.accessKeyId=t.username,this.secretAccessKey=t.password,this.endpoint=xi,["/","",null].includes(t.pathname))this.keyPrefix="";else{let[,...r]=t.pathname.split("/");this.keyPrefix=[...r||[]].join("/")}}defineMinio(t){if(this.forcePathStyle=!0,this.endpoint=t.origin,this.accessKeyId=t.username,this.secretAccessKey=t.password,["/","",null].includes(t.pathname))this.bucket="s3db",this.keyPrefix="";else{let[,r,...i]=t.pathname.split("/");this.bucket=r,this.keyPrefix=[...i||[]].join("/")}}}class ki extends b{constructor({verbose:t=!1,id:r=null,AwsS3Client:i,connectionString:n,parallelism:a=10}){super(),this.verbose=t,this.id=r??ni(7),this.parallelism=a,this.config=new Ei(n),this.client=i||this.createClient()}createClient(){let t={region:this.config.region,endpoint:this.config.endpoint};return this.config.forcePathStyle&&(t.forcePathStyle=!0),this.config.accessKeyId&&(t.credentials={accessKeyId:this.config.accessKeyId,secretAccessKey:this.config.secretAccessKey}),new Ga(t)}async sendCommand(t){this.emit("command.request",t.constructor.name,t.input);const r=console.warn;try{console.warn=n=>{n.includes("Stream of unknown length")||r(n)}}catch(n){console.error(n)}const i=await this.client.send(t);this.emit("command.response",t.constructor.name,i,t.input);try{console.warn=r}catch(n){console.error(n)}return i}errorProxy(t,r){this.verbose&&(r.bucket=this.config.bucket,r.config=this.config,r.verbose=this.verbose),t.data=r;const i=bi[t.name];return i?new i(r):t}async putObject({key:t,metadata:r,contentType:i,body:n,contentEncoding:a}){const o={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t,Metadata:{...r},Body:n||"",ContentType:i,ContentEncoding:a};try{const s=await this.sendCommand(new Va(o));return this.emit("putObject",s,o),s}catch(s){throw this.errorProxy(s,{key:t,command:o})}}async getObject(t){const r={Bucket:this.config.bucket,Key:ie.join(this.config.keyPrefix,t)};try{const i=await this.sendCommand(new Xa(r));return this.emit("getObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async headObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t};try{const i=await this.client.send(new Ja(r));return this.emit("headObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async copyObject({from:t,to:r}){const i={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,r):r,CopySource:ie.join(this.config.bucket,this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t)};try{const n=await this.client.send(new Qa(i));return this.emit("copyObject",n,i),n}catch(n){throw this.errorProxy(n,{from:t,to:r,command:i})}}async exists(t){try{return await this.headObject(t),!0}catch(r){if(r.name==="NoSuchKey")return!1;if(r.name==="NotFound")return!1;throw r}}async deleteObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t};try{const i=await this.sendCommand(new eo(r));return this.emit("deleteObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async deleteObjects(t){const r=ai(t,1e3),{results:i,errors:n}=await ce.for(r).withConcurrency(this.parallelism).process(async o=>{const s={Bucket:this.config.bucket,Delete:{Objects:o.map(h=>({Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,h):h}))}};try{return await this.sendCommand(new to(s))}catch(h){throw this.errorProxy(h,{key,command:s})}}),a={deleted:i,notFound:n};return this.emit("deleteObjects",a,t),a}async deleteAll({prefix:t}={}){const r=await this.getAllKeys({prefix:t}),i=await this.deleteObjects(r);return this.emit("deleteAll",{prefix:t,report:i}),i}async moveObject({from:t,to:r}){try{return await this.copyObject({from:t,to:r}),await this.deleteObject(t),!0}catch(i){throw this.errorProxy(i,{from:t,to:r,command:options})}}async listObjects({prefix:t,maxKeys:r=1e3,continuationToken:i}={}){const n={Bucket:this.config.bucket,MaxKeys:r,ContinuationToken:i,Prefix:this.config.keyPrefix?ie.join(this.config.keyPrefix,t||""):t||""};try{const a=await this.sendCommand(new ro(n));return this.emit("listObjects",a,n),a}catch(a){throw this.errorProxy(a,{command:n})}}async count({prefix:t}={}){let r=0,i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);r+=o.KeyCount||0,i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.emit("count",r,{prefix:t}),r}async getAllKeys({prefix:t}={}){let r=[],i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);o.Contents&&(r=r.concat(o.Contents.map(s=>s.Key))),i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.config.keyPrefix&&(r=r.map(a=>a.replace(this.config.keyPrefix,"")).map(a=>a.startsWith("/")?a.replace("/",""):a)),this.emit("getAllKeys",r,{prefix:t}),r}async getContinuationTokenAfterOffset(t={}){const{prefix:r,offset:i=1e3}=t;if(i===0)return null;let n=!0,a,o=0;for(;n;){let s=i<1e3?i:i-o>1e3?1e3:i-o;const h={prefix:r,maxKeys:s,continuationToken:a},f=await this.listObjects(h);if(f.Contents&&(o+=f.Contents.length),n=f.IsTruncated||!1,a=f.NextContinuationToken,o>=i)break}return this.emit("getContinuationTokenAfterOffset",a,t),a}async getKeysPage(t={}){const{prefix:r,offset:i=0,amount:n=100}=t;let a=[],o=!0,s;for(i>0&&(s=await this.getContinuationTokenAfterOffset({prefix:r,offset:i}));o;){const h={prefix:r,continuationToken:s},f=await this.listObjects(h);if(f.Contents&&(a=a.concat(f.Contents.map(l=>l.Key))),o=f.IsTruncated||!1,s=f.NextContinuationToken,a.length>n){a=a.splice(0,n);break}}return this.config.keyPrefix&&(a=a.map(h=>h.replace(this.config.keyPrefix,"")).map(h=>h.startsWith("/")?h.replace("/",""):h)),this.emit("getKeysPage",a,t),a}async moveAllObjects({prefixFrom:t,prefixTo:r}){const i=await this.getAllKeys({prefix:t}),{results:n,errors:a}=await ce.for(i).withConcurrency(this.parallelism).process(async o=>{const s=o.replace(t,r);try{return await this.moveObject({from:o,to:s}),s}catch(h){throw this.errorProxy(h,{from:o,to:s})}});if(this.emit("moveAllObjects",{results:n,errors:a},{prefixFrom:t,prefixTo:r}),a.length>0)throw console.log({errors:a}),new Error("Some objects could not be moved");return n}}async function Tt(){let e;if(process)try{const{webcrypto:t}=await import("crypto");e=t}catch{throw new Error("Crypto API not available")}else window&&(e=window.crypto);if(!e)throw new Error("Could not load any crypto library");return e}async function Si(e){const t=await Tt(),i=new TextEncoder().encode(e),n=await t.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(n)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function sr(e,t){const r=await Tt(),i=r.getRandomValues(new Uint8Array(16)),n=await Ai(t,i),a=r.getRandomValues(new Uint8Array(12)),s=new TextEncoder().encode(e),h=await r.subtle.encrypt({name:"AES-GCM",iv:a},n,s),f=new Uint8Array(i.length+a.length+h.byteLength);return f.set(i),f.set(a,i.length),f.set(new Uint8Array(h),i.length+a.length),Ro(f)}async function Ri(e,t){const r=await Tt(),i=Ao(e),n=i.slice(0,16),a=i.slice(16,28),o=i.slice(28),s=await Ai(t,n),h=await r.subtle.decrypt({name:"AES-GCM",iv:a},s,o);return new TextDecoder().decode(h)}async function Ai(e,t){const r=await Tt(),n=new TextEncoder().encode(e),a=await r.subtle.importKey("raw",n,{name:"PBKDF2"},!1,["deriveKey"]);return await r.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},a,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}function Ro(e){if(oi(process))return Buffer.from(e).toString("base64");{const t=String.fromCharCode.apply(null,new Uint8Array(e));return window.btoa(t)}}function Ao(e){if(oi(process))return new Uint8Array(Buffer.from(e,"base64"));{const t=window.atob(e),r=t.length,i=new Uint8Array(r);for(let n=0;n<r;n++)i[n]=t.charCodeAt(n);return i}}async function fr(e,t,r){if(!this.passphrase)return t.push({actual:e,type:"encryptionKeyMissing"}),e;try{return await sr(String(e),this.passphrase)}catch(i){t.push({actual:e,type:"encryptionProblem",error:i})}return e}class Ti extends no{constructor({options:t,passphrase:r,autoEncrypt:i=!0}={}){super(it({},{useNewCustomCheckerFunction:!0,messages:{encryptionKeyMissing:"Missing configuration for secrets encryption.",encryptionProblem:"Problem encrypting secret. Actual: {actual}. Error: {error}"},defaults:{string:{trim:!0},object:{strict:"remove"}}},t)),this.passphrase=r,this.autoEncrypt=i,this.alias("secret",{type:"string",custom:this.autoEncrypt?fr:void 0,messages:{string:"The '{field}' field must be a string.",stringMin:"This secret '{field}' field length must be at least {expected} long."}}),this.alias("secretAny",{type:"any",custom:this.autoEncrypt?fr:void 0}),this.alias("secretNumber",{type:"number",custom:this.autoEncrypt?fr:void 0})}}const Li=new Proxy(Ti,{instance:null,construct(e,t){return this.instance||(this.instance=new e(...t)),this.instance}}),To={trim:e=>e.trim(),encrypt:(e,{passphrase:t})=>sr(e,t),decrypt:(e,{passphrase:t})=>Ri(e,t),toString:e=>String(e),fromArray:(e,{separator:t})=>(e||[]).join(t),toArray:(e,{separator:t})=>(e||"").split(t),toJSON:e=>JSON.stringify(e),fromJSON:e=>JSON.parse(e),toNumber:e=>fi(e)?e.includes(".")?parseFloat(e):parseInt(e):e,toBool:e=>["true","1","yes"].includes(e),fromBool:e=>e?"1":"0"};class ti{constructor(t){const{map:r,name:i,attributes:n,passphrase:a,version:o=1,options:s={}}=t;if(this.name=i,this.version=o,this.attributes=n,this.passphrase=a??"secret",this.options=it({},this.defaultOptions(),s),this.validator=new Li({autoEncrypt:!1}).compile(it({$$async:!0},Re(this.attributes))),this.options.generateAutoHooks&&this.generateAutoHooks(),!tr(r))this.map=r,this.reversedMap=si(r);else{const h=rr(this.attributes,{safe:!0});this.reversedMap={...Object.keys(h).filter(f=>!f.includes("$$"))},this.map=si(this.reversedMap)}}defaultOptions(){return{autoEncrypt:!0,autoDecrypt:!0,arraySeparator:"|",generateAutoHooks:!0,hooks:{beforeMap:{},afterMap:{},beforeUnmap:{},afterUnmap:{}}}}addHook(t,r,i){this.options.hooks[t][r]||(this.options.hooks[t][r]=[]),this.options.hooks[t][r]=Ka([...this.options.hooks[t][r],i])}generateAutoHooks(){const t=rr(Re(this.attributes),{safe:!0});for(const[r,i]of Object.entries(t))i.includes("array")?(this.addHook("beforeMap",r,"fromArray"),this.addHook("afterUnmap",r,"toArray")):(i.includes("secret")&&(this.options.autoEncrypt&&this.addHook("beforeMap",r,"encrypt"),this.options.autoDecrypt&&this.addHook("afterUnmap",r,"decrypt")),i.includes("number")&&(this.addHook("beforeMap",r,"toString"),this.addHook("afterUnmap",r,"toNumber")),i.includes("boolean")&&(this.addHook("beforeMap",r,"fromBool"),this.addHook("afterUnmap",r,"toBool")))}static import(t){let{map:r,name:i,options:n,version:a,attributes:o}=fi(t)?JSON.parse(t):t;return new ti({map:r,name:i,options:n,version:a,attributes:o})}export(){const t={version:this.version,name:this.name,options:this.options,attributes:Re(this.attributes),map:this.map};for(const[r,i]of Object.entries(this.attributes))t.attributes[r]=JSON.stringify(i);return t}async applyHooksActions(t,r){for(const[i,n]of Object.entries(this.options.hooks[r]))for(const a of n){const o=Wa(t,i);o&&Ya(t,i,await To[a](o,{passphrase:this.passphrase,separator:this.options.arraySeparator}))}}async validate(t,{mutateOriginal:r=!1}={}){let i=r?t:Re(t);return await this.validator(i)}async mapper(t){const r=rr(Re(t),{safe:!0});await this.applyHooksActions(r,"beforeMap");const i={_v:this.version+""};for(const[n,a]of Object.entries(r))i[this.map[n]]=a;return await this.applyHooksActions(i,"afterMap"),i}async unmapper(t){const r=Re(t);delete r._v,await this.applyHooksActions(r,"beforeUnmap");const i={};for(const[n,a]of Object.entries(r))i[this.reversedMap[n]]=a;return await this.applyHooksActions(i,"afterUnmap"),io(i)}}class Ci extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new ao({highWaterMark:this.client.parallelism*3,start:this._start.bind(this),pull:this._pull.bind(this),cancel:this._cancel.bind(this)})}build(){return this.stream.getReader()}async _start(t){this.controller=t,this.continuationToken=null,this.closeNextIteration=!1}async _pull(t){if(this.closeNextIteration){t.close();return}const r=await this.client.listObjects({prefix:`resource=${this.resource.name}`,continuationToken:this.continuationToken}),i=r?.Contents.map(n=>n.Key).map(n=>n.replace(this.client.config.keyPrefix,"")).map(n=>n.startsWith("/")?n.replace("/",""):n).map(n=>n.replace(`resource=${this.resource.name}/id=`,""));this.continuationToken=r.NextContinuationToken,this.enqueue(i),r.IsTruncated||(this.closeNextIteration=!0)}enqueue(t){t.forEach(r=>{this.controller.enqueue(r),this.emit("id",r)})}_cancel(t){console.warn("Stream cancelled",t)}}class Oi extends Ci{enqueue(t){this.controller.enqueue(t),this.emit("page",t)}}class Ii extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.input=new Oi({resource:this.resource}),this.output=new oo({transform:this._transform.bind(this)},{highWaterMark:this.client.parallelism*2},{highWaterMark:1}),this.stream=this.input.stream.pipeThrough(this.output)}build(){return this.stream.getReader()}async _transform(t,r){await ce.for(t).withConcurrency(this.client.parallelism).process(async i=>{const n=await this.resource.get(i);return r.enqueue(n),n})}}class Mi extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new so({start:this._start.bind(this),write:this._write.bind(this),close:this._close.bind(this),abort:this._abort.bind(this)})}build(){return this.stream.getWriter()}async _start(t){this.controller=t}async _write(t,r){const i=this.resource;await ce.for([].concat(t)).withConcurrency(this.client.parallelism).process(async n=>{await i.insert(n)})}async _close(t){}async _abort(t){console.error("Stream aborted:",t)}}function lr(e){return new Promise((t,r)=>{const i=[];e.on("data",n=>i.push(n)),e.on("error",r),e.on("end",()=>t(Buffer.concat(i).toString("utf-8")))})}class Fi extends b{constructor({name:t,client:r,options:i={},attributes:n={},parallelism:a=10,passphrase:o="secret",observers:s=[]}){super(),this.name=t,this.client=r,this.options=i,this.observers=s,this.parallelism=a,this.passphrase=o??"secret",this.schema=new ti({name:t,attributes:n,passphrase:o})}export(){return this.schema.export()}async validate(t){const r={original:Re(t),isValid:!1,errors:[]},i=await this.schema.validate(t,{mutateOriginal:!0});return i===!0?r.isValid=!0:r.errors=i,r.data=t,r}async insert({id:t,...r}){const{errors:i,isValid:n,data:a}=await this.validate(r);if(!n)throw new At({bucket:this.client.config.bucket,resourceName:this.name,attributes:r,validation:i});!t&&t!==0&&(t=ni());const o=await this.schema.mapper(a);await this.client.putObject({metadata:o,key:K(`resource=${this.name}`,`id=${t}`)});const s=it({id:t},a);return this.emit("insert",s),s}async get(t){const r=await this.client.headObject(K(`resource=${this.name}`,`id=${t}`));let i=await this.schema.unmapper(r.Metadata);return i.id=t,i._length=r.ContentLength,i._createdAt=r.LastModified,r.Expiration&&(i._expiresAt=r.Expiration),this.emit("get",i),i}async exists(t){try{return await this.client.headObject(K(`resource=${this.name}`,`id=${t}`)),!0}catch{return!1}}async update(t,r){const i=await this.get(t),n=it(i,r);delete n.id;const{isValid:a,errors:o,data:s}=await this.validate(n);if(!a)throw new At({bucket:this.client.bucket,resourceName:this.name,attributes:r,validation:o});return await this.client.putObject({key:K(`resource=${this.name}`,`id=${t}`),body:"",metadata:await this.schema.mapper(s)}),s.id=t,this.emit("update",r,s),s}async delete(t){const r=K(`resource=${this.name}`,`id=${t}`),i=await this.client.deleteObject(r);return this.emit("delete",t),i}async count(){const t=await this.client.count({prefix:`resource=${this.name}`});return this.emit("count",t),t}async insertMany(t){const{results:r}=await ce.for(t).withConcurrency(this.parallelism).handleError(async(i,n)=>{this.emit("error",i,n),this.observers.map(a=>a.emit("error",this.name,i,n))}).process(async i=>await this.insert(i));return this.emit("insertMany",t.length),r}async deleteMany(t){const r=ai(t.map(n=>K(`resource=${this.name}`,`id=${n}`)),1e3),{results:i}=await ce.for(r).withConcurrency(this.parallelism).handleError(async(n,a)=>{this.emit("error",n,a),this.observers.map(o=>o.emit("error",this.name,n,a))}).process(async n=>{const a=await this.client.deleteObjects(n);return n.forEach(o=>{const s=o.split("=").pop();this.emit("deleted",s),this.observers.map(h=>h.emit("deleted",this.name,s))}),a});return this.emit("deleteMany",t.length),i}async deleteAll(){const t=await this.listIds();this.emit("deleteAll",t.length),await this.deleteMany(t)}async listIds(){const r=(await this.client.getAllKeys({prefix:`resource=${this.name}`})).map(i=>i.replace(`resource=${this.name}/id=`,""));return this.emit("listIds",r.length),r}async getMany(t){const{results:r}=await ce.for(t).withConcurrency(this.client.parallelism).process(async i=>{this.emit("id",i);const n=await this.get(i);return this.emit("data",n),n});return this.emit("getMany",t.length),r}async getAll(){let t=await this.listIds();if(t.length===0)return[];const{results:r}=await ce.for(t).withConcurrency(this.client.parallelism).process(async i=>await this.get(i));return this.emit("getAll",r.length),r}async page({offset:t=0,size:r=100}){const n=(await this.client.getKeysPage({offset:t,amount:r,prefix:`resource=${this.name}`})).map(o=>o.replace(`resource=${this.name}/id=`,""));return await this.getMany(n)}readable(){return new Ii({resource:this}).build()}writable(){return new Mi({resource:this}).build()}}class Di extends b{constructor(t){super(),this.version="1",this.resources={},this.options=t,this.verbose=t.verbose||!1,this.parallelism=parseInt(t.parallelism+"")||10,this.plugins=t.plugins||[],this.cache=t.cache,this.passphrase=t.passphrase||"secret",this.client=t.client||new ki({verbose:this.verbose,parallelism:this.parallelism,connectionString:t.connectionString}),this.bucket=this.client.bucket,this.keyPrefix=this.client.keyPrefix}async connect(){await this.startPlugins();let t=null;if(await this.client.exists("s3db.json")){const r=await this.client.getObject("s3db.json");t=JSON.parse(await lr(r?.Body)),t=this.unserializeMetadata(t)}else t=this.blankMetadataStructure(),await this.uploadMetadataFile();for(const r of Object.entries(t.resources)){const[i,n]=r;this.resources[i]=new Fi({name:i,client:this.client,options:n.options,attributes:n.schema,parallelism:this.parallelism,passphrase:this.passphrase,observers:[this]})}this.emit("connected",new Date)}async startPlugins(){const t=this;if(!tr(this.plugins)){const r=this.plugins.map(a=>qa(a)?new a(this):a),i=r.map(async a=>{a.beforeSetup&&await a.beforeSetup(),await a.setup(t),a.afterSetup&&await a.afterSetup()});await Promise.all(i);const n=r.map(async a=>{a.beforeStart&&await a.beforeStart(),await a.start(),a.afterStart&&await a.afterStart()});await Promise.all(n)}}unserializeMetadata(t){const r={...t};if(tr(r.resources))return r;for(const[i,n]of Object.entries(r.resources))for(const[a,o]of Object.entries(n.attributes))r.resources[i].attributes[a]=JSON.parse(o);return r}async uploadMetadataFile(){const t={version:this.version,resources:Object.entries(this.resources).reduce((r,i)=>{const[n,a]=i;return r[n]=a.export(),r},{})};await this.client.putObject({key:"s3db.json",contentType:"application/json",body:JSON.stringify(t,null,2)})}blankMetadataStructure(){return{version:"1",resources:{}}}async createResource({name:t,attributes:r,options:i={}}){const n=new Fi({name:t,attributes:r,observers:[this],client:this.client,options:{autoDecrypt:!0,cache:this.cache,...i}});return this.resources[t]=n,await this.uploadMetadataFile(),this.emit("s3db.resourceCreated",t),n}resource(t){return this.resources[t]?this.resources[t]:Promise.reject(`resource ${t} does not exist`)}}class Lo extends Di{}class hr extends b{async _set(t,r){}async _get(t){}async _del(t){}async _clear(t){}async set(t,r){return await this._set(t,r),this.emit("set",r),r}async get(t){const r=await this._get(t);return this.emit("get",r),r}async del(t){const r=await this._del(t);return this.emit("delete",r),r}async clear(){const t=await this._clear();return this.emit("clear",t),t}}class Co extends hr{constructor(){super(),this.cache={}}async _set(t,r){return this.cache[t]=r,r}async _get(t){return this.cache[t]}async _del(t){return delete this.cache[t],!0}async _clear(){return this.cache={},!0}}var We=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{},Q=[],Y=[],Oo=typeof Uint8Array<"u"?Uint8Array:Array,cr=!1;function Ni(){cr=!0;for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=0,r=e.length;t<r;++t)Q[t]=e[t],Y[e.charCodeAt(t)]=t;Y[45]=62,Y[95]=63}function Io(e){cr||Ni();var t,r,i,n,a,o,s=e.length;if(s%4>0)throw new Error("Invalid string. Length must be a multiple of 4");a=e[s-2]==="="?2:e[s-1]==="="?1:0,o=new Oo(s*3/4-a),i=a>0?s-4:s;var h=0;for(t=0,r=0;t<i;t+=4,r+=3)n=Y[e.charCodeAt(t)]<<18|Y[e.charCodeAt(t+1)]<<12|Y[e.charCodeAt(t+2)]<<6|Y[e.charCodeAt(t+3)],o[h++]=n>>16&255,o[h++]=n>>8&255,o[h++]=n&255;return a===2?(n=Y[e.charCodeAt(t)]<<2|Y[e.charCodeAt(t+1)]>>4,o[h++]=n&255):a===1&&(n=Y[e.charCodeAt(t)]<<10|Y[e.charCodeAt(t+1)]<<4|Y[e.charCodeAt(t+2)]>>2,o[h++]=n>>8&255,o[h++]=n&255),o}function Mo(e){return Q[e>>18&63]+Q[e>>12&63]+Q[e>>6&63]+Q[e&63]}function Fo(e,t,r){for(var i,n=[],a=t;a<r;a+=3)i=(e[a]<<16)+(e[a+1]<<8)+e[a+2],n.push(Mo(i));return n.join("")}function zi(e){cr||Ni();for(var t,r=e.length,i=r%3,n="",a=[],o=16383,s=0,h=r-i;s<h;s+=o)a.push(Fo(e,s,s+o>h?h:s+o));return i===1?(t=e[r-1],n+=Q[t>>2],n+=Q[t<<4&63],n+="=="):i===2&&(t=(e[r-2]<<8)+e[r-1],n+=Q[t>>10],n+=Q[t>>4&63],n+=Q[t<<2&63],n+="="),a.push(n),a.join("")}function Lt(e,t,r,i,n){var a,o,s=n*8-i-1,h=(1<<s)-1,f=h>>1,l=-7,d=r?n-1:0,p=r?-1:1,u=e[t+d];for(d+=p,a=u&(1<<-l)-1,u>>=-l,l+=s;l>0;a=a*256+e[t+d],d+=p,l-=8);for(o=a&(1<<-l)-1,a>>=-l,l+=i;l>0;o=o*256+e[t+d],d+=p,l-=8);if(a===0)a=1-f;else{if(a===h)return o?NaN:(u?-1:1)*(1/0);o=o+Math.pow(2,i),a=a-f}return(u?-1:1)*o*Math.pow(2,a-i)}function Pi(e,t,r,i,n,a){var o,s,h,f=a*8-n-1,l=(1<<f)-1,d=l>>1,p=n===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=i?0:a-1,g=i?1:-1,S=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=l):(o=Math.floor(Math.log(t)/Math.LN2),t*(h=Math.pow(2,-o))<1&&(o--,h*=2),o+d>=1?t+=p/h:t+=p*Math.pow(2,1-d),t*h>=2&&(o++,h/=2),o+d>=l?(s=0,o=l):o+d>=1?(s=(t*h-1)*Math.pow(2,n),o=o+d):(s=t*Math.pow(2,d-1)*Math.pow(2,n),o=0));n>=8;e[r+u]=s&255,u+=g,s/=256,n-=8);for(o=o<<n|s,f+=n;f>0;e[r+u]=o&255,u+=g,o/=256,f-=8);e[r+u-g]|=S*128}var Do={}.toString,Ui=Array.isArray||function(e){return Do.call(e)=="[object Array]"};/*!
|
|
7
|
+
${JSON.stringify(n,null,2)}`}),this.resourceName=r,this.attributes=i,this.validation=n}}class mi extends Ke{}const bi={NotFound:gi,NoSuchKey:or,UnknownError:mi,NoSuchBucket:wi,MissingMetadata:vi,InvalidResourceItem:At},yi="us-east-1",xi="https://s3.us-east-1.amazonaws.com";class Ei{constructor(t){let r;try{r=new URL(t)}catch{throw new Error("Invalid connection string: "+t)}this.region=yi,r.protocol==="s3:"?this.defineS3(r):this.defineMinio(r);for(const[i,n]of r.searchParams.entries())this[i]=n}defineS3(t){if(this.bucket=t.hostname,this.accessKeyId=t.username,this.secretAccessKey=t.password,this.endpoint=xi,["/","",null].includes(t.pathname))this.keyPrefix="";else{let[,...r]=t.pathname.split("/");this.keyPrefix=[...r||[]].join("/")}}defineMinio(t){if(this.forcePathStyle=!0,this.endpoint=t.origin,this.accessKeyId=t.username,this.secretAccessKey=t.password,["/","",null].includes(t.pathname))this.bucket="s3db",this.keyPrefix="";else{let[,r,...i]=t.pathname.split("/");this.bucket=r,this.keyPrefix=[...i||[]].join("/")}}}class ki extends b{constructor({verbose:t=!1,id:r=null,AwsS3Client:i,connectionString:n,parallelism:a=10}){super(),this.verbose=t,this.id=r??ni(7),this.parallelism=a,this.config=new Ei(n),this.client=i||this.createClient()}createClient(){let t={region:this.config.region,endpoint:this.config.endpoint};return this.config.forcePathStyle&&(t.forcePathStyle=!0),this.config.accessKeyId&&(t.credentials={accessKeyId:this.config.accessKeyId,secretAccessKey:this.config.secretAccessKey}),new Ga(t)}async sendCommand(t){this.emit("command.request",t.constructor.name,t.input);const r=console.warn;try{console.warn=n=>{n.includes("Stream of unknown length")||r(n)}}catch(n){console.error(n)}const i=await this.client.send(t);this.emit("command.response",t.constructor.name,i,t.input);try{console.warn=r}catch(n){console.error(n)}return i}errorProxy(t,r){this.verbose&&(r.bucket=this.config.bucket,r.config=this.config,r.verbose=this.verbose),t.data=r;const i=bi[t.name];return i?new i(r):t}async putObject({key:t,metadata:r,contentType:i,body:n,contentEncoding:a}){const o={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t,Metadata:{...r},Body:n||"",ContentType:i,ContentEncoding:a};try{const s=await this.sendCommand(new Va(o));return this.emit("putObject",s,o),s}catch(s){throw this.errorProxy(s,{key:t,command:o})}}async getObject(t){const r={Bucket:this.config.bucket,Key:ie.join(this.config.keyPrefix,t)};try{const i=await this.sendCommand(new Xa(r));return this.emit("getObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async headObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t};try{const i=await this.client.send(new Ja(r));return this.emit("headObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async copyObject({from:t,to:r}){const i={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,r):r,CopySource:ie.join(this.config.bucket,this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t)};try{const n=await this.client.send(new Qa(i));return this.emit("copyObject",n,i),n}catch(n){throw this.errorProxy(n,{from:t,to:r,command:i})}}async exists(t){try{return await this.headObject(t),!0}catch(r){if(r.name==="NoSuchKey")return!1;if(r.name==="NotFound")return!1;throw r}}async deleteObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,t):t};try{const i=await this.sendCommand(new eo(r));return this.emit("deleteObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async deleteObjects(t){const r=ai(t,1e3),{results:i,errors:n}=await ce.for(r).withConcurrency(this.parallelism).process(async o=>{const s={Bucket:this.config.bucket,Delete:{Objects:o.map(h=>({Key:this.config.keyPrefix?ie.join(this.config.keyPrefix,h):h}))}};try{return await this.sendCommand(new to(s))}catch(h){throw this.errorProxy(h,{key,command:s})}}),a={deleted:i,notFound:n};return this.emit("deleteObjects",a,t),a}async deleteAll({prefix:t}={}){const r=await this.getAllKeys({prefix:t}),i=await this.deleteObjects(r);return this.emit("deleteAll",{prefix:t,report:i}),i}async moveObject({from:t,to:r}){try{return await this.copyObject({from:t,to:r}),await this.deleteObject(t),!0}catch(i){throw this.errorProxy(i,{from:t,to:r,command:options})}}async listObjects({prefix:t,maxKeys:r=1e3,continuationToken:i}={}){const n={Bucket:this.config.bucket,MaxKeys:r,ContinuationToken:i,Prefix:this.config.keyPrefix?ie.join(this.config.keyPrefix,t||""):t||""};try{const a=await this.sendCommand(new ro(n));return this.emit("listObjects",a,n),a}catch(a){throw this.errorProxy(a,{command:n})}}async count({prefix:t}={}){let r=0,i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);r+=o.KeyCount||0,i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.emit("count",r,{prefix:t}),r}async getAllKeys({prefix:t}={}){let r=[],i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);o.Contents&&(r=r.concat(o.Contents.map(s=>s.Key))),i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.config.keyPrefix&&(r=r.map(a=>a.replace(this.config.keyPrefix,"")).map(a=>a.startsWith("/")?a.replace("/",""):a)),this.emit("getAllKeys",r,{prefix:t}),r}async getContinuationTokenAfterOffset(t={}){const{prefix:r,offset:i=1e3}=t;if(i===0)return null;let n=!0,a,o=0;for(;n;){let s=i<1e3?i:i-o>1e3?1e3:i-o;const h={prefix:r,maxKeys:s,continuationToken:a},f=await this.listObjects(h);if(f.Contents&&(o+=f.Contents.length),n=f.IsTruncated||!1,a=f.NextContinuationToken,o>=i)break}return this.emit("getContinuationTokenAfterOffset",a,t),a}async getKeysPage(t={}){const{prefix:r,offset:i=0,amount:n=100}=t;let a=[],o=!0,s;for(i>0&&(s=await this.getContinuationTokenAfterOffset({prefix:r,offset:i}));o;){const h={prefix:r,continuationToken:s},f=await this.listObjects(h);if(f.Contents&&(a=a.concat(f.Contents.map(l=>l.Key))),o=f.IsTruncated||!1,s=f.NextContinuationToken,a.length>n){a=a.splice(0,n);break}}return this.config.keyPrefix&&(a=a.map(h=>h.replace(this.config.keyPrefix,"")).map(h=>h.startsWith("/")?h.replace("/",""):h)),this.emit("getKeysPage",a,t),a}async moveAllObjects({prefixFrom:t,prefixTo:r}){const i=await this.getAllKeys({prefix:t}),{results:n,errors:a}=await ce.for(i).withConcurrency(this.parallelism).process(async o=>{const s=o.replace(t,r);try{return await this.moveObject({from:o,to:s}),s}catch(h){throw this.errorProxy(h,{from:o,to:s})}});if(this.emit("moveAllObjects",{results:n,errors:a},{prefixFrom:t,prefixTo:r}),a.length>0)throw console.log({errors:a}),new Error("Some objects could not be moved");return n}}async function Tt(){let e;if(process)try{const{webcrypto:t}=await import("crypto");e=t}catch{throw new Error("Crypto API not available")}else window&&(e=window.crypto);if(!e)throw new Error("Could not load any crypto library");return e}async function Si(e){const t=await Tt(),i=new TextEncoder().encode(e),n=await t.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(n)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function sr(e,t){const r=await Tt(),i=r.getRandomValues(new Uint8Array(16)),n=await Ai(t,i),a=r.getRandomValues(new Uint8Array(12)),s=new TextEncoder().encode(e),h=await r.subtle.encrypt({name:"AES-GCM",iv:a},n,s),f=new Uint8Array(i.length+a.length+h.byteLength);return f.set(i),f.set(a,i.length),f.set(new Uint8Array(h),i.length+a.length),Ro(f)}async function Ri(e,t){const r=await Tt(),i=Ao(e),n=i.slice(0,16),a=i.slice(16,28),o=i.slice(28),s=await Ai(t,n),h=await r.subtle.decrypt({name:"AES-GCM",iv:a},s,o);return new TextDecoder().decode(h)}async function Ai(e,t){const r=await Tt(),n=new TextEncoder().encode(e),a=await r.subtle.importKey("raw",n,{name:"PBKDF2"},!1,["deriveKey"]);return await r.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},a,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}function Ro(e){if(oi(process))return Buffer.from(e).toString("base64");{const t=String.fromCharCode.apply(null,new Uint8Array(e));return window.btoa(t)}}function Ao(e){if(oi(process))return new Uint8Array(Buffer.from(e,"base64"));{const t=window.atob(e),r=t.length,i=new Uint8Array(r);for(let n=0;n<r;n++)i[n]=t.charCodeAt(n);return i}}async function fr(e,t,r){if(!this.passphrase)return t.push({actual:e,type:"encryptionKeyMissing"}),e;try{return await sr(String(e),this.passphrase)}catch(i){t.push({actual:e,type:"encryptionProblem",error:i})}return e}class Ti extends no{constructor({options:t,passphrase:r,autoEncrypt:i=!0}={}){super(it({},{useNewCustomCheckerFunction:!0,messages:{encryptionKeyMissing:"Missing configuration for secrets encryption.",encryptionProblem:"Problem encrypting secret. Actual: {actual}. Error: {error}"},defaults:{string:{trim:!0},object:{strict:"remove"}}},t)),this.passphrase=r,this.autoEncrypt=i,this.alias("secret",{type:"string",custom:this.autoEncrypt?fr:void 0,messages:{string:"The '{field}' field must be a string.",stringMin:"This secret '{field}' field length must be at least {expected} long."}}),this.alias("secretAny",{type:"any",custom:this.autoEncrypt?fr:void 0}),this.alias("secretNumber",{type:"number",custom:this.autoEncrypt?fr:void 0})}}const Li=new Proxy(Ti,{instance:null,construct(e,t){return this.instance||(this.instance=new e(...t)),this.instance}}),To={trim:e=>e.trim(),encrypt:(e,{passphrase:t})=>sr(e,t),decrypt:(e,{passphrase:t})=>Ri(e,t),toString:e=>String(e),fromArray:(e,{separator:t})=>(e||[]).join(t),toArray:(e,{separator:t})=>(e||"").split(t),toJSON:e=>JSON.stringify(e),fromJSON:e=>JSON.parse(e),toNumber:e=>fi(e)?e.includes(".")?parseFloat(e):parseInt(e):e,toBool:e=>["1","true","yes",!0,"y"].includes(e),fromBool:e=>e?"1":"0"};class ti{constructor(t){const{map:r,name:i,attributes:n,passphrase:a,version:o=1,options:s={}}=t;if(this.name=i,this.version=o,this.attributes=n,this.passphrase=a??"secret",this.options=it({},this.defaultOptions(),s),this.validator=new Li({autoEncrypt:!1}).compile(it({$$async:!0},Re(this.attributes))),this.options.generateAutoHooks&&this.generateAutoHooks(),!tr(r))this.map=r,this.reversedMap=si(r);else{const h=rr(this.attributes,{safe:!0});this.reversedMap={...Object.keys(h).filter(f=>!f.includes("$$"))},this.map=si(this.reversedMap)}}defaultOptions(){return{autoEncrypt:!0,autoDecrypt:!0,arraySeparator:"|",generateAutoHooks:!0,hooks:{beforeMap:{},afterMap:{},beforeUnmap:{},afterUnmap:{}}}}addHook(t,r,i){this.options.hooks[t][r]||(this.options.hooks[t][r]=[]),this.options.hooks[t][r]=Ka([...this.options.hooks[t][r],i])}generateAutoHooks(){const t=rr(Re(this.attributes),{safe:!0});for(const[r,i]of Object.entries(t))i.includes("array")?(this.addHook("beforeMap",r,"fromArray"),this.addHook("afterUnmap",r,"toArray")):(i.includes("secret")&&(this.options.autoEncrypt&&this.addHook("beforeMap",r,"encrypt"),this.options.autoDecrypt&&this.addHook("afterUnmap",r,"decrypt")),i.includes("number")&&(this.addHook("beforeMap",r,"toString"),this.addHook("afterUnmap",r,"toNumber")),i.includes("boolean")&&(this.addHook("beforeMap",r,"fromBool"),this.addHook("afterUnmap",r,"toBool")))}static import(t){let{map:r,name:i,options:n,version:a,attributes:o}=fi(t)?JSON.parse(t):t;return new ti({map:r,name:i,options:n,version:a,attributes:o})}export(){const t={version:this.version,name:this.name,options:this.options,attributes:Re(this.attributes),map:this.map};for(const[r,i]of Object.entries(this.attributes))t.attributes[r]=JSON.stringify(i);return t}async applyHooksActions(t,r){for(const[i,n]of Object.entries(this.options.hooks[r]))for(const a of n){const o=Wa(t,i);o!==void 0&&Ya(t,i,await To[a](o,{passphrase:this.passphrase,separator:this.options.arraySeparator}))}}async validate(t,{mutateOriginal:r=!1}={}){let i=r?t:Re(t);return await this.validator(i)}async mapper(t){const r=rr(Re(t),{safe:!0});await this.applyHooksActions(r,"beforeMap");const i={_v:this.version+""};for(const[n,a]of Object.entries(r))i[this.map[n]]=a;return await this.applyHooksActions(i,"afterMap"),i}async unmapper(t){const r=Re(t);delete r._v,await this.applyHooksActions(r,"beforeUnmap");const i={};for(const[n,a]of Object.entries(r))i[this.reversedMap[n]]=a;return await this.applyHooksActions(i,"afterUnmap"),io(i)}}class Ci extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new ao({highWaterMark:this.client.parallelism*3,start:this._start.bind(this),pull:this._pull.bind(this),cancel:this._cancel.bind(this)})}build(){return this.stream.getReader()}async _start(t){this.controller=t,this.continuationToken=null,this.closeNextIteration=!1}async _pull(t){if(this.closeNextIteration){t.close();return}const r=await this.client.listObjects({prefix:`resource=${this.resource.name}`,continuationToken:this.continuationToken}),i=r?.Contents.map(n=>n.Key).map(n=>n.replace(this.client.config.keyPrefix,"")).map(n=>n.startsWith("/")?n.replace("/",""):n).map(n=>n.replace(`resource=${this.resource.name}/id=`,""));this.continuationToken=r.NextContinuationToken,this.enqueue(i),r.IsTruncated||(this.closeNextIteration=!0)}enqueue(t){t.forEach(r=>{this.controller.enqueue(r),this.emit("id",r)})}_cancel(t){console.warn("Stream cancelled",t)}}class Oi extends Ci{enqueue(t){this.controller.enqueue(t),this.emit("page",t)}}class Ii extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.input=new Oi({resource:this.resource}),this.output=new oo({transform:this._transform.bind(this)},{highWaterMark:this.client.parallelism*2},{highWaterMark:1}),this.stream=this.input.stream.pipeThrough(this.output)}build(){return this.stream.getReader()}async _transform(t,r){await ce.for(t).withConcurrency(this.client.parallelism).process(async i=>{const n=await this.resource.get(i);return r.enqueue(n),n})}}class Mi extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new so({start:this._start.bind(this),write:this._write.bind(this),close:this._close.bind(this),abort:this._abort.bind(this)})}build(){return this.stream.getWriter()}async _start(t){this.controller=t}async _write(t,r){const i=this.resource;await ce.for([].concat(t)).withConcurrency(this.client.parallelism).process(async n=>{await i.insert(n)})}async _close(t){}async _abort(t){console.error("Stream aborted:",t)}}function lr(e){return new Promise((t,r)=>{const i=[];e.on("data",n=>i.push(n)),e.on("error",r),e.on("end",()=>t(Buffer.concat(i).toString("utf-8")))})}class Fi extends b{constructor({name:t,client:r,options:i={},attributes:n={},parallelism:a=10,passphrase:o="secret",observers:s=[]}){super(),this.name=t,this.client=r,this.options=i,this.observers=s,this.parallelism=a,this.passphrase=o??"secret",this.schema=new ti({name:t,attributes:n,passphrase:o})}export(){return this.schema.export()}async validate(t){const r={original:Re(t),isValid:!1,errors:[]},i=await this.schema.validate(t,{mutateOriginal:!0});return i===!0?r.isValid=!0:r.errors=i,r.data=t,r}async insert({id:t,...r}){const{errors:i,isValid:n,data:a}=await this.validate(r);if(!n)throw new At({bucket:this.client.config.bucket,resourceName:this.name,attributes:r,validation:i});!t&&t!==0&&(t=ni());const o=await this.schema.mapper(a);await this.client.putObject({metadata:o,key:K(`resource=${this.name}`,`id=${t}`)});const s=it({id:t},a);return this.emit("insert",s),s}async get(t){const r=await this.client.headObject(K(`resource=${this.name}`,`id=${t}`));let i=await this.schema.unmapper(r.Metadata);return i.id=t,i._length=r.ContentLength,i._createdAt=r.LastModified,r.Expiration&&(i._expiresAt=r.Expiration),this.emit("get",i),i}async exists(t){try{return await this.client.headObject(K(`resource=${this.name}`,`id=${t}`)),!0}catch{return!1}}async update(t,r){const i=await this.get(t),n=it(i,r);delete n.id;const{isValid:a,errors:o,data:s}=await this.validate(n);if(!a)throw new At({bucket:this.client.bucket,resourceName:this.name,attributes:r,validation:o});return await this.client.putObject({key:K(`resource=${this.name}`,`id=${t}`),body:"",metadata:await this.schema.mapper(s)}),s.id=t,this.emit("update",r,s),s}async delete(t){const r=K(`resource=${this.name}`,`id=${t}`),i=await this.client.deleteObject(r);return this.emit("delete",t),i}async count(){const t=await this.client.count({prefix:`resource=${this.name}`});return this.emit("count",t),t}async insertMany(t){const{results:r}=await ce.for(t).withConcurrency(this.parallelism).handleError(async(i,n)=>{this.emit("error",i,n),this.observers.map(a=>a.emit("error",this.name,i,n))}).process(async i=>await this.insert(i));return this.emit("insertMany",t.length),r}async deleteMany(t){const r=ai(t.map(n=>K(`resource=${this.name}`,`id=${n}`)),1e3),{results:i}=await ce.for(r).withConcurrency(this.parallelism).handleError(async(n,a)=>{this.emit("error",n,a),this.observers.map(o=>o.emit("error",this.name,n,a))}).process(async n=>{const a=await this.client.deleteObjects(n);return n.forEach(o=>{const s=o.split("=").pop();this.emit("deleted",s),this.observers.map(h=>h.emit("deleted",this.name,s))}),a});return this.emit("deleteMany",t.length),i}async deleteAll(){const t=await this.listIds();this.emit("deleteAll",t.length),await this.deleteMany(t)}async listIds(){const r=(await this.client.getAllKeys({prefix:`resource=${this.name}`})).map(i=>i.replace(`resource=${this.name}/id=`,""));return this.emit("listIds",r.length),r}async getMany(t){const{results:r}=await ce.for(t).withConcurrency(this.client.parallelism).process(async i=>{this.emit("id",i);const n=await this.get(i);return this.emit("data",n),n});return this.emit("getMany",t.length),r}async getAll(){let t=await this.listIds();if(t.length===0)return[];const{results:r}=await ce.for(t).withConcurrency(this.client.parallelism).process(async i=>await this.get(i));return this.emit("getAll",r.length),r}async page({offset:t=0,size:r=100}){const n=(await this.client.getKeysPage({offset:t,amount:r,prefix:`resource=${this.name}`})).map(o=>o.replace(`resource=${this.name}/id=`,""));return await this.getMany(n)}readable(){return new Ii({resource:this}).build()}writable(){return new Mi({resource:this}).build()}}class Di extends b{constructor(t){super(),this.version="1",this.resources={},this.options=t,this.verbose=t.verbose||!1,this.parallelism=parseInt(t.parallelism+"")||10,this.plugins=t.plugins||[],this.cache=t.cache,this.passphrase=t.passphrase||"secret",this.client=t.client||new ki({verbose:this.verbose,parallelism:this.parallelism,connectionString:t.connectionString}),this.bucket=this.client.bucket,this.keyPrefix=this.client.keyPrefix}async connect(){await this.startPlugins();let t=null;if(await this.client.exists("s3db.json")){const r=await this.client.getObject("s3db.json");t=JSON.parse(await lr(r?.Body)),t=this.unserializeMetadata(t)}else t=this.blankMetadataStructure(),await this.uploadMetadataFile();for(const r of Object.entries(t.resources)){const[i,n]=r;this.resources[i]=new Fi({name:i,client:this.client,options:n.options,attributes:n.schema,parallelism:this.parallelism,passphrase:this.passphrase,observers:[this]})}this.emit("connected",new Date)}async startPlugins(){const t=this;if(!tr(this.plugins)){const r=this.plugins.map(a=>qa(a)?new a(this):a),i=r.map(async a=>{a.beforeSetup&&await a.beforeSetup(),await a.setup(t),a.afterSetup&&await a.afterSetup()});await Promise.all(i);const n=r.map(async a=>{a.beforeStart&&await a.beforeStart(),await a.start(),a.afterStart&&await a.afterStart()});await Promise.all(n)}}unserializeMetadata(t){const r={...t};if(tr(r.resources))return r;for(const[i,n]of Object.entries(r.resources))for(const[a,o]of Object.entries(n.attributes))r.resources[i].attributes[a]=JSON.parse(o);return r}async uploadMetadataFile(){const t={version:this.version,resources:Object.entries(this.resources).reduce((r,i)=>{const[n,a]=i;return r[n]=a.export(),r},{})};await this.client.putObject({key:"s3db.json",contentType:"application/json",body:JSON.stringify(t,null,2)})}blankMetadataStructure(){return{version:"1",resources:{}}}async createResource({name:t,attributes:r,options:i={}}){const n=new Fi({name:t,attributes:r,observers:[this],client:this.client,options:{autoDecrypt:!0,cache:this.cache,...i}});return this.resources[t]=n,await this.uploadMetadataFile(),this.emit("s3db.resourceCreated",t),n}resource(t){return this.resources[t]?this.resources[t]:Promise.reject(`resource ${t} does not exist`)}}class Lo extends Di{}class hr extends b{async _set(t,r){}async _get(t){}async _del(t){}async _clear(t){}async set(t,r){return await this._set(t,r),this.emit("set",r),r}async get(t){const r=await this._get(t);return this.emit("get",r),r}async del(t){const r=await this._del(t);return this.emit("delete",r),r}async clear(){const t=await this._clear();return this.emit("clear",t),t}}class Co extends hr{constructor(){super(),this.cache={}}async _set(t,r){return this.cache[t]=r,r}async _get(t){return this.cache[t]}async _del(t){return delete this.cache[t],!0}async _clear(){return this.cache={},!0}}var We=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{},Q=[],Y=[],Oo=typeof Uint8Array<"u"?Uint8Array:Array,cr=!1;function Ni(){cr=!0;for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=0,r=e.length;t<r;++t)Q[t]=e[t],Y[e.charCodeAt(t)]=t;Y[45]=62,Y[95]=63}function Io(e){cr||Ni();var t,r,i,n,a,o,s=e.length;if(s%4>0)throw new Error("Invalid string. Length must be a multiple of 4");a=e[s-2]==="="?2:e[s-1]==="="?1:0,o=new Oo(s*3/4-a),i=a>0?s-4:s;var h=0;for(t=0,r=0;t<i;t+=4,r+=3)n=Y[e.charCodeAt(t)]<<18|Y[e.charCodeAt(t+1)]<<12|Y[e.charCodeAt(t+2)]<<6|Y[e.charCodeAt(t+3)],o[h++]=n>>16&255,o[h++]=n>>8&255,o[h++]=n&255;return a===2?(n=Y[e.charCodeAt(t)]<<2|Y[e.charCodeAt(t+1)]>>4,o[h++]=n&255):a===1&&(n=Y[e.charCodeAt(t)]<<10|Y[e.charCodeAt(t+1)]<<4|Y[e.charCodeAt(t+2)]>>2,o[h++]=n>>8&255,o[h++]=n&255),o}function Mo(e){return Q[e>>18&63]+Q[e>>12&63]+Q[e>>6&63]+Q[e&63]}function Fo(e,t,r){for(var i,n=[],a=t;a<r;a+=3)i=(e[a]<<16)+(e[a+1]<<8)+e[a+2],n.push(Mo(i));return n.join("")}function zi(e){cr||Ni();for(var t,r=e.length,i=r%3,n="",a=[],o=16383,s=0,h=r-i;s<h;s+=o)a.push(Fo(e,s,s+o>h?h:s+o));return i===1?(t=e[r-1],n+=Q[t>>2],n+=Q[t<<4&63],n+="=="):i===2&&(t=(e[r-2]<<8)+e[r-1],n+=Q[t>>10],n+=Q[t>>4&63],n+=Q[t<<2&63],n+="="),a.push(n),a.join("")}function Lt(e,t,r,i,n){var a,o,s=n*8-i-1,h=(1<<s)-1,f=h>>1,l=-7,d=r?n-1:0,p=r?-1:1,u=e[t+d];for(d+=p,a=u&(1<<-l)-1,u>>=-l,l+=s;l>0;a=a*256+e[t+d],d+=p,l-=8);for(o=a&(1<<-l)-1,a>>=-l,l+=i;l>0;o=o*256+e[t+d],d+=p,l-=8);if(a===0)a=1-f;else{if(a===h)return o?NaN:(u?-1:1)*(1/0);o=o+Math.pow(2,i),a=a-f}return(u?-1:1)*o*Math.pow(2,a-i)}function Pi(e,t,r,i,n,a){var o,s,h,f=a*8-n-1,l=(1<<f)-1,d=l>>1,p=n===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=i?0:a-1,g=i?1:-1,S=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=l):(o=Math.floor(Math.log(t)/Math.LN2),t*(h=Math.pow(2,-o))<1&&(o--,h*=2),o+d>=1?t+=p/h:t+=p*Math.pow(2,1-d),t*h>=2&&(o++,h/=2),o+d>=l?(s=0,o=l):o+d>=1?(s=(t*h-1)*Math.pow(2,n),o=o+d):(s=t*Math.pow(2,d-1)*Math.pow(2,n),o=0));n>=8;e[r+u]=s&255,u+=g,s/=256,n-=8);for(o=o<<n|s,f+=n;f>0;e[r+u]=o&255,u+=g,o/=256,f-=8);e[r+u-g]|=S*128}var Do={}.toString,Ui=Array.isArray||function(e){return Do.call(e)=="[object Array]"};/*!
|
|
8
8
|
* The buffer module from node.js, for the browser.
|
|
9
9
|
*
|
|
10
10
|
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
package/dist/s3db.iife.js
CHANGED
|
@@ -1340,7 +1340,7 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
1340
1340
|
toJSON: (value) => JSON.stringify(value),
|
|
1341
1341
|
fromJSON: (value) => JSON.parse(value),
|
|
1342
1342
|
toNumber: (value) => lodashEs.isString(value) ? value.includes(".") ? parseFloat(value) : parseInt(value) : value,
|
|
1343
|
-
toBool: (value) => ["
|
|
1343
|
+
toBool: (value) => ["1", "true", "yes", true, "y"].includes(value),
|
|
1344
1344
|
fromBool: (value) => value ? "1" : "0"
|
|
1345
1345
|
};
|
|
1346
1346
|
class Schema {
|
|
@@ -1450,7 +1450,7 @@ ${JSON.stringify(validation, null, 2)}`
|
|
|
1450
1450
|
for (const [attribute, actions] of Object.entries(this.options.hooks[hook])) {
|
|
1451
1451
|
for (const action of actions) {
|
|
1452
1452
|
const value = lodashEs.get(resourceItem, attribute);
|
|
1453
|
-
if (value) {
|
|
1453
|
+
if (value !== void 0) {
|
|
1454
1454
|
lodashEs.set(resourceItem, attribute, await SchemaActions[action](value, {
|
|
1455
1455
|
passphrase: this.passphrase,
|
|
1456
1456
|
separator: this.options.arraySeparator
|
package/dist/s3db.iife.min.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
Verbose:
|
|
5
5
|
|
|
6
6
|
${JSON.stringify(n,null,2)}`),super(i),typeof Error.captureStackTrace=="function"?Error.captureStackTrace(this,this.constructor):this.stack=new Error(i).stack,super.name=this.constructor.name,this.name=this.constructor.name,this.bucket=r,this.thrownAt=new Date}toJson(){return{...this}}toString(){return`${this.name} | ${this.message}`}}class ci extends Ye{constructor({bucket:t,...r}){super({...r,bucket:t,message:`Bucket does not exists [bucket:${t}]`})}}class sr extends Ye{constructor({bucket:t,key:r,...i}){super({...i,bucket:t,message:`Key [${r}] does not exists [bucket:${t}/${r}]`}),this.key=r}}class ui extends sr{}class di extends Ye{constructor({bucket:t,...r}){super({...r,bucket:t,message:`Missing metadata for bucket [bucket:${t}]`})}}class Rt extends Ye{constructor({bucket:t,resourceName:r,attributes:i,validation:n}){super({bucket:t,message:`This item is not valid. Resource=${r} [bucket:${t}].
|
|
7
|
-
${JSON.stringify(n,null,2)}`}),this.resourceName=r,this.attributes=i,this.validation=n}}class pi extends Ye{}const _i={NotFound:ui,NoSuchKey:sr,UnknownError:pi,NoSuchBucket:ci,MissingMetadata:di,InvalidResourceItem:Rt},wi="us-east-1",gi="https://s3.us-east-1.amazonaws.com";class vi{constructor(t){let r;try{r=new URL(t)}catch{throw new Error("Invalid connection string: "+t)}this.region=wi,r.protocol==="s3:"?this.defineS3(r):this.defineMinio(r);for(const[i,n]of r.searchParams.entries())this[i]=n}defineS3(t){if(this.bucket=t.hostname,this.accessKeyId=t.username,this.secretAccessKey=t.password,this.endpoint=gi,["/","",null].includes(t.pathname))this.keyPrefix="";else{let[,...r]=t.pathname.split("/");this.keyPrefix=[...r||[]].join("/")}}defineMinio(t){if(this.forcePathStyle=!0,this.endpoint=t.origin,this.accessKeyId=t.username,this.secretAccessKey=t.password,["/","",null].includes(t.pathname))this.bucket="s3db",this.keyPrefix="";else{let[,r,...i]=t.pathname.split("/");this.bucket=r,this.keyPrefix=[...i||[]].join("/")}}}class mi extends b{constructor({verbose:t=!1,id:r=null,AwsS3Client:i,connectionString:n,parallelism:a=10}){super(),this.verbose=t,this.id=r??ii.nanoid(7),this.parallelism=a,this.config=new vi(n),this.client=i||this.createClient()}createClient(){let t={region:this.config.region,endpoint:this.config.endpoint};return this.config.forcePathStyle&&(t.forcePathStyle=!0),this.config.accessKeyId&&(t.credentials={accessKeyId:this.config.accessKeyId,secretAccessKey:this.config.secretAccessKey}),new pe.S3Client(t)}async sendCommand(t){this.emit("command.request",t.constructor.name,t.input);const r=console.warn;try{console.warn=n=>{n.includes("Stream of unknown length")||r(n)}}catch(n){console.error(n)}const i=await this.client.send(t);this.emit("command.response",t.constructor.name,i,t.input);try{console.warn=r}catch(n){console.error(n)}return i}errorProxy(t,r){this.verbose&&(r.bucket=this.config.bucket,r.config=this.config,r.verbose=this.verbose),t.data=r;const i=_i[t.name];return i?new i(r):t}async putObject({key:t,metadata:r,contentType:i,body:n,contentEncoding:a}){const o={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t,Metadata:{...r},Body:n||"",ContentType:i,ContentEncoding:a};try{const s=await this.sendCommand(new pe.PutObjectCommand(o));return this.emit("putObject",s,o),s}catch(s){throw this.errorProxy(s,{key:t,command:o})}}async getObject(t){const r={Bucket:this.config.bucket,Key:ae.join(this.config.keyPrefix,t)};try{const i=await this.sendCommand(new pe.GetObjectCommand(r));return this.emit("getObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async headObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t};try{const i=await this.client.send(new pe.HeadObjectCommand(r));return this.emit("headObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async copyObject({from:t,to:r}){const i={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,r):r,CopySource:ae.join(this.config.bucket,this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t)};try{const n=await this.client.send(new pe.CopyObjectCommand(i));return this.emit("copyObject",n,i),n}catch(n){throw this.errorProxy(n,{from:t,to:r,command:i})}}async exists(t){try{return await this.headObject(t),!0}catch(r){if(r.name==="NoSuchKey")return!1;if(r.name==="NotFound")return!1;throw r}}async deleteObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t};try{const i=await this.sendCommand(new pe.DeleteObjectCommand(r));return this.emit("deleteObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async deleteObjects(t){const r=I.chunk(t,1e3),{results:i,errors:n}=await de.PromisePool.for(r).withConcurrency(this.parallelism).process(async o=>{const s={Bucket:this.config.bucket,Delete:{Objects:o.map(h=>({Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,h):h}))}};try{return await this.sendCommand(new pe.DeleteObjectsCommand(s))}catch(h){throw this.errorProxy(h,{key,command:s})}}),a={deleted:i,notFound:n};return this.emit("deleteObjects",a,t),a}async deleteAll({prefix:t}={}){const r=await this.getAllKeys({prefix:t}),i=await this.deleteObjects(r);return this.emit("deleteAll",{prefix:t,report:i}),i}async moveObject({from:t,to:r}){try{return await this.copyObject({from:t,to:r}),await this.deleteObject(t),!0}catch(i){throw this.errorProxy(i,{from:t,to:r,command:options})}}async listObjects({prefix:t,maxKeys:r=1e3,continuationToken:i}={}){const n={Bucket:this.config.bucket,MaxKeys:r,ContinuationToken:i,Prefix:this.config.keyPrefix?ae.join(this.config.keyPrefix,t||""):t||""};try{const a=await this.sendCommand(new pe.ListObjectsV2Command(n));return this.emit("listObjects",a,n),a}catch(a){throw this.errorProxy(a,{command:n})}}async count({prefix:t}={}){let r=0,i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);r+=o.KeyCount||0,i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.emit("count",r,{prefix:t}),r}async getAllKeys({prefix:t}={}){let r=[],i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);o.Contents&&(r=r.concat(o.Contents.map(s=>s.Key))),i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.config.keyPrefix&&(r=r.map(a=>a.replace(this.config.keyPrefix,"")).map(a=>a.startsWith("/")?a.replace("/",""):a)),this.emit("getAllKeys",r,{prefix:t}),r}async getContinuationTokenAfterOffset(t={}){const{prefix:r,offset:i=1e3}=t;if(i===0)return null;let n=!0,a,o=0;for(;n;){let s=i<1e3?i:i-o>1e3?1e3:i-o;const h={prefix:r,maxKeys:s,continuationToken:a},f=await this.listObjects(h);if(f.Contents&&(o+=f.Contents.length),n=f.IsTruncated||!1,a=f.NextContinuationToken,o>=i)break}return this.emit("getContinuationTokenAfterOffset",a,t),a}async getKeysPage(t={}){const{prefix:r,offset:i=0,amount:n=100}=t;let a=[],o=!0,s;for(i>0&&(s=await this.getContinuationTokenAfterOffset({prefix:r,offset:i}));o;){const h={prefix:r,continuationToken:s},f=await this.listObjects(h);if(f.Contents&&(a=a.concat(f.Contents.map(l=>l.Key))),o=f.IsTruncated||!1,s=f.NextContinuationToken,a.length>n){a=a.splice(0,n);break}}return this.config.keyPrefix&&(a=a.map(h=>h.replace(this.config.keyPrefix,"")).map(h=>h.startsWith("/")?h.replace("/",""):h)),this.emit("getKeysPage",a,t),a}async moveAllObjects({prefixFrom:t,prefixTo:r}){const i=await this.getAllKeys({prefix:t}),{results:n,errors:a}=await de.PromisePool.for(i).withConcurrency(this.parallelism).process(async o=>{const s=o.replace(t,r);try{return await this.moveObject({from:o,to:s}),s}catch(h){throw this.errorProxy(h,{from:o,to:s})}});if(this.emit("moveAllObjects",{results:n,errors:a},{prefixFrom:t,prefixTo:r}),a.length>0)throw console.log({errors:a}),new Error("Some objects could not be moved");return n}}async function At(){let e;if(process)try{const{webcrypto:t}=await import("crypto");e=t}catch{throw new Error("Crypto API not available")}else window&&(e=window.crypto);if(!e)throw new Error("Could not load any crypto library");return e}async function bi(e){const t=await At(),i=new TextEncoder().encode(e),n=await t.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(n)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function fr(e,t){const r=await At(),i=r.getRandomValues(new Uint8Array(16)),n=await xi(t,i),a=r.getRandomValues(new Uint8Array(12)),s=new TextEncoder().encode(e),h=await r.subtle.encrypt({name:"AES-GCM",iv:a},n,s),f=new Uint8Array(i.length+a.length+h.byteLength);return f.set(i),f.set(a,i.length),f.set(new Uint8Array(h),i.length+a.length),oo(f)}async function yi(e,t){const r=await At(),i=so(e),n=i.slice(0,16),a=i.slice(16,28),o=i.slice(28),s=await xi(t,n),h=await r.subtle.decrypt({name:"AES-GCM",iv:a},s,o);return new TextDecoder().decode(h)}async function xi(e,t){const r=await At(),n=new TextEncoder().encode(e),a=await r.subtle.importKey("raw",n,{name:"PBKDF2"},!1,["deriveKey"]);return await r.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},a,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}function oo(e){if(I.isObject(process))return Buffer.from(e).toString("base64");{const t=String.fromCharCode.apply(null,new Uint8Array(e));return window.btoa(t)}}function so(e){if(I.isObject(process))return new Uint8Array(Buffer.from(e,"base64"));{const t=window.atob(e),r=t.length,i=new Uint8Array(r);for(let n=0;n<r;n++)i[n]=t.charCodeAt(n);return i}}async function lr(e,t,r){if(!this.passphrase)return t.push({actual:e,type:"encryptionKeyMissing"}),e;try{return await fr(String(e),this.passphrase)}catch(i){t.push({actual:e,type:"encryptionProblem",error:i})}return e}class Ei extends ja{constructor({options:t,passphrase:r,autoEncrypt:i=!0}={}){super(I.merge({},{useNewCustomCheckerFunction:!0,messages:{encryptionKeyMissing:"Missing configuration for secrets encryption.",encryptionProblem:"Problem encrypting secret. Actual: {actual}. Error: {error}"},defaults:{string:{trim:!0},object:{strict:"remove"}}},t)),this.passphrase=r,this.autoEncrypt=i,this.alias("secret",{type:"string",custom:this.autoEncrypt?lr:void 0,messages:{string:"The '{field}' field must be a string.",stringMin:"This secret '{field}' field length must be at least {expected} long."}}),this.alias("secretAny",{type:"any",custom:this.autoEncrypt?lr:void 0}),this.alias("secretNumber",{type:"number",custom:this.autoEncrypt?lr:void 0})}}const ki=new Proxy(Ei,{instance:null,construct(e,t){return this.instance||(this.instance=new e(...t)),this.instance}}),fo={trim:e=>e.trim(),encrypt:(e,{passphrase:t})=>fr(e,t),decrypt:(e,{passphrase:t})=>yi(e,t),toString:e=>String(e),fromArray:(e,{separator:t})=>(e||[]).join(t),toArray:(e,{separator:t})=>(e||"").split(t),toJSON:e=>JSON.stringify(e),fromJSON:e=>JSON.parse(e),toNumber:e=>I.isString(e)?e.includes(".")?parseFloat(e):parseInt(e):e,toBool:e=>["true","1","yes"].includes(e),fromBool:e=>e?"1":"0"};class hr{constructor(t){const{map:r,name:i,attributes:n,passphrase:a,version:o=1,options:s={}}=t;if(this.name=i,this.version=o,this.attributes=n,this.passphrase=a??"secret",this.options=I.merge({},this.defaultOptions(),s),this.validator=new ki({autoEncrypt:!1}).compile(I.merge({$$async:!0},I.cloneDeep(this.attributes))),this.options.generateAutoHooks&&this.generateAutoHooks(),!I.isEmpty(r))this.map=r,this.reversedMap=I.invert(r);else{const h=St.flatten(this.attributes,{safe:!0});this.reversedMap={...Object.keys(h).filter(f=>!f.includes("$$"))},this.map=I.invert(this.reversedMap)}}defaultOptions(){return{autoEncrypt:!0,autoDecrypt:!0,arraySeparator:"|",generateAutoHooks:!0,hooks:{beforeMap:{},afterMap:{},beforeUnmap:{},afterUnmap:{}}}}addHook(t,r,i){this.options.hooks[t][r]||(this.options.hooks[t][r]=[]),this.options.hooks[t][r]=I.uniq([...this.options.hooks[t][r],i])}generateAutoHooks(){const t=St.flatten(I.cloneDeep(this.attributes),{safe:!0});for(const[r,i]of Object.entries(t))i.includes("array")?(this.addHook("beforeMap",r,"fromArray"),this.addHook("afterUnmap",r,"toArray")):(i.includes("secret")&&(this.options.autoEncrypt&&this.addHook("beforeMap",r,"encrypt"),this.options.autoDecrypt&&this.addHook("afterUnmap",r,"decrypt")),i.includes("number")&&(this.addHook("beforeMap",r,"toString"),this.addHook("afterUnmap",r,"toNumber")),i.includes("boolean")&&(this.addHook("beforeMap",r,"fromBool"),this.addHook("afterUnmap",r,"toBool")))}static import(t){let{map:r,name:i,options:n,version:a,attributes:o}=I.isString(t)?JSON.parse(t):t;return new hr({map:r,name:i,options:n,version:a,attributes:o})}export(){const t={version:this.version,name:this.name,options:this.options,attributes:I.cloneDeep(this.attributes),map:this.map};for(const[r,i]of Object.entries(this.attributes))t.attributes[r]=JSON.stringify(i);return t}async applyHooksActions(t,r){for(const[i,n]of Object.entries(this.options.hooks[r]))for(const a of n){const o=I.get(t,i);o&&I.set(t,i,await fo[a](o,{passphrase:this.passphrase,separator:this.options.arraySeparator}))}}async validate(t,{mutateOriginal:r=!1}={}){let i=r?t:I.cloneDeep(t);return await this.validator(i)}async mapper(t){const r=St.flatten(I.cloneDeep(t),{safe:!0});await this.applyHooksActions(r,"beforeMap");const i={_v:this.version+""};for(const[n,a]of Object.entries(r))i[this.map[n]]=a;return await this.applyHooksActions(i,"afterMap"),i}async unmapper(t){const r=I.cloneDeep(t);delete r._v,await this.applyHooksActions(r,"beforeUnmap");const i={};for(const[n,a]of Object.entries(r))i[this.reversedMap[n]]=a;return await this.applyHooksActions(i,"afterUnmap"),St.unflatten(i)}}class Si extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new ir.ReadableStream({highWaterMark:this.client.parallelism*3,start:this._start.bind(this),pull:this._pull.bind(this),cancel:this._cancel.bind(this)})}build(){return this.stream.getReader()}async _start(t){this.controller=t,this.continuationToken=null,this.closeNextIteration=!1}async _pull(t){if(this.closeNextIteration){t.close();return}const r=await this.client.listObjects({prefix:`resource=${this.resource.name}`,continuationToken:this.continuationToken}),i=r?.Contents.map(n=>n.Key).map(n=>n.replace(this.client.config.keyPrefix,"")).map(n=>n.startsWith("/")?n.replace("/",""):n).map(n=>n.replace(`resource=${this.resource.name}/id=`,""));this.continuationToken=r.NextContinuationToken,this.enqueue(i),r.IsTruncated||(this.closeNextIteration=!0)}enqueue(t){t.forEach(r=>{this.controller.enqueue(r),this.emit("id",r)})}_cancel(t){console.warn("Stream cancelled",t)}}class Ri extends Si{enqueue(t){this.controller.enqueue(t),this.emit("page",t)}}class Ai extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.input=new Ri({resource:this.resource}),this.output=new ir.TransformStream({transform:this._transform.bind(this)},{highWaterMark:this.client.parallelism*2},{highWaterMark:1}),this.stream=this.input.stream.pipeThrough(this.output)}build(){return this.stream.getReader()}async _transform(t,r){await de.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{const n=await this.resource.get(i);return r.enqueue(n),n})}}class Ti extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new ir.WritableStream({start:this._start.bind(this),write:this._write.bind(this),close:this._close.bind(this),abort:this._abort.bind(this)})}build(){return this.stream.getWriter()}async _start(t){this.controller=t}async _write(t,r){const i=this.resource;await de.PromisePool.for([].concat(t)).withConcurrency(this.client.parallelism).process(async n=>{await i.insert(n)})}async _close(t){}async _abort(t){console.error("Stream aborted:",t)}}function cr(e){return new Promise((t,r)=>{const i=[];e.on("data",n=>i.push(n)),e.on("error",r),e.on("end",()=>t(Buffer.concat(i).toString("utf-8")))})}class Li extends b{constructor({name:t,client:r,options:i={},attributes:n={},parallelism:a=10,passphrase:o="secret",observers:s=[]}){super(),this.name=t,this.client=r,this.options=i,this.observers=s,this.parallelism=a,this.passphrase=o??"secret",this.schema=new hr({name:t,attributes:n,passphrase:o})}export(){return this.schema.export()}async validate(t){const r={original:I.cloneDeep(t),isValid:!1,errors:[]},i=await this.schema.validate(t,{mutateOriginal:!0});return i===!0?r.isValid=!0:r.errors=i,r.data=t,r}async insert({id:t,...r}){const{errors:i,isValid:n,data:a}=await this.validate(r);if(!n)throw new Rt({bucket:this.client.config.bucket,resourceName:this.name,attributes:r,validation:i});!t&&t!==0&&(t=ii.nanoid());const o=await this.schema.mapper(a);await this.client.putObject({metadata:o,key:Y(`resource=${this.name}`,`id=${t}`)});const s=I.merge({id:t},a);return this.emit("insert",s),s}async get(t){const r=await this.client.headObject(Y(`resource=${this.name}`,`id=${t}`));let i=await this.schema.unmapper(r.Metadata);return i.id=t,i._length=r.ContentLength,i._createdAt=r.LastModified,r.Expiration&&(i._expiresAt=r.Expiration),this.emit("get",i),i}async exists(t){try{return await this.client.headObject(Y(`resource=${this.name}`,`id=${t}`)),!0}catch{return!1}}async update(t,r){const i=await this.get(t),n=I.merge(i,r);delete n.id;const{isValid:a,errors:o,data:s}=await this.validate(n);if(!a)throw new Rt({bucket:this.client.bucket,resourceName:this.name,attributes:r,validation:o});return await this.client.putObject({key:Y(`resource=${this.name}`,`id=${t}`),body:"",metadata:await this.schema.mapper(s)}),s.id=t,this.emit("update",r,s),s}async delete(t){const r=Y(`resource=${this.name}`,`id=${t}`),i=await this.client.deleteObject(r);return this.emit("delete",t),i}async count(){const t=await this.client.count({prefix:`resource=${this.name}`});return this.emit("count",t),t}async insertMany(t){const{results:r}=await de.PromisePool.for(t).withConcurrency(this.parallelism).handleError(async(i,n)=>{this.emit("error",i,n),this.observers.map(a=>a.emit("error",this.name,i,n))}).process(async i=>await this.insert(i));return this.emit("insertMany",t.length),r}async deleteMany(t){const r=I.chunk(t.map(n=>Y(`resource=${this.name}`,`id=${n}`)),1e3),{results:i}=await de.PromisePool.for(r).withConcurrency(this.parallelism).handleError(async(n,a)=>{this.emit("error",n,a),this.observers.map(o=>o.emit("error",this.name,n,a))}).process(async n=>{const a=await this.client.deleteObjects(n);return n.forEach(o=>{const s=o.split("=").pop();this.emit("deleted",s),this.observers.map(h=>h.emit("deleted",this.name,s))}),a});return this.emit("deleteMany",t.length),i}async deleteAll(){const t=await this.listIds();this.emit("deleteAll",t.length),await this.deleteMany(t)}async listIds(){const r=(await this.client.getAllKeys({prefix:`resource=${this.name}`})).map(i=>i.replace(`resource=${this.name}/id=`,""));return this.emit("listIds",r.length),r}async getMany(t){const{results:r}=await de.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{this.emit("id",i);const n=await this.get(i);return this.emit("data",n),n});return this.emit("getMany",t.length),r}async getAll(){let t=await this.listIds();if(t.length===0)return[];const{results:r}=await de.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>await this.get(i));return this.emit("getAll",r.length),r}async page({offset:t=0,size:r=100}){const n=(await this.client.getKeysPage({offset:t,amount:r,prefix:`resource=${this.name}`})).map(o=>o.replace(`resource=${this.name}/id=`,""));return await this.getMany(n)}readable(){return new Ai({resource:this}).build()}writable(){return new Ti({resource:this}).build()}}class Ci extends b{constructor(t){super(),this.version="1",this.resources={},this.options=t,this.verbose=t.verbose||!1,this.parallelism=parseInt(t.parallelism+"")||10,this.plugins=t.plugins||[],this.cache=t.cache,this.passphrase=t.passphrase||"secret",this.client=t.client||new mi({verbose:this.verbose,parallelism:this.parallelism,connectionString:t.connectionString}),this.bucket=this.client.bucket,this.keyPrefix=this.client.keyPrefix}async connect(){await this.startPlugins();let t=null;if(await this.client.exists("s3db.json")){const r=await this.client.getObject("s3db.json");t=JSON.parse(await cr(r?.Body)),t=this.unserializeMetadata(t)}else t=this.blankMetadataStructure(),await this.uploadMetadataFile();for(const r of Object.entries(t.resources)){const[i,n]=r;this.resources[i]=new Li({name:i,client:this.client,options:n.options,attributes:n.schema,parallelism:this.parallelism,passphrase:this.passphrase,observers:[this]})}this.emit("connected",new Date)}async startPlugins(){const t=this;if(!I.isEmpty(this.plugins)){const r=this.plugins.map(a=>I.isFunction(a)?new a(this):a),i=r.map(async a=>{a.beforeSetup&&await a.beforeSetup(),await a.setup(t),a.afterSetup&&await a.afterSetup()});await Promise.all(i);const n=r.map(async a=>{a.beforeStart&&await a.beforeStart(),await a.start(),a.afterStart&&await a.afterStart()});await Promise.all(n)}}unserializeMetadata(t){const r={...t};if(I.isEmpty(r.resources))return r;for(const[i,n]of Object.entries(r.resources))for(const[a,o]of Object.entries(n.attributes))r.resources[i].attributes[a]=JSON.parse(o);return r}async uploadMetadataFile(){const t={version:this.version,resources:Object.entries(this.resources).reduce((r,i)=>{const[n,a]=i;return r[n]=a.export(),r},{})};await this.client.putObject({key:"s3db.json",contentType:"application/json",body:JSON.stringify(t,null,2)})}blankMetadataStructure(){return{version:"1",resources:{}}}async createResource({name:t,attributes:r,options:i={}}){const n=new Li({name:t,attributes:r,observers:[this],client:this.client,options:{autoDecrypt:!0,cache:this.cache,...i}});return this.resources[t]=n,await this.uploadMetadataFile(),this.emit("s3db.resourceCreated",t),n}resource(t){return this.resources[t]?this.resources[t]:Promise.reject(`resource ${t} does not exist`)}}class lo extends Ci{}class ur extends b{async _set(t,r){}async _get(t){}async _del(t){}async _clear(t){}async set(t,r){return await this._set(t,r),this.emit("set",r),r}async get(t){const r=await this._get(t);return this.emit("get",r),r}async del(t){const r=await this._del(t);return this.emit("delete",r),r}async clear(){const t=await this._clear();return this.emit("clear",t),t}}class ho extends ur{constructor(){super(),this.cache={}}async _set(t,r){return this.cache[t]=r,r}async _get(t){return this.cache[t]}async _del(t){return delete this.cache[t],!0}async _clear(){return this.cache={},!0}}var qe=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{},te=[],G=[],co=typeof Uint8Array<"u"?Uint8Array:Array,dr=!1;function Oi(){dr=!0;for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=0,r=e.length;t<r;++t)te[t]=e[t],G[e.charCodeAt(t)]=t;G[45]=62,G[95]=63}function uo(e){dr||Oi();var t,r,i,n,a,o,s=e.length;if(s%4>0)throw new Error("Invalid string. Length must be a multiple of 4");a=e[s-2]==="="?2:e[s-1]==="="?1:0,o=new co(s*3/4-a),i=a>0?s-4:s;var h=0;for(t=0,r=0;t<i;t+=4,r+=3)n=G[e.charCodeAt(t)]<<18|G[e.charCodeAt(t+1)]<<12|G[e.charCodeAt(t+2)]<<6|G[e.charCodeAt(t+3)],o[h++]=n>>16&255,o[h++]=n>>8&255,o[h++]=n&255;return a===2?(n=G[e.charCodeAt(t)]<<2|G[e.charCodeAt(t+1)]>>4,o[h++]=n&255):a===1&&(n=G[e.charCodeAt(t)]<<10|G[e.charCodeAt(t+1)]<<4|G[e.charCodeAt(t+2)]>>2,o[h++]=n>>8&255,o[h++]=n&255),o}function po(e){return te[e>>18&63]+te[e>>12&63]+te[e>>6&63]+te[e&63]}function _o(e,t,r){for(var i,n=[],a=t;a<r;a+=3)i=(e[a]<<16)+(e[a+1]<<8)+e[a+2],n.push(po(i));return n.join("")}function Ii(e){dr||Oi();for(var t,r=e.length,i=r%3,n="",a=[],o=16383,s=0,h=r-i;s<h;s+=o)a.push(_o(e,s,s+o>h?h:s+o));return i===1?(t=e[r-1],n+=te[t>>2],n+=te[t<<4&63],n+="=="):i===2&&(t=(e[r-2]<<8)+e[r-1],n+=te[t>>10],n+=te[t>>4&63],n+=te[t<<2&63],n+="="),a.push(n),a.join("")}function Tt(e,t,r,i,n){var a,o,s=n*8-i-1,h=(1<<s)-1,f=h>>1,l=-7,d=r?n-1:0,p=r?-1:1,u=e[t+d];for(d+=p,a=u&(1<<-l)-1,u>>=-l,l+=s;l>0;a=a*256+e[t+d],d+=p,l-=8);for(o=a&(1<<-l)-1,a>>=-l,l+=i;l>0;o=o*256+e[t+d],d+=p,l-=8);if(a===0)a=1-f;else{if(a===h)return o?NaN:(u?-1:1)*(1/0);o=o+Math.pow(2,i),a=a-f}return(u?-1:1)*o*Math.pow(2,a-i)}function Mi(e,t,r,i,n,a){var o,s,h,f=a*8-n-1,l=(1<<f)-1,d=l>>1,p=n===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=i?0:a-1,g=i?1:-1,S=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=l):(o=Math.floor(Math.log(t)/Math.LN2),t*(h=Math.pow(2,-o))<1&&(o--,h*=2),o+d>=1?t+=p/h:t+=p*Math.pow(2,1-d),t*h>=2&&(o++,h/=2),o+d>=l?(s=0,o=l):o+d>=1?(s=(t*h-1)*Math.pow(2,n),o=o+d):(s=t*Math.pow(2,d-1)*Math.pow(2,n),o=0));n>=8;e[r+u]=s&255,u+=g,s/=256,n-=8);for(o=o<<n|s,f+=n;f>0;e[r+u]=o&255,u+=g,o/=256,f-=8);e[r+u-g]|=S*128}var wo={}.toString,Di=Array.isArray||function(e){return wo.call(e)=="[object Array]"};/*!
|
|
7
|
+
${JSON.stringify(n,null,2)}`}),this.resourceName=r,this.attributes=i,this.validation=n}}class pi extends Ye{}const _i={NotFound:ui,NoSuchKey:sr,UnknownError:pi,NoSuchBucket:ci,MissingMetadata:di,InvalidResourceItem:Rt},wi="us-east-1",gi="https://s3.us-east-1.amazonaws.com";class vi{constructor(t){let r;try{r=new URL(t)}catch{throw new Error("Invalid connection string: "+t)}this.region=wi,r.protocol==="s3:"?this.defineS3(r):this.defineMinio(r);for(const[i,n]of r.searchParams.entries())this[i]=n}defineS3(t){if(this.bucket=t.hostname,this.accessKeyId=t.username,this.secretAccessKey=t.password,this.endpoint=gi,["/","",null].includes(t.pathname))this.keyPrefix="";else{let[,...r]=t.pathname.split("/");this.keyPrefix=[...r||[]].join("/")}}defineMinio(t){if(this.forcePathStyle=!0,this.endpoint=t.origin,this.accessKeyId=t.username,this.secretAccessKey=t.password,["/","",null].includes(t.pathname))this.bucket="s3db",this.keyPrefix="";else{let[,r,...i]=t.pathname.split("/");this.bucket=r,this.keyPrefix=[...i||[]].join("/")}}}class mi extends b{constructor({verbose:t=!1,id:r=null,AwsS3Client:i,connectionString:n,parallelism:a=10}){super(),this.verbose=t,this.id=r??ii.nanoid(7),this.parallelism=a,this.config=new vi(n),this.client=i||this.createClient()}createClient(){let t={region:this.config.region,endpoint:this.config.endpoint};return this.config.forcePathStyle&&(t.forcePathStyle=!0),this.config.accessKeyId&&(t.credentials={accessKeyId:this.config.accessKeyId,secretAccessKey:this.config.secretAccessKey}),new pe.S3Client(t)}async sendCommand(t){this.emit("command.request",t.constructor.name,t.input);const r=console.warn;try{console.warn=n=>{n.includes("Stream of unknown length")||r(n)}}catch(n){console.error(n)}const i=await this.client.send(t);this.emit("command.response",t.constructor.name,i,t.input);try{console.warn=r}catch(n){console.error(n)}return i}errorProxy(t,r){this.verbose&&(r.bucket=this.config.bucket,r.config=this.config,r.verbose=this.verbose),t.data=r;const i=_i[t.name];return i?new i(r):t}async putObject({key:t,metadata:r,contentType:i,body:n,contentEncoding:a}){const o={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t,Metadata:{...r},Body:n||"",ContentType:i,ContentEncoding:a};try{const s=await this.sendCommand(new pe.PutObjectCommand(o));return this.emit("putObject",s,o),s}catch(s){throw this.errorProxy(s,{key:t,command:o})}}async getObject(t){const r={Bucket:this.config.bucket,Key:ae.join(this.config.keyPrefix,t)};try{const i=await this.sendCommand(new pe.GetObjectCommand(r));return this.emit("getObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async headObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t};try{const i=await this.client.send(new pe.HeadObjectCommand(r));return this.emit("headObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async copyObject({from:t,to:r}){const i={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,r):r,CopySource:ae.join(this.config.bucket,this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t)};try{const n=await this.client.send(new pe.CopyObjectCommand(i));return this.emit("copyObject",n,i),n}catch(n){throw this.errorProxy(n,{from:t,to:r,command:i})}}async exists(t){try{return await this.headObject(t),!0}catch(r){if(r.name==="NoSuchKey")return!1;if(r.name==="NotFound")return!1;throw r}}async deleteObject(t){const r={Bucket:this.config.bucket,Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,t):t};try{const i=await this.sendCommand(new pe.DeleteObjectCommand(r));return this.emit("deleteObject",i,r),i}catch(i){throw this.errorProxy(i,{key:t,command:r})}}async deleteObjects(t){const r=I.chunk(t,1e3),{results:i,errors:n}=await de.PromisePool.for(r).withConcurrency(this.parallelism).process(async o=>{const s={Bucket:this.config.bucket,Delete:{Objects:o.map(h=>({Key:this.config.keyPrefix?ae.join(this.config.keyPrefix,h):h}))}};try{return await this.sendCommand(new pe.DeleteObjectsCommand(s))}catch(h){throw this.errorProxy(h,{key,command:s})}}),a={deleted:i,notFound:n};return this.emit("deleteObjects",a,t),a}async deleteAll({prefix:t}={}){const r=await this.getAllKeys({prefix:t}),i=await this.deleteObjects(r);return this.emit("deleteAll",{prefix:t,report:i}),i}async moveObject({from:t,to:r}){try{return await this.copyObject({from:t,to:r}),await this.deleteObject(t),!0}catch(i){throw this.errorProxy(i,{from:t,to:r,command:options})}}async listObjects({prefix:t,maxKeys:r=1e3,continuationToken:i}={}){const n={Bucket:this.config.bucket,MaxKeys:r,ContinuationToken:i,Prefix:this.config.keyPrefix?ae.join(this.config.keyPrefix,t||""):t||""};try{const a=await this.sendCommand(new pe.ListObjectsV2Command(n));return this.emit("listObjects",a,n),a}catch(a){throw this.errorProxy(a,{command:n})}}async count({prefix:t}={}){let r=0,i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);r+=o.KeyCount||0,i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.emit("count",r,{prefix:t}),r}async getAllKeys({prefix:t}={}){let r=[],i=!0,n;for(;i;){const a={prefix:t,continuationToken:n},o=await this.listObjects(a);o.Contents&&(r=r.concat(o.Contents.map(s=>s.Key))),i=o.IsTruncated||!1,n=o.NextContinuationToken}return this.config.keyPrefix&&(r=r.map(a=>a.replace(this.config.keyPrefix,"")).map(a=>a.startsWith("/")?a.replace("/",""):a)),this.emit("getAllKeys",r,{prefix:t}),r}async getContinuationTokenAfterOffset(t={}){const{prefix:r,offset:i=1e3}=t;if(i===0)return null;let n=!0,a,o=0;for(;n;){let s=i<1e3?i:i-o>1e3?1e3:i-o;const h={prefix:r,maxKeys:s,continuationToken:a},f=await this.listObjects(h);if(f.Contents&&(o+=f.Contents.length),n=f.IsTruncated||!1,a=f.NextContinuationToken,o>=i)break}return this.emit("getContinuationTokenAfterOffset",a,t),a}async getKeysPage(t={}){const{prefix:r,offset:i=0,amount:n=100}=t;let a=[],o=!0,s;for(i>0&&(s=await this.getContinuationTokenAfterOffset({prefix:r,offset:i}));o;){const h={prefix:r,continuationToken:s},f=await this.listObjects(h);if(f.Contents&&(a=a.concat(f.Contents.map(l=>l.Key))),o=f.IsTruncated||!1,s=f.NextContinuationToken,a.length>n){a=a.splice(0,n);break}}return this.config.keyPrefix&&(a=a.map(h=>h.replace(this.config.keyPrefix,"")).map(h=>h.startsWith("/")?h.replace("/",""):h)),this.emit("getKeysPage",a,t),a}async moveAllObjects({prefixFrom:t,prefixTo:r}){const i=await this.getAllKeys({prefix:t}),{results:n,errors:a}=await de.PromisePool.for(i).withConcurrency(this.parallelism).process(async o=>{const s=o.replace(t,r);try{return await this.moveObject({from:o,to:s}),s}catch(h){throw this.errorProxy(h,{from:o,to:s})}});if(this.emit("moveAllObjects",{results:n,errors:a},{prefixFrom:t,prefixTo:r}),a.length>0)throw console.log({errors:a}),new Error("Some objects could not be moved");return n}}async function At(){let e;if(process)try{const{webcrypto:t}=await import("crypto");e=t}catch{throw new Error("Crypto API not available")}else window&&(e=window.crypto);if(!e)throw new Error("Could not load any crypto library");return e}async function bi(e){const t=await At(),i=new TextEncoder().encode(e),n=await t.subtle.digest("SHA-256",i);return Array.from(new Uint8Array(n)).map(s=>s.toString(16).padStart(2,"0")).join("")}async function fr(e,t){const r=await At(),i=r.getRandomValues(new Uint8Array(16)),n=await xi(t,i),a=r.getRandomValues(new Uint8Array(12)),s=new TextEncoder().encode(e),h=await r.subtle.encrypt({name:"AES-GCM",iv:a},n,s),f=new Uint8Array(i.length+a.length+h.byteLength);return f.set(i),f.set(a,i.length),f.set(new Uint8Array(h),i.length+a.length),oo(f)}async function yi(e,t){const r=await At(),i=so(e),n=i.slice(0,16),a=i.slice(16,28),o=i.slice(28),s=await xi(t,n),h=await r.subtle.decrypt({name:"AES-GCM",iv:a},s,o);return new TextDecoder().decode(h)}async function xi(e,t){const r=await At(),n=new TextEncoder().encode(e),a=await r.subtle.importKey("raw",n,{name:"PBKDF2"},!1,["deriveKey"]);return await r.subtle.deriveKey({name:"PBKDF2",salt:t,iterations:1e5,hash:"SHA-256"},a,{name:"AES-GCM",length:256},!0,["encrypt","decrypt"])}function oo(e){if(I.isObject(process))return Buffer.from(e).toString("base64");{const t=String.fromCharCode.apply(null,new Uint8Array(e));return window.btoa(t)}}function so(e){if(I.isObject(process))return new Uint8Array(Buffer.from(e,"base64"));{const t=window.atob(e),r=t.length,i=new Uint8Array(r);for(let n=0;n<r;n++)i[n]=t.charCodeAt(n);return i}}async function lr(e,t,r){if(!this.passphrase)return t.push({actual:e,type:"encryptionKeyMissing"}),e;try{return await fr(String(e),this.passphrase)}catch(i){t.push({actual:e,type:"encryptionProblem",error:i})}return e}class Ei extends ja{constructor({options:t,passphrase:r,autoEncrypt:i=!0}={}){super(I.merge({},{useNewCustomCheckerFunction:!0,messages:{encryptionKeyMissing:"Missing configuration for secrets encryption.",encryptionProblem:"Problem encrypting secret. Actual: {actual}. Error: {error}"},defaults:{string:{trim:!0},object:{strict:"remove"}}},t)),this.passphrase=r,this.autoEncrypt=i,this.alias("secret",{type:"string",custom:this.autoEncrypt?lr:void 0,messages:{string:"The '{field}' field must be a string.",stringMin:"This secret '{field}' field length must be at least {expected} long."}}),this.alias("secretAny",{type:"any",custom:this.autoEncrypt?lr:void 0}),this.alias("secretNumber",{type:"number",custom:this.autoEncrypt?lr:void 0})}}const ki=new Proxy(Ei,{instance:null,construct(e,t){return this.instance||(this.instance=new e(...t)),this.instance}}),fo={trim:e=>e.trim(),encrypt:(e,{passphrase:t})=>fr(e,t),decrypt:(e,{passphrase:t})=>yi(e,t),toString:e=>String(e),fromArray:(e,{separator:t})=>(e||[]).join(t),toArray:(e,{separator:t})=>(e||"").split(t),toJSON:e=>JSON.stringify(e),fromJSON:e=>JSON.parse(e),toNumber:e=>I.isString(e)?e.includes(".")?parseFloat(e):parseInt(e):e,toBool:e=>["1","true","yes",!0,"y"].includes(e),fromBool:e=>e?"1":"0"};class hr{constructor(t){const{map:r,name:i,attributes:n,passphrase:a,version:o=1,options:s={}}=t;if(this.name=i,this.version=o,this.attributes=n,this.passphrase=a??"secret",this.options=I.merge({},this.defaultOptions(),s),this.validator=new ki({autoEncrypt:!1}).compile(I.merge({$$async:!0},I.cloneDeep(this.attributes))),this.options.generateAutoHooks&&this.generateAutoHooks(),!I.isEmpty(r))this.map=r,this.reversedMap=I.invert(r);else{const h=St.flatten(this.attributes,{safe:!0});this.reversedMap={...Object.keys(h).filter(f=>!f.includes("$$"))},this.map=I.invert(this.reversedMap)}}defaultOptions(){return{autoEncrypt:!0,autoDecrypt:!0,arraySeparator:"|",generateAutoHooks:!0,hooks:{beforeMap:{},afterMap:{},beforeUnmap:{},afterUnmap:{}}}}addHook(t,r,i){this.options.hooks[t][r]||(this.options.hooks[t][r]=[]),this.options.hooks[t][r]=I.uniq([...this.options.hooks[t][r],i])}generateAutoHooks(){const t=St.flatten(I.cloneDeep(this.attributes),{safe:!0});for(const[r,i]of Object.entries(t))i.includes("array")?(this.addHook("beforeMap",r,"fromArray"),this.addHook("afterUnmap",r,"toArray")):(i.includes("secret")&&(this.options.autoEncrypt&&this.addHook("beforeMap",r,"encrypt"),this.options.autoDecrypt&&this.addHook("afterUnmap",r,"decrypt")),i.includes("number")&&(this.addHook("beforeMap",r,"toString"),this.addHook("afterUnmap",r,"toNumber")),i.includes("boolean")&&(this.addHook("beforeMap",r,"fromBool"),this.addHook("afterUnmap",r,"toBool")))}static import(t){let{map:r,name:i,options:n,version:a,attributes:o}=I.isString(t)?JSON.parse(t):t;return new hr({map:r,name:i,options:n,version:a,attributes:o})}export(){const t={version:this.version,name:this.name,options:this.options,attributes:I.cloneDeep(this.attributes),map:this.map};for(const[r,i]of Object.entries(this.attributes))t.attributes[r]=JSON.stringify(i);return t}async applyHooksActions(t,r){for(const[i,n]of Object.entries(this.options.hooks[r]))for(const a of n){const o=I.get(t,i);o!==void 0&&I.set(t,i,await fo[a](o,{passphrase:this.passphrase,separator:this.options.arraySeparator}))}}async validate(t,{mutateOriginal:r=!1}={}){let i=r?t:I.cloneDeep(t);return await this.validator(i)}async mapper(t){const r=St.flatten(I.cloneDeep(t),{safe:!0});await this.applyHooksActions(r,"beforeMap");const i={_v:this.version+""};for(const[n,a]of Object.entries(r))i[this.map[n]]=a;return await this.applyHooksActions(i,"afterMap"),i}async unmapper(t){const r=I.cloneDeep(t);delete r._v,await this.applyHooksActions(r,"beforeUnmap");const i={};for(const[n,a]of Object.entries(r))i[this.reversedMap[n]]=a;return await this.applyHooksActions(i,"afterUnmap"),St.unflatten(i)}}class Si extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new ir.ReadableStream({highWaterMark:this.client.parallelism*3,start:this._start.bind(this),pull:this._pull.bind(this),cancel:this._cancel.bind(this)})}build(){return this.stream.getReader()}async _start(t){this.controller=t,this.continuationToken=null,this.closeNextIteration=!1}async _pull(t){if(this.closeNextIteration){t.close();return}const r=await this.client.listObjects({prefix:`resource=${this.resource.name}`,continuationToken:this.continuationToken}),i=r?.Contents.map(n=>n.Key).map(n=>n.replace(this.client.config.keyPrefix,"")).map(n=>n.startsWith("/")?n.replace("/",""):n).map(n=>n.replace(`resource=${this.resource.name}/id=`,""));this.continuationToken=r.NextContinuationToken,this.enqueue(i),r.IsTruncated||(this.closeNextIteration=!0)}enqueue(t){t.forEach(r=>{this.controller.enqueue(r),this.emit("id",r)})}_cancel(t){console.warn("Stream cancelled",t)}}class Ri extends Si{enqueue(t){this.controller.enqueue(t),this.emit("page",t)}}class Ai extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.input=new Ri({resource:this.resource}),this.output=new ir.TransformStream({transform:this._transform.bind(this)},{highWaterMark:this.client.parallelism*2},{highWaterMark:1}),this.stream=this.input.stream.pipeThrough(this.output)}build(){return this.stream.getReader()}async _transform(t,r){await de.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{const n=await this.resource.get(i);return r.enqueue(n),n})}}class Ti extends b{constructor({resource:t}){super(),this.resource=t,this.client=t.client,this.stream=new ir.WritableStream({start:this._start.bind(this),write:this._write.bind(this),close:this._close.bind(this),abort:this._abort.bind(this)})}build(){return this.stream.getWriter()}async _start(t){this.controller=t}async _write(t,r){const i=this.resource;await de.PromisePool.for([].concat(t)).withConcurrency(this.client.parallelism).process(async n=>{await i.insert(n)})}async _close(t){}async _abort(t){console.error("Stream aborted:",t)}}function cr(e){return new Promise((t,r)=>{const i=[];e.on("data",n=>i.push(n)),e.on("error",r),e.on("end",()=>t(Buffer.concat(i).toString("utf-8")))})}class Li extends b{constructor({name:t,client:r,options:i={},attributes:n={},parallelism:a=10,passphrase:o="secret",observers:s=[]}){super(),this.name=t,this.client=r,this.options=i,this.observers=s,this.parallelism=a,this.passphrase=o??"secret",this.schema=new hr({name:t,attributes:n,passphrase:o})}export(){return this.schema.export()}async validate(t){const r={original:I.cloneDeep(t),isValid:!1,errors:[]},i=await this.schema.validate(t,{mutateOriginal:!0});return i===!0?r.isValid=!0:r.errors=i,r.data=t,r}async insert({id:t,...r}){const{errors:i,isValid:n,data:a}=await this.validate(r);if(!n)throw new Rt({bucket:this.client.config.bucket,resourceName:this.name,attributes:r,validation:i});!t&&t!==0&&(t=ii.nanoid());const o=await this.schema.mapper(a);await this.client.putObject({metadata:o,key:Y(`resource=${this.name}`,`id=${t}`)});const s=I.merge({id:t},a);return this.emit("insert",s),s}async get(t){const r=await this.client.headObject(Y(`resource=${this.name}`,`id=${t}`));let i=await this.schema.unmapper(r.Metadata);return i.id=t,i._length=r.ContentLength,i._createdAt=r.LastModified,r.Expiration&&(i._expiresAt=r.Expiration),this.emit("get",i),i}async exists(t){try{return await this.client.headObject(Y(`resource=${this.name}`,`id=${t}`)),!0}catch{return!1}}async update(t,r){const i=await this.get(t),n=I.merge(i,r);delete n.id;const{isValid:a,errors:o,data:s}=await this.validate(n);if(!a)throw new Rt({bucket:this.client.bucket,resourceName:this.name,attributes:r,validation:o});return await this.client.putObject({key:Y(`resource=${this.name}`,`id=${t}`),body:"",metadata:await this.schema.mapper(s)}),s.id=t,this.emit("update",r,s),s}async delete(t){const r=Y(`resource=${this.name}`,`id=${t}`),i=await this.client.deleteObject(r);return this.emit("delete",t),i}async count(){const t=await this.client.count({prefix:`resource=${this.name}`});return this.emit("count",t),t}async insertMany(t){const{results:r}=await de.PromisePool.for(t).withConcurrency(this.parallelism).handleError(async(i,n)=>{this.emit("error",i,n),this.observers.map(a=>a.emit("error",this.name,i,n))}).process(async i=>await this.insert(i));return this.emit("insertMany",t.length),r}async deleteMany(t){const r=I.chunk(t.map(n=>Y(`resource=${this.name}`,`id=${n}`)),1e3),{results:i}=await de.PromisePool.for(r).withConcurrency(this.parallelism).handleError(async(n,a)=>{this.emit("error",n,a),this.observers.map(o=>o.emit("error",this.name,n,a))}).process(async n=>{const a=await this.client.deleteObjects(n);return n.forEach(o=>{const s=o.split("=").pop();this.emit("deleted",s),this.observers.map(h=>h.emit("deleted",this.name,s))}),a});return this.emit("deleteMany",t.length),i}async deleteAll(){const t=await this.listIds();this.emit("deleteAll",t.length),await this.deleteMany(t)}async listIds(){const r=(await this.client.getAllKeys({prefix:`resource=${this.name}`})).map(i=>i.replace(`resource=${this.name}/id=`,""));return this.emit("listIds",r.length),r}async getMany(t){const{results:r}=await de.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>{this.emit("id",i);const n=await this.get(i);return this.emit("data",n),n});return this.emit("getMany",t.length),r}async getAll(){let t=await this.listIds();if(t.length===0)return[];const{results:r}=await de.PromisePool.for(t).withConcurrency(this.client.parallelism).process(async i=>await this.get(i));return this.emit("getAll",r.length),r}async page({offset:t=0,size:r=100}){const n=(await this.client.getKeysPage({offset:t,amount:r,prefix:`resource=${this.name}`})).map(o=>o.replace(`resource=${this.name}/id=`,""));return await this.getMany(n)}readable(){return new Ai({resource:this}).build()}writable(){return new Ti({resource:this}).build()}}class Ci extends b{constructor(t){super(),this.version="1",this.resources={},this.options=t,this.verbose=t.verbose||!1,this.parallelism=parseInt(t.parallelism+"")||10,this.plugins=t.plugins||[],this.cache=t.cache,this.passphrase=t.passphrase||"secret",this.client=t.client||new mi({verbose:this.verbose,parallelism:this.parallelism,connectionString:t.connectionString}),this.bucket=this.client.bucket,this.keyPrefix=this.client.keyPrefix}async connect(){await this.startPlugins();let t=null;if(await this.client.exists("s3db.json")){const r=await this.client.getObject("s3db.json");t=JSON.parse(await cr(r?.Body)),t=this.unserializeMetadata(t)}else t=this.blankMetadataStructure(),await this.uploadMetadataFile();for(const r of Object.entries(t.resources)){const[i,n]=r;this.resources[i]=new Li({name:i,client:this.client,options:n.options,attributes:n.schema,parallelism:this.parallelism,passphrase:this.passphrase,observers:[this]})}this.emit("connected",new Date)}async startPlugins(){const t=this;if(!I.isEmpty(this.plugins)){const r=this.plugins.map(a=>I.isFunction(a)?new a(this):a),i=r.map(async a=>{a.beforeSetup&&await a.beforeSetup(),await a.setup(t),a.afterSetup&&await a.afterSetup()});await Promise.all(i);const n=r.map(async a=>{a.beforeStart&&await a.beforeStart(),await a.start(),a.afterStart&&await a.afterStart()});await Promise.all(n)}}unserializeMetadata(t){const r={...t};if(I.isEmpty(r.resources))return r;for(const[i,n]of Object.entries(r.resources))for(const[a,o]of Object.entries(n.attributes))r.resources[i].attributes[a]=JSON.parse(o);return r}async uploadMetadataFile(){const t={version:this.version,resources:Object.entries(this.resources).reduce((r,i)=>{const[n,a]=i;return r[n]=a.export(),r},{})};await this.client.putObject({key:"s3db.json",contentType:"application/json",body:JSON.stringify(t,null,2)})}blankMetadataStructure(){return{version:"1",resources:{}}}async createResource({name:t,attributes:r,options:i={}}){const n=new Li({name:t,attributes:r,observers:[this],client:this.client,options:{autoDecrypt:!0,cache:this.cache,...i}});return this.resources[t]=n,await this.uploadMetadataFile(),this.emit("s3db.resourceCreated",t),n}resource(t){return this.resources[t]?this.resources[t]:Promise.reject(`resource ${t} does not exist`)}}class lo extends Ci{}class ur extends b{async _set(t,r){}async _get(t){}async _del(t){}async _clear(t){}async set(t,r){return await this._set(t,r),this.emit("set",r),r}async get(t){const r=await this._get(t);return this.emit("get",r),r}async del(t){const r=await this._del(t);return this.emit("delete",r),r}async clear(){const t=await this._clear();return this.emit("clear",t),t}}class ho extends ur{constructor(){super(),this.cache={}}async _set(t,r){return this.cache[t]=r,r}async _get(t){return this.cache[t]}async _del(t){return delete this.cache[t],!0}async _clear(){return this.cache={},!0}}var qe=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{},te=[],G=[],co=typeof Uint8Array<"u"?Uint8Array:Array,dr=!1;function Oi(){dr=!0;for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=0,r=e.length;t<r;++t)te[t]=e[t],G[e.charCodeAt(t)]=t;G[45]=62,G[95]=63}function uo(e){dr||Oi();var t,r,i,n,a,o,s=e.length;if(s%4>0)throw new Error("Invalid string. Length must be a multiple of 4");a=e[s-2]==="="?2:e[s-1]==="="?1:0,o=new co(s*3/4-a),i=a>0?s-4:s;var h=0;for(t=0,r=0;t<i;t+=4,r+=3)n=G[e.charCodeAt(t)]<<18|G[e.charCodeAt(t+1)]<<12|G[e.charCodeAt(t+2)]<<6|G[e.charCodeAt(t+3)],o[h++]=n>>16&255,o[h++]=n>>8&255,o[h++]=n&255;return a===2?(n=G[e.charCodeAt(t)]<<2|G[e.charCodeAt(t+1)]>>4,o[h++]=n&255):a===1&&(n=G[e.charCodeAt(t)]<<10|G[e.charCodeAt(t+1)]<<4|G[e.charCodeAt(t+2)]>>2,o[h++]=n>>8&255,o[h++]=n&255),o}function po(e){return te[e>>18&63]+te[e>>12&63]+te[e>>6&63]+te[e&63]}function _o(e,t,r){for(var i,n=[],a=t;a<r;a+=3)i=(e[a]<<16)+(e[a+1]<<8)+e[a+2],n.push(po(i));return n.join("")}function Ii(e){dr||Oi();for(var t,r=e.length,i=r%3,n="",a=[],o=16383,s=0,h=r-i;s<h;s+=o)a.push(_o(e,s,s+o>h?h:s+o));return i===1?(t=e[r-1],n+=te[t>>2],n+=te[t<<4&63],n+="=="):i===2&&(t=(e[r-2]<<8)+e[r-1],n+=te[t>>10],n+=te[t>>4&63],n+=te[t<<2&63],n+="="),a.push(n),a.join("")}function Tt(e,t,r,i,n){var a,o,s=n*8-i-1,h=(1<<s)-1,f=h>>1,l=-7,d=r?n-1:0,p=r?-1:1,u=e[t+d];for(d+=p,a=u&(1<<-l)-1,u>>=-l,l+=s;l>0;a=a*256+e[t+d],d+=p,l-=8);for(o=a&(1<<-l)-1,a>>=-l,l+=i;l>0;o=o*256+e[t+d],d+=p,l-=8);if(a===0)a=1-f;else{if(a===h)return o?NaN:(u?-1:1)*(1/0);o=o+Math.pow(2,i),a=a-f}return(u?-1:1)*o*Math.pow(2,a-i)}function Mi(e,t,r,i,n,a){var o,s,h,f=a*8-n-1,l=(1<<f)-1,d=l>>1,p=n===23?Math.pow(2,-24)-Math.pow(2,-77):0,u=i?0:a-1,g=i?1:-1,S=t<0||t===0&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,o=l):(o=Math.floor(Math.log(t)/Math.LN2),t*(h=Math.pow(2,-o))<1&&(o--,h*=2),o+d>=1?t+=p/h:t+=p*Math.pow(2,1-d),t*h>=2&&(o++,h/=2),o+d>=l?(s=0,o=l):o+d>=1?(s=(t*h-1)*Math.pow(2,n),o=o+d):(s=t*Math.pow(2,d-1)*Math.pow(2,n),o=0));n>=8;e[r+u]=s&255,u+=g,s/=256,n-=8);for(o=o<<n|s,f+=n;f>0;e[r+u]=o&255,u+=g,o/=256,f-=8);e[r+u-g]|=S*128}var wo={}.toString,Di=Array.isArray||function(e){return wo.call(e)=="[object Array]"};/*!
|
|
8
8
|
* The buffer module from node.js, for the browser.
|
|
9
9
|
*
|
|
10
10
|
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|