@junobuild/storage 0.0.1-next-2024-06-26

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 David Dal Busco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ [![npm][npm-badge]][npm-badge-url]
2
+ [![license][npm-license]][npm-license-url]
3
+
4
+ [npm-badge]: https://img.shields.io/npm/v/@junobuild/utils
5
+ [npm-badge-url]: https://www.npmjs.com/package/@junobuild/utils
6
+ [npm-license]: https://img.shields.io/npm/l/@junobuild/utils
7
+ [npm-license-url]: https://github.com/junobuild/juno-js/blob/main/LICENSE
8
+
9
+ # Juno JavaScript utils
10
+
11
+ A collection of utilities and constants for Juno JS libs.
12
+
13
+ ## License
14
+
15
+ MIT © [David Dal Busco](mailto:david.dalbusco@outlook.com)
16
+
17
+ [juno]: https://juno.build
@@ -0,0 +1,2 @@
1
+ import{Principal as R}from"@dfinity/principal";var U=t=>t==null,k=t=>!U(t);var n=t=>k(t)?[t]:[];var _=()=>typeof window<"u";var B=async({asset:{data:t,filename:s,collection:o,headers:a,token:l,fullPath:i,encoding:b,description:A},actor:p,init_asset_upload:C})=>{let{batch_id:c}=await C({collection:o,full_path:i,name:s,token:n(l),encoding_type:n(b),description:n(A)}),u=19e5,d=[],y=_()?new Blob([await t.arrayBuffer()]):t,f=0n;for(let e=0;e<y.size;e+=u){let m=y.slice(e,e+u);d.push({batchId:c,chunk:m,actor:p,orderId:f}),f++}let r=[];for await(let e of I({uploadChunks:d}))r=[...r,...e];let h=a.find(([e,m])=>e.toLowerCase()==="content-type")===void 0&&t.type!==void 0&&t.type!==""?[["Content-Type",t.type]]:void 0;await p.commit_asset_upload({batch_id:c,chunk_ids:r.map(({chunk_id:e})=>e),headers:[...a,...h||[]]})};async function*I({uploadChunks:t,limit:s=12}){for(let o=0;o<t.length;o=o+s){let a=t.slice(o,o+s);yield await Promise.all(a.map(i=>w(i)))}}var w=async({batchId:t,chunk:s,actor:o,orderId:a})=>o.upload_asset_chunk({batch_id:t,content:new Uint8Array(await s.arrayBuffer()),order_id:n(a)});export{B as uploadAsset};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../utils/src/utils/debounce.utils.ts", "../../../utils/src/utils/json.utils.ts", "../../../utils/src/utils/null.utils.ts", "../../../utils/src/utils/did.utils.ts", "../../../utils/src/utils/env.utils.ts", "../../src/api/storage.api.ts"],
4
+ "sourcesContent": ["/**\n * Creates a debounced function that delays invoking the provided function until after the specified timeout.\n * @param {Function} func - The function to debounce.\n * @param {number} [timeout=300] - The number of milliseconds to delay. Defaults to 300ms if not specified or invalid.\n * @returns {Function} A debounced function.\n */\n/* eslint-disable-next-line @typescript-eslint/ban-types */\nexport const debounce = (func: Function, timeout?: number): Function => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n return (...args: unknown[]) => {\n const next = () => func(...args);\n\n if (timer) {\n clearTimeout(timer);\n }\n\n timer = setTimeout(next, timeout !== undefined && timeout > 0 ? timeout : 300);\n };\n};\n", "import {Principal} from '@dfinity/principal';\nimport {nonNullish} from './null.utils';\n\nconst JSON_KEY_BIGINT = '__bigint__';\nconst JSON_KEY_PRINCIPAL = '__principal__';\nconst JSON_KEY_UINT8ARRAY = '__uint8array__';\n\n/**\n * A function that alters the behavior of the stringification process for BigInt, Principal, and Uint8Array.\n * @param {string} _key - The key of the value being stringified.\n * @param {unknown} value - The value being stringified.\n * @returns {unknown} The altered value for stringification.\n */\nexport const jsonReplacer = (_key: string, value: unknown): unknown => {\n if (typeof value === 'bigint') {\n return {[JSON_KEY_BIGINT]: `${value}`};\n }\n\n if (nonNullish(value) && value instanceof Principal) {\n return {[JSON_KEY_PRINCIPAL]: value.toText()};\n }\n\n if (nonNullish(value) && value instanceof Uint8Array) {\n return {[JSON_KEY_UINT8ARRAY]: Array.from(value)};\n }\n\n return value;\n};\n\n/**\n * A parser that interprets revived BigInt, Principal, and Uint8Array when constructing JavaScript values or objects.\n * @param {string} _key - The key of the value being parsed.\n * @param {unknown} value - The value being parsed.\n * @returns {unknown} The parsed value.\n */\nexport const jsonReviver = (_key: string, value: unknown): unknown => {\n const mapValue = <T>(key: string): T => (value as Record<string, T>)[key];\n\n if (nonNullish(value) && typeof value === 'object' && JSON_KEY_BIGINT in value) {\n return BigInt(mapValue(JSON_KEY_BIGINT));\n }\n\n if (nonNullish(value) && typeof value === 'object' && JSON_KEY_PRINCIPAL in value) {\n return Principal.fromText(mapValue(JSON_KEY_PRINCIPAL));\n }\n\n if (nonNullish(value) && typeof value === 'object' && JSON_KEY_UINT8ARRAY in value) {\n return Uint8Array.from(mapValue(JSON_KEY_UINT8ARRAY));\n }\n\n return value;\n};\n", "/**\n * Checks if the provided argument is null or undefined.\n * @template T\n * @param {T | undefined | null} argument - The argument to check.\n * @returns {boolean} True if the argument is null or undefined, false otherwise.\n */\nexport const isNullish = <T>(argument: T | undefined | null): argument is undefined | null =>\n argument === null || argument === undefined;\n\n/**\n * Checks if the provided argument is neither null nor undefined.\n * @template T\n * @param {T | undefined | null} argument - The argument to check.\n * @returns {boolean} True if the argument is neither null nor undefined, false otherwise.\n */\nexport const nonNullish = <T>(argument: T | undefined | null): argument is NonNullable<T> =>\n !isNullish(argument);\n\n/**\n * Represents an error thrown when a value is null or undefined.\n * @class\n * @extends {Error}\n */\nexport class NullishError extends Error {}\n\n/**\n * Asserts that a value is neither null nor undefined.\n * @template T\n * @param {T} value - The value to check.\n * @param {string} [message] - The optional error message to use if the assertion fails.\n * @throws {NullishError} If the value is null or undefined.\n * @returns {asserts value is NonNullable<T>} Asserts that the value is neither null nor undefined.\n */\nexport const assertNonNullish: <T>(\n value: T,\n message?: string\n) => asserts value is NonNullable<T> = <T>(value: T, message?: string): void => {\n if (isNullish(value)) {\n throw new NullishError(message);\n }\n};\n", "import {jsonReplacer, jsonReviver} from './json.utils';\nimport {nonNullish} from './null.utils';\n\n/**\n * Converts a value to a nullable array.\n * @template T\n * @param {T} [value] - The value to convert.\n * @returns {([] | [T])} A nullable array containing the value if non-nullish, or an empty array if nullish.\n */\nexport const toNullable = <T>(value?: T): [] | [T] => {\n return nonNullish(value) ? [value] : [];\n};\n\n/**\n * Extracts a value from a nullable array.\n * @template T\n * @param {([] | [T])} value - The nullable array.\n * @returns {(T | undefined)} The value if present, or undefined if the array is empty.\n */\nexport const fromNullable = <T>(value: [] | [T]): T | undefined => {\n return value?.[0];\n};\n\n/**\n * Converts data to a Uint8Array for transmission or storage.\n * @template T\n * @param {T} data - The data to convert.\n * @returns {Promise<Uint8Array>} A promise that resolves to a Uint8Array representation of the data.\n */\nexport const toArray = async <T>(data: T): Promise<Uint8Array> => {\n const blob: Blob = new Blob([JSON.stringify(data, jsonReplacer)], {\n type: 'application/json; charset=utf-8'\n });\n return new Uint8Array(await blob.arrayBuffer());\n};\n\n/**\n * Converts a Uint8Array or number array back to the original data type.\n * @template T\n * @param {(Uint8Array | number[])} data - The array to convert.\n * @returns {Promise<T>} A promise that resolves to the original data.\n */\nexport const fromArray = async <T>(data: Uint8Array | number[]): Promise<T> => {\n const blob: Blob = new Blob([data instanceof Uint8Array ? data : new Uint8Array(data)], {\n type: 'application/json; charset=utf-8'\n });\n return JSON.parse(await blob.text(), jsonReviver);\n};\n", "/**\n * Checks if the current environment is a browser.\n * @returns {boolean} True if the current environment is a browser, false otherwise.\n */\nexport const isBrowser = (): boolean => typeof window !== `undefined`;\n", "import {isBrowser, toNullable} from '@junobuild/utils';\nimport type {\n _SERVICE as ConsoleActor,\n InitAssetKey as ConsoleInitAssetKey,\n InitUploadResult as ConsoleInitUploadResult\n} from '../../declarations/console/console.did';\nimport type {\n _SERVICE as SatelliteActor,\n InitAssetKey as SatelliteInitAssetKey,\n InitUploadResult as SatelliteInitUploadResult\n} from '../../declarations/satellite/satellite.did';\nimport type {ENCODING_TYPE, Storage} from '../types/storage.types';\n\nexport type UploadAsset = Required<Omit<Storage, 'token' | 'encoding' | 'description'>> &\n Pick<Storage, 'token' | 'encoding' | 'description'>;\n\nexport const uploadAsset = async ({\n asset: {data, filename, collection, headers, token, fullPath, encoding, description},\n actor,\n init_asset_upload\n}: {\n asset: UploadAsset;\n actor: SatelliteActor | ConsoleActor;\n init_asset_upload: (\n initAssetKey: SatelliteInitAssetKey | ConsoleInitAssetKey\n ) => Promise<SatelliteInitUploadResult | ConsoleInitUploadResult>;\n}): Promise<void> => {\n const {batch_id: batchId} = await init_asset_upload({\n collection,\n full_path: fullPath,\n name: filename,\n token: toNullable<string>(token),\n encoding_type: toNullable<ENCODING_TYPE>(encoding),\n description: toNullable(description)\n });\n\n // https://forum.dfinity.org/t/optimal-upload-chunk-size/20444/23?u=peterparker\n const chunkSize = 1900000;\n\n const uploadChunks: UploadChunkParams[] = [];\n\n // Prevent transforming chunk to arrayBuffer error: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.\n const clone: Blob = isBrowser() ? new Blob([await data.arrayBuffer()]) : data;\n\n // Split data into chunks\n let orderId = 0n;\n for (let start = 0; start < clone.size; start += chunkSize) {\n const chunk: Blob = clone.slice(start, start + chunkSize);\n\n uploadChunks.push({\n batchId,\n chunk,\n actor,\n orderId\n });\n\n orderId++;\n }\n\n // Upload chunks to the IC in batch - i.e. 12 chunks uploaded at a time.\n let chunkIds: UploadChunkResult[] = [];\n for await (const results of batchUploadChunks({uploadChunks})) {\n chunkIds = [...chunkIds, ...results];\n }\n\n const contentType: [[string, string]] | undefined =\n headers.find(([type, _]) => type.toLowerCase() === 'content-type') === undefined &&\n data.type !== undefined &&\n data.type !== ''\n ? [['Content-Type', data.type]]\n : undefined;\n\n await actor.commit_asset_upload({\n batch_id: batchId,\n chunk_ids: chunkIds.map(({chunk_id}: UploadChunkResult) => chunk_id),\n headers: [...headers, ...(contentType ? contentType : [])]\n });\n};\n\nasync function* batchUploadChunks({\n uploadChunks,\n limit = 12\n}: {\n uploadChunks: UploadChunkParams[];\n limit?: number;\n}): AsyncGenerator<UploadChunkResult[], void> {\n for (let i = 0; i < uploadChunks.length; i = i + limit) {\n const batch = uploadChunks.slice(i, i + limit);\n const result = await Promise.all(batch.map((params) => uploadChunk(params)));\n yield result;\n }\n}\n\ntype UploadChunkResult = {chunk_id: bigint};\n\ntype UploadChunkParams = {\n batchId: bigint;\n chunk: Blob;\n actor: SatelliteActor | ConsoleActor;\n orderId: bigint;\n};\n\nconst uploadChunk = async ({\n batchId,\n chunk,\n actor,\n orderId\n}: UploadChunkParams): Promise<UploadChunkResult> =>\n actor.upload_asset_chunk({\n batch_id: batchId,\n content: new Uint8Array(await chunk.arrayBuffer()),\n order_id: toNullable(orderId)\n });\n"],
5
+ "mappings": "ACAA,OAAQ,aAAAA,MAAgB,qBCMjB,IAAMC,EAAgBC,GAC3BA,GAAa,KAQFC,EAAiBD,GAC5B,CAACD,EAAUC,CAAQ,ECPd,IAAME,EAAiBC,GACrBC,EAAWD,CAAK,EAAI,CAACA,CAAK,EAAI,CAAC,ECNjC,IAAME,EAAY,IAAe,OAAO,OAAW,ICYnD,IAAMC,EAAc,MAAO,CAChC,MAAO,CAAC,KAAAC,EAAM,SAAAC,EAAU,WAAAC,EAAY,QAAAC,EAAS,MAAAC,EAAO,SAAAC,EAAU,SAAAC,EAAU,YAAAC,CAAW,EACnF,MAAAC,EACA,kBAAAC,CACF,IAMqB,CACnB,GAAM,CAAC,SAAUC,CAAO,EAAI,MAAMD,EAAkB,CAClD,WAAAP,EACA,UAAWG,EACX,KAAMJ,EACN,MAAOU,EAAmBP,CAAK,EAC/B,cAAeO,EAA0BL,CAAQ,EACjD,YAAaK,EAAWJ,CAAW,CACrC,CAAC,EAGKK,EAAY,KAEZC,EAAoC,CAAC,EAGrCC,EAAcC,EAAU,EAAI,IAAI,KAAK,CAAC,MAAMf,EAAK,YAAY,CAAC,CAAC,EAAIA,EAGrEgB,EAAU,GACd,QAASC,EAAQ,EAAGA,EAAQH,EAAM,KAAMG,GAASL,EAAW,CAC1D,IAAMM,EAAcJ,EAAM,MAAMG,EAAOA,EAAQL,CAAS,EAExDC,EAAa,KAAK,CAChB,QAAAH,EACA,MAAAQ,EACA,MAAAV,EACA,QAAAQ,CACF,CAAC,EAEDA,GACF,CAGA,IAAIG,EAAgC,CAAC,EACrC,cAAiBC,KAAWC,EAAkB,CAAC,aAAAR,CAAY,CAAC,EAC1DM,EAAW,CAAC,GAAGA,EAAU,GAAGC,CAAO,EAGrC,IAAME,EACJnB,EAAQ,KAAK,CAAC,CAACoB,EAAMC,CAAC,IAAMD,EAAK,YAAY,IAAM,cAAc,IAAM,QACvEvB,EAAK,OAAS,QACdA,EAAK,OAAS,GACV,CAAC,CAAC,eAAgBA,EAAK,IAAI,CAAC,EAC5B,OAEN,MAAMQ,EAAM,oBAAoB,CAC9B,SAAUE,EACV,UAAWS,EAAS,IAAI,CAAC,CAAC,SAAAM,CAAQ,IAAyBA,CAAQ,EACnE,QAAS,CAAC,GAAGtB,EAAS,GAAImB,GAA4B,CAAC,CAAE,CAC3D,CAAC,CACH,EAEA,eAAgBD,EAAkB,CAChC,aAAAR,EACA,MAAAa,EAAQ,EACV,EAG8C,CAC5C,QAASC,EAAI,EAAGA,EAAId,EAAa,OAAQc,EAAIA,EAAID,EAAO,CACtD,IAAME,EAAQf,EAAa,MAAMc,EAAGA,EAAID,CAAK,EAE7C,MADe,MAAM,QAAQ,IAAIE,EAAM,IAAKC,GAAWC,EAAYD,CAAM,CAAC,CAAC,CAE7E,CACF,CAWA,IAAMC,EAAc,MAAO,CACzB,QAAApB,EACA,MAAAQ,EACA,MAAAV,EACA,QAAAQ,CACF,IACER,EAAM,mBAAmB,CACvB,SAAUE,EACV,QAAS,IAAI,WAAW,MAAMQ,EAAM,YAAY,CAAC,EACjD,SAAUP,EAAWK,CAAO,CAC9B,CAAC",
6
+ "names": ["Principal", "isNullish", "argument", "nonNullish", "toNullable", "value", "nonNullish", "isBrowser", "uploadAsset", "data", "filename", "collection", "headers", "token", "fullPath", "encoding", "description", "actor", "init_asset_upload", "batchId", "g", "chunkSize", "uploadChunks", "clone", "h", "orderId", "start", "chunk", "chunkIds", "results", "batchUploadChunks", "contentType", "type", "_", "chunk_id", "limit", "i", "batch", "params", "uploadChunk"]
7
+ }
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './browser/index.js';
@@ -0,0 +1,4 @@
1
+ import { createRequire as topLevelCreateRequire } from 'module';
2
+ const require = topLevelCreateRequire(import.meta.url);
3
+ import{Principal as R}from"@dfinity/principal";var U=t=>t==null,k=t=>!U(t);var n=t=>k(t)?[t]:[];var _=()=>typeof window<"u";var B=async({asset:{data:t,filename:s,collection:o,headers:a,token:l,fullPath:i,encoding:b,description:A},actor:p,init_asset_upload:C})=>{let{batch_id:c}=await C({collection:o,full_path:i,name:s,token:n(l),encoding_type:n(b),description:n(A)}),u=19e5,d=[],y=_()?new Blob([await t.arrayBuffer()]):t,f=0n;for(let e=0;e<y.size;e+=u){let m=y.slice(e,e+u);d.push({batchId:c,chunk:m,actor:p,orderId:f}),f++}let r=[];for await(let e of I({uploadChunks:d}))r=[...r,...e];let h=a.find(([e,m])=>e.toLowerCase()==="content-type")===void 0&&t.type!==void 0&&t.type!==""?[["Content-Type",t.type]]:void 0;await p.commit_asset_upload({batch_id:c,chunk_ids:r.map(({chunk_id:e})=>e),headers:[...a,...h||[]]})};async function*I({uploadChunks:t,limit:s=12}){for(let o=0;o<t.length;o=o+s){let a=t.slice(o,o+s);yield await Promise.all(a.map(i=>w(i)))}}var w=async({batchId:t,chunk:s,actor:o,orderId:a})=>o.upload_asset_chunk({batch_id:t,content:new Uint8Array(await s.arrayBuffer()),order_id:n(a)});export{B as uploadAsset};
4
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../utils/src/utils/debounce.utils.ts", "../../../utils/src/utils/json.utils.ts", "../../../utils/src/utils/null.utils.ts", "../../../utils/src/utils/did.utils.ts", "../../../utils/src/utils/env.utils.ts", "../../src/api/storage.api.ts"],
4
+ "sourcesContent": ["/**\n * Creates a debounced function that delays invoking the provided function until after the specified timeout.\n * @param {Function} func - The function to debounce.\n * @param {number} [timeout=300] - The number of milliseconds to delay. Defaults to 300ms if not specified or invalid.\n * @returns {Function} A debounced function.\n */\n/* eslint-disable-next-line @typescript-eslint/ban-types */\nexport const debounce = (func: Function, timeout?: number): Function => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n return (...args: unknown[]) => {\n const next = () => func(...args);\n\n if (timer) {\n clearTimeout(timer);\n }\n\n timer = setTimeout(next, timeout !== undefined && timeout > 0 ? timeout : 300);\n };\n};\n", "import {Principal} from '@dfinity/principal';\nimport {nonNullish} from './null.utils';\n\nconst JSON_KEY_BIGINT = '__bigint__';\nconst JSON_KEY_PRINCIPAL = '__principal__';\nconst JSON_KEY_UINT8ARRAY = '__uint8array__';\n\n/**\n * A function that alters the behavior of the stringification process for BigInt, Principal, and Uint8Array.\n * @param {string} _key - The key of the value being stringified.\n * @param {unknown} value - The value being stringified.\n * @returns {unknown} The altered value for stringification.\n */\nexport const jsonReplacer = (_key: string, value: unknown): unknown => {\n if (typeof value === 'bigint') {\n return {[JSON_KEY_BIGINT]: `${value}`};\n }\n\n if (nonNullish(value) && value instanceof Principal) {\n return {[JSON_KEY_PRINCIPAL]: value.toText()};\n }\n\n if (nonNullish(value) && value instanceof Uint8Array) {\n return {[JSON_KEY_UINT8ARRAY]: Array.from(value)};\n }\n\n return value;\n};\n\n/**\n * A parser that interprets revived BigInt, Principal, and Uint8Array when constructing JavaScript values or objects.\n * @param {string} _key - The key of the value being parsed.\n * @param {unknown} value - The value being parsed.\n * @returns {unknown} The parsed value.\n */\nexport const jsonReviver = (_key: string, value: unknown): unknown => {\n const mapValue = <T>(key: string): T => (value as Record<string, T>)[key];\n\n if (nonNullish(value) && typeof value === 'object' && JSON_KEY_BIGINT in value) {\n return BigInt(mapValue(JSON_KEY_BIGINT));\n }\n\n if (nonNullish(value) && typeof value === 'object' && JSON_KEY_PRINCIPAL in value) {\n return Principal.fromText(mapValue(JSON_KEY_PRINCIPAL));\n }\n\n if (nonNullish(value) && typeof value === 'object' && JSON_KEY_UINT8ARRAY in value) {\n return Uint8Array.from(mapValue(JSON_KEY_UINT8ARRAY));\n }\n\n return value;\n};\n", "/**\n * Checks if the provided argument is null or undefined.\n * @template T\n * @param {T | undefined | null} argument - The argument to check.\n * @returns {boolean} True if the argument is null or undefined, false otherwise.\n */\nexport const isNullish = <T>(argument: T | undefined | null): argument is undefined | null =>\n argument === null || argument === undefined;\n\n/**\n * Checks if the provided argument is neither null nor undefined.\n * @template T\n * @param {T | undefined | null} argument - The argument to check.\n * @returns {boolean} True if the argument is neither null nor undefined, false otherwise.\n */\nexport const nonNullish = <T>(argument: T | undefined | null): argument is NonNullable<T> =>\n !isNullish(argument);\n\n/**\n * Represents an error thrown when a value is null or undefined.\n * @class\n * @extends {Error}\n */\nexport class NullishError extends Error {}\n\n/**\n * Asserts that a value is neither null nor undefined.\n * @template T\n * @param {T} value - The value to check.\n * @param {string} [message] - The optional error message to use if the assertion fails.\n * @throws {NullishError} If the value is null or undefined.\n * @returns {asserts value is NonNullable<T>} Asserts that the value is neither null nor undefined.\n */\nexport const assertNonNullish: <T>(\n value: T,\n message?: string\n) => asserts value is NonNullable<T> = <T>(value: T, message?: string): void => {\n if (isNullish(value)) {\n throw new NullishError(message);\n }\n};\n", "import {jsonReplacer, jsonReviver} from './json.utils';\nimport {nonNullish} from './null.utils';\n\n/**\n * Converts a value to a nullable array.\n * @template T\n * @param {T} [value] - The value to convert.\n * @returns {([] | [T])} A nullable array containing the value if non-nullish, or an empty array if nullish.\n */\nexport const toNullable = <T>(value?: T): [] | [T] => {\n return nonNullish(value) ? [value] : [];\n};\n\n/**\n * Extracts a value from a nullable array.\n * @template T\n * @param {([] | [T])} value - The nullable array.\n * @returns {(T | undefined)} The value if present, or undefined if the array is empty.\n */\nexport const fromNullable = <T>(value: [] | [T]): T | undefined => {\n return value?.[0];\n};\n\n/**\n * Converts data to a Uint8Array for transmission or storage.\n * @template T\n * @param {T} data - The data to convert.\n * @returns {Promise<Uint8Array>} A promise that resolves to a Uint8Array representation of the data.\n */\nexport const toArray = async <T>(data: T): Promise<Uint8Array> => {\n const blob: Blob = new Blob([JSON.stringify(data, jsonReplacer)], {\n type: 'application/json; charset=utf-8'\n });\n return new Uint8Array(await blob.arrayBuffer());\n};\n\n/**\n * Converts a Uint8Array or number array back to the original data type.\n * @template T\n * @param {(Uint8Array | number[])} data - The array to convert.\n * @returns {Promise<T>} A promise that resolves to the original data.\n */\nexport const fromArray = async <T>(data: Uint8Array | number[]): Promise<T> => {\n const blob: Blob = new Blob([data instanceof Uint8Array ? data : new Uint8Array(data)], {\n type: 'application/json; charset=utf-8'\n });\n return JSON.parse(await blob.text(), jsonReviver);\n};\n", "/**\n * Checks if the current environment is a browser.\n * @returns {boolean} True if the current environment is a browser, false otherwise.\n */\nexport const isBrowser = (): boolean => typeof window !== `undefined`;\n", "import {isBrowser, toNullable} from '@junobuild/utils';\nimport type {\n _SERVICE as ConsoleActor,\n InitAssetKey as ConsoleInitAssetKey,\n InitUploadResult as ConsoleInitUploadResult\n} from '../../declarations/console/console.did';\nimport type {\n _SERVICE as SatelliteActor,\n InitAssetKey as SatelliteInitAssetKey,\n InitUploadResult as SatelliteInitUploadResult\n} from '../../declarations/satellite/satellite.did';\nimport type {ENCODING_TYPE, Storage} from '../types/storage.types';\n\nexport type UploadAsset = Required<Omit<Storage, 'token' | 'encoding' | 'description'>> &\n Pick<Storage, 'token' | 'encoding' | 'description'>;\n\nexport const uploadAsset = async ({\n asset: {data, filename, collection, headers, token, fullPath, encoding, description},\n actor,\n init_asset_upload\n}: {\n asset: UploadAsset;\n actor: SatelliteActor | ConsoleActor;\n init_asset_upload: (\n initAssetKey: SatelliteInitAssetKey | ConsoleInitAssetKey\n ) => Promise<SatelliteInitUploadResult | ConsoleInitUploadResult>;\n}): Promise<void> => {\n const {batch_id: batchId} = await init_asset_upload({\n collection,\n full_path: fullPath,\n name: filename,\n token: toNullable<string>(token),\n encoding_type: toNullable<ENCODING_TYPE>(encoding),\n description: toNullable(description)\n });\n\n // https://forum.dfinity.org/t/optimal-upload-chunk-size/20444/23?u=peterparker\n const chunkSize = 1900000;\n\n const uploadChunks: UploadChunkParams[] = [];\n\n // Prevent transforming chunk to arrayBuffer error: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.\n const clone: Blob = isBrowser() ? new Blob([await data.arrayBuffer()]) : data;\n\n // Split data into chunks\n let orderId = 0n;\n for (let start = 0; start < clone.size; start += chunkSize) {\n const chunk: Blob = clone.slice(start, start + chunkSize);\n\n uploadChunks.push({\n batchId,\n chunk,\n actor,\n orderId\n });\n\n orderId++;\n }\n\n // Upload chunks to the IC in batch - i.e. 12 chunks uploaded at a time.\n let chunkIds: UploadChunkResult[] = [];\n for await (const results of batchUploadChunks({uploadChunks})) {\n chunkIds = [...chunkIds, ...results];\n }\n\n const contentType: [[string, string]] | undefined =\n headers.find(([type, _]) => type.toLowerCase() === 'content-type') === undefined &&\n data.type !== undefined &&\n data.type !== ''\n ? [['Content-Type', data.type]]\n : undefined;\n\n await actor.commit_asset_upload({\n batch_id: batchId,\n chunk_ids: chunkIds.map(({chunk_id}: UploadChunkResult) => chunk_id),\n headers: [...headers, ...(contentType ? contentType : [])]\n });\n};\n\nasync function* batchUploadChunks({\n uploadChunks,\n limit = 12\n}: {\n uploadChunks: UploadChunkParams[];\n limit?: number;\n}): AsyncGenerator<UploadChunkResult[], void> {\n for (let i = 0; i < uploadChunks.length; i = i + limit) {\n const batch = uploadChunks.slice(i, i + limit);\n const result = await Promise.all(batch.map((params) => uploadChunk(params)));\n yield result;\n }\n}\n\ntype UploadChunkResult = {chunk_id: bigint};\n\ntype UploadChunkParams = {\n batchId: bigint;\n chunk: Blob;\n actor: SatelliteActor | ConsoleActor;\n orderId: bigint;\n};\n\nconst uploadChunk = async ({\n batchId,\n chunk,\n actor,\n orderId\n}: UploadChunkParams): Promise<UploadChunkResult> =>\n actor.upload_asset_chunk({\n batch_id: batchId,\n content: new Uint8Array(await chunk.arrayBuffer()),\n order_id: toNullable(orderId)\n });\n"],
5
+ "mappings": ";;ACAA,OAAQ,aAAAA,MAAgB,qBCMjB,IAAMC,EAAgBC,GAC3BA,GAAa,KAQFC,EAAiBD,GAC5B,CAACD,EAAUC,CAAQ,ECPd,IAAME,EAAiBC,GACrBC,EAAWD,CAAK,EAAI,CAACA,CAAK,EAAI,CAAC,ECNjC,IAAME,EAAY,IAAe,OAAO,OAAW,ICYnD,IAAMC,EAAc,MAAO,CAChC,MAAO,CAAC,KAAAC,EAAM,SAAAC,EAAU,WAAAC,EAAY,QAAAC,EAAS,MAAAC,EAAO,SAAAC,EAAU,SAAAC,EAAU,YAAAC,CAAW,EACnF,MAAAC,EACA,kBAAAC,CACF,IAMqB,CACnB,GAAM,CAAC,SAAUC,CAAO,EAAI,MAAMD,EAAkB,CAClD,WAAAP,EACA,UAAWG,EACX,KAAMJ,EACN,MAAOU,EAAmBP,CAAK,EAC/B,cAAeO,EAA0BL,CAAQ,EACjD,YAAaK,EAAWJ,CAAW,CACrC,CAAC,EAGKK,EAAY,KAEZC,EAAoC,CAAC,EAGrCC,EAAcC,EAAU,EAAI,IAAI,KAAK,CAAC,MAAMf,EAAK,YAAY,CAAC,CAAC,EAAIA,EAGrEgB,EAAU,GACd,QAASC,EAAQ,EAAGA,EAAQH,EAAM,KAAMG,GAASL,EAAW,CAC1D,IAAMM,EAAcJ,EAAM,MAAMG,EAAOA,EAAQL,CAAS,EAExDC,EAAa,KAAK,CAChB,QAAAH,EACA,MAAAQ,EACA,MAAAV,EACA,QAAAQ,CACF,CAAC,EAEDA,GACF,CAGA,IAAIG,EAAgC,CAAC,EACrC,cAAiBC,KAAWC,EAAkB,CAAC,aAAAR,CAAY,CAAC,EAC1DM,EAAW,CAAC,GAAGA,EAAU,GAAGC,CAAO,EAGrC,IAAME,EACJnB,EAAQ,KAAK,CAAC,CAACoB,EAAMC,CAAC,IAAMD,EAAK,YAAY,IAAM,cAAc,IAAM,QACvEvB,EAAK,OAAS,QACdA,EAAK,OAAS,GACV,CAAC,CAAC,eAAgBA,EAAK,IAAI,CAAC,EAC5B,OAEN,MAAMQ,EAAM,oBAAoB,CAC9B,SAAUE,EACV,UAAWS,EAAS,IAAI,CAAC,CAAC,SAAAM,CAAQ,IAAyBA,CAAQ,EACnE,QAAS,CAAC,GAAGtB,EAAS,GAAImB,GAA4B,CAAC,CAAE,CAC3D,CAAC,CACH,EAEA,eAAgBD,EAAkB,CAChC,aAAAR,EACA,MAAAa,EAAQ,EACV,EAG8C,CAC5C,QAASC,EAAI,EAAGA,EAAId,EAAa,OAAQc,EAAIA,EAAID,EAAO,CACtD,IAAME,EAAQf,EAAa,MAAMc,EAAGA,EAAID,CAAK,EAE7C,MADe,MAAM,QAAQ,IAAIE,EAAM,IAAKC,GAAWC,EAAYD,CAAM,CAAC,CAAC,CAE7E,CACF,CAWA,IAAMC,EAAc,MAAO,CACzB,QAAApB,EACA,MAAAQ,EACA,MAAAV,EACA,QAAAQ,CACF,IACER,EAAM,mBAAmB,CACvB,SAAUE,EACV,QAAS,IAAI,WAAW,MAAMQ,EAAM,YAAY,CAAC,EACjD,SAAUP,EAAWK,CAAO,CAC9B,CAAC",
6
+ "names": ["Principal", "isNullish", "argument", "nonNullish", "toNullable", "value", "nonNullish", "isBrowser", "uploadAsset", "data", "filename", "collection", "headers", "token", "fullPath", "encoding", "description", "actor", "init_asset_upload", "batchId", "g", "chunkSize", "uploadChunks", "clone", "h", "orderId", "start", "chunk", "chunkIds", "results", "batchUploadChunks", "contentType", "type", "_", "chunk_id", "limit", "i", "batch", "params", "uploadChunk"]
7
+ }
@@ -0,0 +1,9 @@
1
+ import type { _SERVICE as ConsoleActor, InitAssetKey as ConsoleInitAssetKey, InitUploadResult as ConsoleInitUploadResult } from '../../declarations/console/console.did';
2
+ import type { _SERVICE as SatelliteActor, InitAssetKey as SatelliteInitAssetKey, InitUploadResult as SatelliteInitUploadResult } from '../../declarations/satellite/satellite.did';
3
+ import type { Storage } from '../types/storage.types';
4
+ export type UploadAsset = Required<Omit<Storage, 'token' | 'encoding' | 'description'>> & Pick<Storage, 'token' | 'encoding' | 'description'>;
5
+ export declare const uploadAsset: ({ asset: { data, filename, collection, headers, token, fullPath, encoding, description }, actor, init_asset_upload }: {
6
+ asset: UploadAsset;
7
+ actor: SatelliteActor | ConsoleActor;
8
+ init_asset_upload: (initAssetKey: SatelliteInitAssetKey | ConsoleInitAssetKey) => Promise<SatelliteInitUploadResult | ConsoleInitUploadResult>;
9
+ }) => Promise<void>;
@@ -0,0 +1,2 @@
1
+ export * from './api/storage.api';
2
+ export type * from './types/storage.types';
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Represents the encoding details of an asset.
3
+ * @interface
4
+ */
5
+ export interface AssetEncoding {
6
+ /**
7
+ * The timestamp when the encoding was modified.
8
+ * @type {bigint}
9
+ */
10
+ modified: bigint;
11
+ /**
12
+ * The SHA-256 hash of the encoding.
13
+ * @type {string}
14
+ */
15
+ sha256: string;
16
+ /**
17
+ * The total length of the encoding.
18
+ * @type {bigint}
19
+ */
20
+ total_length: bigint;
21
+ }
22
+ /**
23
+ * Represents the key details of an asset.
24
+ * @interface
25
+ */
26
+ export interface AssetKey {
27
+ /**
28
+ * The full path of the asset, i.e., URL.pathname. Except for the dApps assets (HTML, JS, etc.), the full path must start with `/collection/`.
29
+ * @type {string}
30
+ */
31
+ fullPath: string;
32
+ /**
33
+ * The name of the asset.
34
+ * @type {string}
35
+ */
36
+ name: string;
37
+ /**
38
+ * The download URL of the asset.
39
+ * @type {string}
40
+ */
41
+ downloadUrl: string;
42
+ }
43
+ /**
44
+ * Type representing the possible encoding types.
45
+ * @typedef {('identity' | 'gzip' | 'compress' | 'deflate' | 'br')} ENCODING_TYPE
46
+ */
47
+ export type ENCODING_TYPE = 'identity' | 'gzip' | 'compress' | 'deflate' | 'br';
48
+ /**
49
+ * Represents an asset with its key details, encodings, and metadata.
50
+ * @interface
51
+ * @extends {AssetKey}
52
+ */
53
+ export interface Asset extends AssetKey {
54
+ /**
55
+ * An optional description of the asset. A field which can be used to filter assets when listing those.
56
+ * @type {string}
57
+ */
58
+ description?: string;
59
+ /**
60
+ * An optional token associated with the asset. Tokens are used to protect assets on the web. If a token is provided, the asset is delivered on the web only if the query parameter `token` is provided with a matching value.
61
+ * @type {string}
62
+ */
63
+ token?: string;
64
+ /**
65
+ * The headers associated with the asset.
66
+ * @type {[string, string][]}
67
+ */
68
+ headers: [string, string][];
69
+ /**
70
+ * The encodings of the asset.
71
+ * @type {Record<ENCODING_TYPE, AssetEncoding>}
72
+ */
73
+ encodings: Record<ENCODING_TYPE, AssetEncoding>;
74
+ /**
75
+ * The owner of the asset.
76
+ * @type {string}
77
+ */
78
+ owner?: string;
79
+ /**
80
+ * The timestamp when the asset was created.
81
+ * @type {bigint}
82
+ */
83
+ created_at?: bigint;
84
+ /**
85
+ * The timestamp when the asset was last updated.
86
+ * @type {bigint}
87
+ */
88
+ updated_at?: bigint;
89
+ }
90
+ /**
91
+ * Represents the details required to store an asset.
92
+ * @interface
93
+ */
94
+ export interface Storage {
95
+ /**
96
+ * The filename of the asset.
97
+ * @type {string}
98
+ */
99
+ filename: string;
100
+ /**
101
+ * The data of the asset.
102
+ * @type {Blob}
103
+ */
104
+ data: Blob;
105
+ /**
106
+ * The collection to which the asset belongs.
107
+ * @type {string}
108
+ */
109
+ collection: string;
110
+ /**
111
+ * The full path of the asset, i.e., URL.pathname. Except for the dApps assets (HTML, JS, etc.), the full path must start with `/collection/`.
112
+ * @type {string}
113
+ */
114
+ fullPath?: string;
115
+ /**
116
+ * The headers associated with the asset.
117
+ * @type {[string, string][]}
118
+ */
119
+ headers?: [string, string][];
120
+ /**
121
+ * An optional token associated with the asset. Tokens are used to protect assets on the web. If a token is provided, the asset is delivered on the web only if the query parameter `token` is provided with a matching value.
122
+ * @type {string}
123
+ */
124
+ token?: string;
125
+ /**
126
+ * The encoding type of the asset.
127
+ * @type {ENCODING_TYPE}
128
+ */
129
+ encoding?: ENCODING_TYPE;
130
+ /**
131
+ * An optional description of the asset. A field which can be used to filter assets when listing those.
132
+ * @type {string}
133
+ */
134
+ description?: string;
135
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@junobuild/storage",
3
+ "version": "0.0.1-next-2024-06-26",
4
+ "description": "A library for interfacing with Juno's Storage features.",
5
+ "author": "David Dal Busco (https://daviddalbusco.com)",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/node/index.mjs",
9
+ "module": "./dist/browser/index.js",
10
+ "types": "./dist/types/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/types/index.d.ts",
15
+ "default": "./dist/browser/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./dist/types/index.d.ts",
19
+ "default": "./dist/node/index.mjs"
20
+ }
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "scripts": {
29
+ "rmdir": "node ../../scripts/rmdir.mjs",
30
+ "ts-declaration": "tsc --emitDeclarationOnly --outDir dist/types",
31
+ "build": "npm run rmdir && mkdir -p dist && node esbuild.mjs && npm run ts-declaration",
32
+ "prepack": "npm run build"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/junobuild/juno-js.git",
37
+ "directory": "packages/storage"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/junobuild/juno-js"
41
+ },
42
+ "keywords": [
43
+ "web3",
44
+ "storage",
45
+ "web storage",
46
+ "file storage"
47
+ ],
48
+ "homepage": "https://juno.build",
49
+ "peerDependencies": {
50
+ "@dfinity/principal": "*"
51
+ }
52
+ }