@ocfl/ocfl 0.3.0 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +24 -9
- package/dist/constants.d.ts +15 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/digest.d.ts +58 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +204 -0
- package/dist/digest.js.map +1 -0
- package/dist/error.d.ts +5 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +9 -0
- package/dist/error.js.map +1 -0
- package/dist/extension.d.ts +83 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +170 -0
- package/dist/extension.js.map +1 -0
- package/dist/extensions/0002-flat-direct-storage-layout.d.ts +12 -0
- package/dist/extensions/0002-flat-direct-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0002-flat-direct-storage-layout.js +34 -0
- package/dist/extensions/0002-flat-direct-storage-layout.js.map +1 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.d.ts +15 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.js +49 -0
- package/dist/extensions/0003-hash-and-id-n-tuple-storage-layout.js.map +1 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.d.ts +17 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.js +54 -0
- package/dist/extensions/0004-hashed-n-tuple-storage-layout.js.map +1 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.d.ts +6 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.js +8 -0
- package/dist/extensions/0006-flat-omit-prefix-storage-layout.js.map +1 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.d.ts +6 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.d.ts.map +1 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.js +8 -0
- package/dist/extensions/0007-n-tuple-omit-prefix-storage-layout.js.map +1 -0
- package/dist/extensions/0009-digest-algorithms.d.ts +9 -0
- package/dist/extensions/0009-digest-algorithms.d.ts.map +1 -0
- package/dist/extensions/0009-digest-algorithms.js +11 -0
- package/dist/extensions/0009-digest-algorithms.js.map +1 -0
- package/dist/extensions/000N-path-direct-storage-layout.d.ts +16 -0
- package/dist/extensions/000N-path-direct-storage-layout.d.ts.map +1 -0
- package/dist/extensions/000N-path-direct-storage-layout.js +46 -0
- package/dist/extensions/000N-path-direct-storage-layout.js.map +1 -0
- package/dist/extensions/index.d.ts +23 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +8 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/file.d.ts +87 -0
- package/dist/file.d.ts.map +1 -0
- package/dist/file.js +118 -0
- package/dist/file.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory.d.ts +155 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +591 -0
- package/dist/inventory.js.map +1 -0
- package/dist/object.d.ts +188 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/object.js +425 -0
- package/dist/object.js.map +1 -0
- package/dist/storage.d.ts +94 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +302 -0
- package/dist/storage.js.map +1 -0
- package/dist/store.d.ts +100 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +157 -0
- package/dist/store.js.map +1 -0
- package/dist/transaction.d.ts +155 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +395 -0
- package/dist/transaction.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +85 -0
- package/dist/utils.js.map +1 -0
- package/dist/validation.d.ts +32 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +31 -0
- package/dist/validation.js.map +1 -0
- package/package.json +31 -50
- package/src/constants.ts +16 -0
- package/src/digest.ts +258 -0
- package/src/error.ts +9 -0
- package/src/extension.ts +206 -0
- package/{lib/extensions/0002-flat-direct-storage-layout.js → src/extensions/0002-flat-direct-storage-layout.ts} +15 -22
- package/src/extensions/0003-hash-and-id-n-tuple-storage-layout.ts +56 -0
- package/src/extensions/0004-hashed-n-tuple-storage-layout.ts +59 -0
- package/src/extensions/0006-flat-omit-prefix-storage-layout.ts +9 -0
- package/src/extensions/0007-n-tuple-omit-prefix-storage-layout.ts +9 -0
- package/src/extensions/0009-digest-algorithms.ts +12 -0
- package/src/extensions/000N-path-direct-storage-layout.ts +51 -0
- package/src/extensions/index.ts +24 -0
- package/src/file.ts +161 -0
- package/src/index.ts +92 -0
- package/src/inventory.ts +634 -0
- package/src/object.ts +473 -0
- package/src/storage.ts +342 -0
- package/src/store.ts +199 -0
- package/src/transaction.ts +456 -0
- package/{lib/types.d.ts → src/types.ts} +28 -74
- package/src/utils.ts +94 -0
- package/src/validation.ts +46 -0
- package/index.js +0 -129
- package/index.mjs +0 -18
- package/lib/constants.js +0 -24
- package/lib/digest.js +0 -307
- package/lib/enum.js +0 -90
- package/lib/error.js +0 -19
- package/lib/extension.js +0 -194
- package/lib/extensions/0003-hash-and-id-n-tuple-storage-layout.js +0 -60
- package/lib/extensions/0004-hashed-n-tuple-storage-layout.js +0 -63
- package/lib/extensions/0005-mutable-head.js +0 -0
- package/lib/extensions/0006-flat-omit-prefix-storage-layout.js +0 -7
- package/lib/extensions/0007-n-tuple-omit-prefix-storage-layout.js +0 -7
- package/lib/extensions/0008-schema-registry.js +0 -0
- package/lib/extensions/0009-digest-algorithms.js +0 -18
- package/lib/extensions/000N-path-direct-storage-layout.js +0 -61
- package/lib/extensions/index.js +0 -13
- package/lib/file.js +0 -129
- package/lib/index.js +0 -13
- package/lib/inventory.js +0 -564
- package/lib/object.js +0 -451
- package/lib/storage.js +0 -338
- package/lib/store.js +0 -191
- package/lib/transaction.js +0 -448
- package/lib/utils.js +0 -123
- package/lib/validation.js +0 -174
package/src/utils.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { OcflConstants } from './constants.ts';
|
|
2
|
+
import type { OcflStore } from './store.ts';
|
|
3
|
+
import type { OcflVersion } from './types.ts';
|
|
4
|
+
|
|
5
|
+
const { OCFL_VERSIONS, NAMASTE_T } = OcflConstants;
|
|
6
|
+
|
|
7
|
+
function isErrnoException(error: unknown): error is NodeJS.ErrnoException {
|
|
8
|
+
return error instanceof Error && 'code' in error;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Run `asyncFn` over every element of `inputStack` with at most `count` calls in flight.
|
|
13
|
+
* Rejections are returned in the result array rather than thrown.
|
|
14
|
+
* @param inputStack - Consumed during the process.
|
|
15
|
+
* @param count - Maximum number of concurrency
|
|
16
|
+
*/
|
|
17
|
+
async function parallelize<T, R>(inputStack: T[], asyncFn: (input: T) => Promise<R>, count = 10): Promise<(R | Error)[]> {
|
|
18
|
+
const promises: Promise<void>[] = [];
|
|
19
|
+
let len = Math.min(inputStack.length, count);
|
|
20
|
+
const result: (R | Error)[] = [];
|
|
21
|
+
while (len--) {
|
|
22
|
+
promises.push(
|
|
23
|
+
(async () => {
|
|
24
|
+
while (inputStack.length) {
|
|
25
|
+
const input = inputStack.pop() as T;
|
|
26
|
+
const index = inputStack.length;
|
|
27
|
+
try {
|
|
28
|
+
result[index] = await asyncFn(input);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
result[index] = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})().catch(() => {}),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
await Promise.all(promises);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function isDirEmpty(store: OcflStore, dirPath: string): Promise<boolean> {
|
|
41
|
+
let de: unknown;
|
|
42
|
+
try {
|
|
43
|
+
const dir = await store.opendir(dirPath);
|
|
44
|
+
de = await dir.read();
|
|
45
|
+
await dir.close();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return isErrnoException(error) && error.code === 'ENOENT';
|
|
48
|
+
}
|
|
49
|
+
return !de;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if there is any valid namaste in the rootPath and return the version number.
|
|
54
|
+
* @returns The version, `''` if a namaste exists but is invalid, or `undefined` if there is none.
|
|
55
|
+
*/
|
|
56
|
+
async function findNamasteVersion(store: OcflStore, prefix: string, rootPath: string): Promise<OcflVersion | '' | undefined> {
|
|
57
|
+
const namastePath = `${rootPath}/${NAMASTE_T}${prefix}`;
|
|
58
|
+
let version: OcflVersion | null | undefined;
|
|
59
|
+
try {
|
|
60
|
+
version = await Promise.any(OCFL_VERSIONS.map(async (v) => ((await store.readFile(namastePath + v, 'utf8')) === `${prefix}${v}\n` ? v : null)));
|
|
61
|
+
if (!version) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (!(error instanceof AggregateError) || error.errors.some((e) => !isErrnoException(e) || e.code !== 'ENOENT')) {
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return version ?? undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type TypedArray = ArrayLike<number> & { buffer: ArrayBufferLike; byteLength: number; set(array: ArrayLike<number>, offset?: number): void };
|
|
73
|
+
|
|
74
|
+
function isTypedArray(obj: unknown): obj is TypedArray {
|
|
75
|
+
return ArrayBuffer.isView(obj) && !(obj instanceof DataView);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function joinTypedArray(target: ArrayBuffer | TypedArray, source: ArrayBuffer | TypedArray): TypedArray {
|
|
79
|
+
const sourceArray = isTypedArray(source) ? source : new Uint8Array(source);
|
|
80
|
+
const [targetBuffer, targetArray] = isTypedArray(target) ? [target.buffer as ArrayBuffer, target] : [target, new Uint8Array(target)];
|
|
81
|
+
const targetLength = targetArray.byteLength;
|
|
82
|
+
const totalSize = targetLength + sourceArray.byteLength;
|
|
83
|
+
let mergedArray = targetArray;
|
|
84
|
+
if (targetBuffer.resizable && targetBuffer.maxByteLength >= totalSize) {
|
|
85
|
+
targetBuffer.resize(totalSize);
|
|
86
|
+
} else {
|
|
87
|
+
mergedArray = new Uint8Array(totalSize);
|
|
88
|
+
mergedArray.set(targetArray, 0);
|
|
89
|
+
}
|
|
90
|
+
mergedArray.set(sourceArray, targetLength);
|
|
91
|
+
return mergedArray;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { findNamasteVersion, isDirEmpty, isErrnoException, joinTypedArray, parallelize };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Messages for the OCFL 1.1 validation codes this library raises: https://ocfl.io/1.1/spec/validation-codes.html
|
|
2
|
+
const ERRORS = {
|
|
3
|
+
E007: 'The text contents of the [version declaration] file must be the same as dvalue, followed by a newline (\\n).',
|
|
4
|
+
E036: 'An OCFL Object Inventory MUST include the following keys: [id, type, digestAlgorithm, head].',
|
|
5
|
+
E040: '[head] MUST be the version directory name with the highest version number.',
|
|
6
|
+
E050: 'The keys of [the state JSON object] are digest values, each of which MUST correspond to an entry in the manifest of the inventory.',
|
|
7
|
+
E060: 'The digest sidecar file must contain the digest of the inventory file.',
|
|
8
|
+
E069: 'An OCFL Storage Root MUST contain a Root Conformance Declaration identifying it as such.',
|
|
9
|
+
E080: 'The text contents of [the OCFL version declaration file] MUST be the same as dvalue, followed by a newline (\\n).',
|
|
10
|
+
E082: 'OCFL Object Roots MUST be stored either as the terminal resource at the end of a directory storage hierarchy or as direct children of a containing OCFL Storage Root.',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type ErrorCode = keyof typeof ERRORS;
|
|
14
|
+
|
|
15
|
+
interface ValidationErrorOptions {
|
|
16
|
+
code: ErrorCode;
|
|
17
|
+
description: string;
|
|
18
|
+
path: string;
|
|
19
|
+
detail?: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class ValidationError extends Error {
|
|
23
|
+
declare code: ErrorCode;
|
|
24
|
+
declare description: string;
|
|
25
|
+
declare path: string;
|
|
26
|
+
declare detail?: string | undefined;
|
|
27
|
+
|
|
28
|
+
constructor(options: ValidationErrorOptions) {
|
|
29
|
+
super(`${options.code}: ${options.description} At ${options.path}${options.detail ? ` (${options.detail})` : ''}`);
|
|
30
|
+
this.name = 'ValidationError';
|
|
31
|
+
Object.assign(this, options);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param code - OCFL validation code, eg 'E060'
|
|
37
|
+
* @param path - Path the problem was found at
|
|
38
|
+
* @param detail - Extra context appended to the message
|
|
39
|
+
*/
|
|
40
|
+
export const createError = (code: ErrorCode, path: string, detail?: string) => {
|
|
41
|
+
const description = Object.hasOwn(ERRORS, code) ? ERRORS[code] : undefined;
|
|
42
|
+
if (!description) {
|
|
43
|
+
throw new RangeError(`Unknown OCFL validation code ${code}`);
|
|
44
|
+
}
|
|
45
|
+
return new ValidationError({ code, description, path, detail });
|
|
46
|
+
};
|
package/index.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module ocfl
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const { OcflConstants, OcflDigest, OcflExtension, OcflStorageLayout, OcflStore,
|
|
6
|
-
OcflObject, OcflObjectInventory, OcflObjectTransactionImpl,
|
|
7
|
-
OcflStorage, OcflStorageImpl,
|
|
8
|
-
extensions
|
|
9
|
-
} = require('./lib/index.js');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @typedef {Object} StorageLayout
|
|
13
|
-
* @property {typeof extensions.FlatDirectStorageLayout} FlatDirectStorageLayout
|
|
14
|
-
* @property {typeof extensions.FlatOmitPrefixStorageLayout} FlatOmitPrefixStorageLayout
|
|
15
|
-
* @property {typeof extensions.HashAndIdNTupleStorageLayout} HashAndIdNTupleStorageLayout
|
|
16
|
-
* @property {typeof extensions.HashedNTupleStorageLayout} HashedNTupleStorageLayout
|
|
17
|
-
* @property {typeof extensions.NTupleOmitPrefixStorageLayout} NTupleOmitPrefixStorageLayout
|
|
18
|
-
* @property {typeof extensions.PathDirectStorageLayout} PathDirectStorageLayout
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
/** @typedef {import('./lib/types').OcflExtensionConfig} OcflExtensionConfig */
|
|
22
|
-
/** @typedef {import('./lib/types').OcflObjectConfig} OcflObjectConfig */
|
|
23
|
-
/** @typedef {import('./lib/types').OcflStorageConfig} OcflStorageConfig */
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Create a storage layout from extension name or config
|
|
27
|
-
* @param {string|OcflExtensionConfig} config - Layout name or config
|
|
28
|
-
*/
|
|
29
|
-
function layout(config) {
|
|
30
|
-
let name, c;
|
|
31
|
-
if (typeof config === 'string') {
|
|
32
|
-
name = config;
|
|
33
|
-
} else {
|
|
34
|
-
name = config.extensionName;
|
|
35
|
-
c = config;
|
|
36
|
-
}
|
|
37
|
-
return OcflStorageLayout.class(name).create(c);
|
|
38
|
-
}
|
|
39
|
-
let storageLayout = Object.assign(layout, /** @type {StorageLayout} */(OcflStorageLayout.layout))
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Creates a new OCFL core API with a specific backend store.
|
|
43
|
-
* Any backend store implementation module must return the result of this method.
|
|
44
|
-
* @param {typeof OcflStore} store
|
|
45
|
-
* @param {SO} defaultOptions
|
|
46
|
-
* @template SO
|
|
47
|
-
* @returns Top level OCFL API interface that provides storage and object creation.
|
|
48
|
-
*/
|
|
49
|
-
function implementOcfl(store, defaultOptions) {
|
|
50
|
-
return {
|
|
51
|
-
OCFL_VERSIONS: OcflConstants.OCFL_VERSIONS,
|
|
52
|
-
|
|
53
|
-
/** Built-in storage layout classes */
|
|
54
|
-
StorageLayout: OcflStorageLayout.layout,
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Instantiate a storage layout from extension name or config
|
|
58
|
-
* @param {string | OcflExtensionConfig} config - Layout name or config
|
|
59
|
-
*/
|
|
60
|
-
storageLayout(config) {
|
|
61
|
-
let name, c;
|
|
62
|
-
if (typeof config === 'string') {
|
|
63
|
-
name = config;
|
|
64
|
-
} else {
|
|
65
|
-
name = config.extensionName;
|
|
66
|
-
c = config;
|
|
67
|
-
}
|
|
68
|
-
let cls = OcflStorageLayout.class(name)
|
|
69
|
-
if (!cls) throw new Error(`Layout "${name}" is not supported.`);
|
|
70
|
-
return cls.create(c);
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Instantiate an OCFL Object
|
|
75
|
-
* @param {OcflObjectConfig} config
|
|
76
|
-
* @param {SO} [storeOptions]
|
|
77
|
-
*/
|
|
78
|
-
object(config, storeOptions = defaultOptions) {
|
|
79
|
-
return new OcflObject(config, store.getInstance(storeOptions));
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Instantiate an OCFL Storage root
|
|
84
|
-
* @param {OcflStorageConfig} config
|
|
85
|
-
* @param {SO} [storeOptions]
|
|
86
|
-
* @return {OcflStorage}
|
|
87
|
-
*/
|
|
88
|
-
storage(config, storeOptions = defaultOptions) {
|
|
89
|
-
return new OcflStorageImpl(config, store.getInstance(storeOptions));
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Create a new OCFL Storage
|
|
94
|
-
* @param {OcflStorageConfig} config
|
|
95
|
-
* @param {SO} [storeOptions]
|
|
96
|
-
* @return {Promise<OcflStorage>}
|
|
97
|
-
*/
|
|
98
|
-
async createStorage(config, storeOptions = defaultOptions) {
|
|
99
|
-
return (new OcflStorageImpl(config, store.getInstance(storeOptions))).create();
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Load an existing OCFL Storage
|
|
104
|
-
* @param {OcflStorageConfig} config
|
|
105
|
-
* @param {SO} [storeOptions]
|
|
106
|
-
* @return {Promise<OcflStorage>}
|
|
107
|
-
*/
|
|
108
|
-
async loadStorage(config, storeOptions = defaultOptions) {
|
|
109
|
-
return (new OcflStorageImpl(config, store.getInstance(storeOptions))).load();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
module.exports = {
|
|
115
|
-
OcflConstants,
|
|
116
|
-
OcflDigest,
|
|
117
|
-
OcflExtension, OcflStorageLayout,
|
|
118
|
-
OcflStore,
|
|
119
|
-
OcflObject,
|
|
120
|
-
OcflObjectInventory,
|
|
121
|
-
OcflObjectTransactionImpl,
|
|
122
|
-
OcflStorage, OcflStorageImpl,
|
|
123
|
-
implementOcfl,
|
|
124
|
-
extensions
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
for (let name in extensions) {
|
|
128
|
-
extensions[name].register();
|
|
129
|
-
}
|
package/index.mjs
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
// export * from './lib/storage.js';
|
|
3
|
-
// export * from './lib/object.js';
|
|
4
|
-
// export * from './lib/digest.js';
|
|
5
|
-
//export * from './lib/extension.js';
|
|
6
|
-
//export * from './lib/constants.js';
|
|
7
|
-
|
|
8
|
-
export * from './index.js';
|
|
9
|
-
import * as ocfl from './index.js';
|
|
10
|
-
export default ocfl;
|
|
11
|
-
//export const { DIGEST, DIGEST_FIXITY, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_PREFIX_STORAGE, OCFL_VERSION } = ocfl
|
|
12
|
-
// export const { OcflObject, OcflObjectInventory, OcflObjectTransaction, OcflObjectTransactionImpl,
|
|
13
|
-
// OcflStorage, OcflStorageImpl,
|
|
14
|
-
// createProxy, digestFrom, digestFromStream,
|
|
15
|
-
// DIGEST, DIGEST_FIXITY, INVENTORY_NAME, NAMASTE_PREFIX_OBJECT, NAMASTE_PREFIX_STORAGE, OCFL_VERSION } = ocfl;
|
|
16
|
-
//export const {} = ocfl;
|
|
17
|
-
//export { default as ocfl } from './index.js';
|
|
18
|
-
//export {Storage, Object} from './index.js';
|
package/lib/constants.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module
|
|
3
|
-
* default constants
|
|
4
|
-
*/
|
|
5
|
-
// module.exports = {
|
|
6
|
-
// OCFL_VERSION: 1.1,
|
|
7
|
-
// NAMASTE_PREFIX_OBJECT: '0=ocfl_object_',
|
|
8
|
-
// NAMASTE_PREFIX_STORAGE: '0=ocfl_',
|
|
9
|
-
// INVENTORY_NAME: 'inventory.json'
|
|
10
|
-
// };
|
|
11
|
-
const OcflConstants = {
|
|
12
|
-
OCFL_VERSION: '1.1',
|
|
13
|
-
OCFL_VERSIONS: Object.freeze(['1.1', '1.0']),
|
|
14
|
-
OCFL_LAYOUT: 'ocfl_layout.json',
|
|
15
|
-
NAMASTE_T: '0=',
|
|
16
|
-
NAMASTE_PREFIX_OBJECT: 'ocfl_object_',
|
|
17
|
-
NAMASTE_PREFIX_STORAGE: 'ocfl_',
|
|
18
|
-
INVENTORY_NAME: 'inventory.json',
|
|
19
|
-
EXTENSIONS_DIR: 'extensions',
|
|
20
|
-
EXTENSION_CONFIG: 'config.json',
|
|
21
|
-
DIGEST_ALGORITHM: 'sha512'
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
module.exports = { OcflConstants };
|
package/lib/digest.js
DELETED
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
//@ts-check
|
|
2
|
-
|
|
3
|
-
const hashWasm = require('hash-wasm');
|
|
4
|
-
const { sha512_256 } = require('js-sha512'); // fallback for sha512/256 algorithm, which is not included in hash-wasm
|
|
5
|
-
//const worker = require('node:worker_threads');
|
|
6
|
-
const { enumeration } = require('./enum.js');
|
|
7
|
-
const { testSymbol } = require('./utils.js');
|
|
8
|
-
|
|
9
|
-
const CONTENT = enumeration(['sha256', 'sha512']);
|
|
10
|
-
const FIXITY = enumeration(['sha256', 'sha512', 'md5', 'sha1', 'blake2b-512', 'blake2b-160', 'blake2b-256', 'blake2b-384', 'sha512/256', 'size', 'crc32']);
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {((outputType: "binary") => Uint8Array) & ((outputType?: "hex") => string)} IDigest
|
|
14
|
-
* @typedef {((outputType: "binary") => {[key:string]: Uint8Array}) & ((outputType?: "hex") => {[key:string]: string})} IMultiDigest
|
|
15
|
-
* @typedef {WritableStream & { update(data: IDataType): void, digest: IDigest }} WritableStreamHasher
|
|
16
|
-
* @typedef {WritableStream & { update(data: IDataType): void, digest: IMultiDigest }} WritableStreamMultiHasher
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @type { Object.<string, CommonHasher[]> }
|
|
21
|
-
*/
|
|
22
|
-
const hasherCache = {};
|
|
23
|
-
/**
|
|
24
|
-
* @type { Object.<string, CommonHasher> }
|
|
25
|
-
*/
|
|
26
|
-
const hasherSyncCache = {};
|
|
27
|
-
// for (let algo of FIXITY) {
|
|
28
|
-
// algorithms[algo] = function () { return crypto.createHash(algo); }
|
|
29
|
-
// }
|
|
30
|
-
|
|
31
|
-
/** @type { WeakMap<CommonHasher, boolean>} */
|
|
32
|
-
const hasherPending = new WeakMap();
|
|
33
|
-
|
|
34
|
-
/** @type { {[key: string]: () => Promise<CommonHasher>} } */
|
|
35
|
-
const hasherFactory = {};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Register a hash algorithm and it's factory function.
|
|
39
|
-
* @param {string} name
|
|
40
|
-
* @param {() => Promise<CommonHasher>} factory
|
|
41
|
-
*/
|
|
42
|
-
function registerHasherFactory(name, factory) {
|
|
43
|
-
if (typeof factory === 'function') {
|
|
44
|
-
hasherFactory[name] = factory;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function hasAlgorithm(name) {
|
|
49
|
-
return name in hasherFactory;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
(function init() {
|
|
53
|
-
for (const algo of FIXITY) {
|
|
54
|
-
if (algo.startsWith('blake2b')) {
|
|
55
|
-
const bits = parseInt(algo.split('-')[1]);
|
|
56
|
-
registerHasherFactory(algo, async () => hashWasm.createBLAKE2b(bits));
|
|
57
|
-
} else if (algo === 'sha512/256') {
|
|
58
|
-
registerHasherFactory(algo, async () => {
|
|
59
|
-
let h = sha512_256.create();
|
|
60
|
-
/** @type {CommonHasher} */
|
|
61
|
-
const hasher = {
|
|
62
|
-
digestSize: 32,
|
|
63
|
-
init() { h = sha512_256.create(); return this; },
|
|
64
|
-
update(data) { h.update(/** @type {any} */(data)); return this },
|
|
65
|
-
// @ts-ignore
|
|
66
|
-
digest(encoding) {
|
|
67
|
-
if (encoding === 'binary') return new Uint8Array(h.digest());
|
|
68
|
-
else return h.hex();
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
return hasher;
|
|
72
|
-
});
|
|
73
|
-
} else if (algo === 'size') {
|
|
74
|
-
registerHasherFactory(algo, async () => {
|
|
75
|
-
let encoder;
|
|
76
|
-
let size = 0;
|
|
77
|
-
/** @type {CommonHasher} */
|
|
78
|
-
const hasher = {
|
|
79
|
-
digestSize: 1,
|
|
80
|
-
init() {
|
|
81
|
-
size = 0;
|
|
82
|
-
encoder = new TextEncoder();
|
|
83
|
-
return this;
|
|
84
|
-
},
|
|
85
|
-
update(data) {
|
|
86
|
-
if (data) {
|
|
87
|
-
if (typeof data === 'string') size += encoder.encode(data).length;
|
|
88
|
-
else size += data.byteLength;
|
|
89
|
-
}
|
|
90
|
-
return this;
|
|
91
|
-
},
|
|
92
|
-
// @ts-ignore
|
|
93
|
-
digest(encoding) {
|
|
94
|
-
if (encoding === 'binary') return new Uint8Array(new BigUint64Array([BigInt(size)]).buffer);
|
|
95
|
-
else return '' + size;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
return hasher;
|
|
99
|
-
});
|
|
100
|
-
} else {
|
|
101
|
-
registerHasherFactory(algo, hashWasm['create' + algo.toUpperCase()]);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
})();
|
|
105
|
-
// (async () => {
|
|
106
|
-
// for (const name in hasherFactory) {
|
|
107
|
-
// const factory = hasherFactory[name];
|
|
108
|
-
// const h = await factory();
|
|
109
|
-
// console.log(name);
|
|
110
|
-
// console.log(h);
|
|
111
|
-
// console.log(h.init().update('test').digest().length)
|
|
112
|
-
// }
|
|
113
|
-
// })();
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
*
|
|
117
|
-
* @param {*} hasher
|
|
118
|
-
* @returns {hasher is MultiHasher}
|
|
119
|
-
*/
|
|
120
|
-
function isMulti(hasher) {
|
|
121
|
-
return hasher.multi;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Create a hasher instance for the given algorithm or get it from cache if available.
|
|
126
|
-
* @param {string} algorithm
|
|
127
|
-
*/
|
|
128
|
-
async function createHasher(algorithm) {
|
|
129
|
-
const cacheArray = hasherCache[algorithm];
|
|
130
|
-
let hash = (cacheArray ?? []).find(hasher => !hasherPending.get(hasher));
|
|
131
|
-
if (!hash) {
|
|
132
|
-
const factory = hasherFactory[algorithm];
|
|
133
|
-
if (!factory) throw new Error(`Unsupported digest algorithm: ${algorithm}`);
|
|
134
|
-
hash = await factory();
|
|
135
|
-
if (cacheArray) cacheArray.push(hash);
|
|
136
|
-
else hasherCache[algorithm] = [hash];
|
|
137
|
-
}
|
|
138
|
-
hasherPending.set(hash, true);
|
|
139
|
-
hash.init();
|
|
140
|
-
return hash;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Create a hasher instance for calculating multiple types of digest in one go.
|
|
145
|
-
* @param {string|string[]} algorithms
|
|
146
|
-
*/
|
|
147
|
-
async function createMultiHasher(algorithms) {
|
|
148
|
-
const algoNames = Array.isArray(algorithms) ? (algorithms.length ? [...(new Set(algorithms))] : ['sha512']) : [algorithms];
|
|
149
|
-
const hashers = await Promise.all(algoNames.map(createHasher));
|
|
150
|
-
return /** @type { MultiHasher } */({
|
|
151
|
-
multi: true,
|
|
152
|
-
update(data) {
|
|
153
|
-
for (const hash of hashers) {
|
|
154
|
-
hash.update(data);
|
|
155
|
-
}
|
|
156
|
-
return this;
|
|
157
|
-
},
|
|
158
|
-
digest(encoding) {
|
|
159
|
-
const result = {};
|
|
160
|
-
for (let i = 0; i < hashers.length; i++) {
|
|
161
|
-
hasherPending.set(hashers[i], false);
|
|
162
|
-
result[algoNames[i]] = hashers[i].digest(encoding);
|
|
163
|
-
}
|
|
164
|
-
return result;
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Create a hash instance that implements WritableStream.
|
|
171
|
-
* @overload
|
|
172
|
-
* @param {string} algorithm One or more digest algorithm names
|
|
173
|
-
* @return {Promise<WritableStreamHasher>}
|
|
174
|
-
*/
|
|
175
|
-
/**
|
|
176
|
-
* @overload
|
|
177
|
-
* @param {string[]} algorithm One or more digest algorithm names
|
|
178
|
-
* @return {Promise<WritableStreamMultiHasher>}
|
|
179
|
-
*/
|
|
180
|
-
async function createStream(algorithm = 'sha512') {
|
|
181
|
-
const hash = await (Array.isArray(algorithm) ? createMultiHasher(algorithm) : createHasher(algorithm));
|
|
182
|
-
/** @type {any} */
|
|
183
|
-
const ws = new WritableStream({
|
|
184
|
-
write(chunk) {
|
|
185
|
-
hash.update(chunk);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
ws.update = function (data) {
|
|
189
|
-
hash.update(data);
|
|
190
|
-
};
|
|
191
|
-
ws.digest = function (encoding) {
|
|
192
|
-
if (!isMulti(hash)) hasherPending.set(hash, false);
|
|
193
|
-
return hash.digest(encoding);
|
|
194
|
-
}
|
|
195
|
-
return ws;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** @typedef {TransformStream & { digest: IMultiDigest }} StreamThroughHash */
|
|
199
|
-
/**
|
|
200
|
-
* Create a pass through stream that pass data as it is and calculate the digest as data pass through.
|
|
201
|
-
* @param {string|string[]} [algorithm]
|
|
202
|
-
* @param {object} [options]
|
|
203
|
-
* @return {Promise<StreamThroughHash>}
|
|
204
|
-
*/
|
|
205
|
-
async function createStreamThrough(algorithm = 'sha512', options) {
|
|
206
|
-
const hash = await (Array.isArray(algorithm) ? createMultiHasher(algorithm) : createHasher(algorithm));
|
|
207
|
-
const { writableStrategy, readableStrategy } = options || {};
|
|
208
|
-
//hash.setEncoding('hex');
|
|
209
|
-
/** @type {any} */
|
|
210
|
-
const ts = new TransformStream({
|
|
211
|
-
transform(chunk, controller) {
|
|
212
|
-
controller.enqueue(chunk);
|
|
213
|
-
hash.update(chunk);
|
|
214
|
-
}
|
|
215
|
-
}, writableStrategy, readableStrategy);
|
|
216
|
-
ts.digest = function (encoding) {
|
|
217
|
-
if (!isMulti(hash)) hasherPending.set(hash, false);
|
|
218
|
-
return hash.digest(encoding);
|
|
219
|
-
}
|
|
220
|
-
return ts;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
*
|
|
225
|
-
* @overload
|
|
226
|
-
* @param {string[]} algorithm
|
|
227
|
-
* @param {IDataType | ReadableStream} input
|
|
228
|
-
* @return {Promise<{[key:string]: string}>}
|
|
229
|
-
*/
|
|
230
|
-
/**
|
|
231
|
-
* @overload
|
|
232
|
-
* @param {string} algorithm
|
|
233
|
-
* @param {IDataType | ReadableStream} input
|
|
234
|
-
* @return {Promise<string>}
|
|
235
|
-
*/
|
|
236
|
-
async function digest(algorithm, input) {
|
|
237
|
-
if (input instanceof ReadableStream) {
|
|
238
|
-
const hash = await createStream(algorithm);
|
|
239
|
-
await input.pipeTo(hash);
|
|
240
|
-
return hash.digest();
|
|
241
|
-
} else {
|
|
242
|
-
const hash = await (Array.isArray(algorithm) ? createMultiHasher(algorithm) : createHasher(algorithm));
|
|
243
|
-
if (!isMulti(hash)) hasherPending.set(hash, false);
|
|
244
|
-
return hash.update(input).digest();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
async function initSync(algorithm) {
|
|
249
|
-
if (!hasherSyncCache[algorithm]) {
|
|
250
|
-
const factory = hasherFactory[algorithm];
|
|
251
|
-
if (!factory) throw new Error(`Unsupported digest algorithm: ${algorithm}`);
|
|
252
|
-
const hasher = await factory();
|
|
253
|
-
hasherSyncCache[algorithm] = hasher;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* For this sync function to work correctly, it requires a call to initSync() to be awaited for each algorithm before calling this function.
|
|
259
|
-
* @param {string} algorithm
|
|
260
|
-
* @param {IDataType} input
|
|
261
|
-
*/
|
|
262
|
-
function digestSync(algorithm, input) {
|
|
263
|
-
const hasher = hasherSyncCache[algorithm];
|
|
264
|
-
if (!hasher) throw new Error(`Hasher for algorithm ${algorithm} is not initialized. Please call and await initSync('${algorithm}') before calling this function.`);
|
|
265
|
-
return hasher.init().update(input).digest();
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const HEX_DIGEST_LENGTH = {
|
|
269
|
-
'md5': 32,
|
|
270
|
-
'sha1': 40,
|
|
271
|
-
'sha256': 64,
|
|
272
|
-
'sha512/256': 64,
|
|
273
|
-
'sha512': 128,
|
|
274
|
-
'blake2b-160': 40,
|
|
275
|
-
'blake2b-256': 64,
|
|
276
|
-
'blake2b-384': 96,
|
|
277
|
-
'blake2b-512': 128,
|
|
278
|
-
'crc32': 8,
|
|
279
|
-
'size': 1
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
function getHexDigestLength(algorithm) {
|
|
283
|
-
return HEX_DIGEST_LENGTH[algorithm] || 0;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const OcflDigest = {
|
|
287
|
-
CONTENT,
|
|
288
|
-
FIXITY,
|
|
289
|
-
// algorithms,
|
|
290
|
-
createStream, createStreamThrough, digest,
|
|
291
|
-
registerHasherFactory, hasAlgorithm, getHexDigestLength,
|
|
292
|
-
digestSync, initSync
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
module.exports = {
|
|
296
|
-
OcflDigest,
|
|
297
|
-
[testSymbol]: {
|
|
298
|
-
hasherCache
|
|
299
|
-
}
|
|
300
|
-
// DIGEST,
|
|
301
|
-
// DIGEST_FIXITY,
|
|
302
|
-
// digestSync,
|
|
303
|
-
// digestAsync,
|
|
304
|
-
// digestFromStream,
|
|
305
|
-
// digestStream,
|
|
306
|
-
// HashThrough
|
|
307
|
-
};
|