@wemush/wols 0.1.0
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 +201 -0
- package/README.md +294 -0
- package/dist/cjs/compact-url.js +272 -0
- package/dist/cjs/compact-url.js.map +1 -0
- package/dist/cjs/crypto/decrypt.js +125 -0
- package/dist/cjs/crypto/decrypt.js.map +1 -0
- package/dist/cjs/crypto/encrypt.js +151 -0
- package/dist/cjs/crypto/encrypt.js.map +1 -0
- package/dist/cjs/crypto/index.js +16 -0
- package/dist/cjs/crypto/index.js.map +1 -0
- package/dist/cjs/errors.js +188 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.js +81 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/parser.js +148 -0
- package/dist/cjs/parser.js.map +1 -0
- package/dist/cjs/qr/generate.js +275 -0
- package/dist/cjs/qr/generate.js.map +1 -0
- package/dist/cjs/qr/index.js +18 -0
- package/dist/cjs/qr/index.js.map +1 -0
- package/dist/cjs/specimen.js +163 -0
- package/dist/cjs/specimen.js.map +1 -0
- package/dist/cjs/types.js +78 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/cuid.js +150 -0
- package/dist/cjs/utils/cuid.js.map +1 -0
- package/dist/cjs/utils/iso8601.js +131 -0
- package/dist/cjs/utils/iso8601.js.map +1 -0
- package/dist/cjs/validator.js +307 -0
- package/dist/cjs/validator.js.map +1 -0
- package/dist/esm/compact-url.js +264 -0
- package/dist/esm/compact-url.js.map +1 -0
- package/dist/esm/crypto/decrypt.js +122 -0
- package/dist/esm/crypto/decrypt.js.map +1 -0
- package/dist/esm/crypto/encrypt.js +147 -0
- package/dist/esm/crypto/encrypt.js.map +1 -0
- package/dist/esm/crypto/index.js +10 -0
- package/dist/esm/crypto/index.js.map +1 -0
- package/dist/esm/errors.js +178 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.js +47 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/parser.js +145 -0
- package/dist/esm/parser.js.map +1 -0
- package/dist/esm/qr/generate.js +235 -0
- package/dist/esm/qr/generate.js.map +1 -0
- package/dist/esm/qr/index.js +10 -0
- package/dist/esm/qr/index.js.map +1 -0
- package/dist/esm/specimen.js +159 -0
- package/dist/esm/specimen.js.map +1 -0
- package/dist/esm/types.js +71 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/cuid.js +145 -0
- package/dist/esm/utils/cuid.js.map +1 -0
- package/dist/esm/utils/iso8601.js +122 -0
- package/dist/esm/utils/iso8601.js.map +1 -0
- package/dist/esm/validator.js +304 -0
- package/dist/esm/validator.js.map +1 -0
- package/dist/types/compact-url.d.ts +104 -0
- package/dist/types/compact-url.d.ts.map +1 -0
- package/dist/types/crypto/decrypt.d.ts +33 -0
- package/dist/types/crypto/decrypt.d.ts.map +1 -0
- package/dist/types/crypto/encrypt.d.ts +32 -0
- package/dist/types/crypto/encrypt.d.ts.map +1 -0
- package/dist/types/crypto/index.d.ts +10 -0
- package/dist/types/crypto/index.d.ts.map +1 -0
- package/dist/types/errors.d.ts +103 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/parser.d.ts +30 -0
- package/dist/types/parser.d.ts.map +1 -0
- package/dist/types/qr/generate.d.ts +62 -0
- package/dist/types/qr/generate.d.ts.map +1 -0
- package/dist/types/qr/index.d.ts +10 -0
- package/dist/types/qr/index.d.ts.map +1 -0
- package/dist/types/specimen.d.ts +71 -0
- package/dist/types/specimen.d.ts.map +1 -0
- package/dist/types/types.d.ts +269 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils/cuid.d.ts +44 -0
- package/dist/types/utils/cuid.d.ts.map +1 -0
- package/dist/types/utils/iso8601.d.ts +79 -0
- package/dist/types/utils/iso8601.d.ts.map +1 -0
- package/dist/types/validator.d.ts +25 -0
- package/dist/types/validator.d.ts.map +1 -0
- package/package.json +95 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specimen Creation and Serialization
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions to create and serialize
|
|
5
|
+
* WOLS v1.1.0 compliant specimen objects.
|
|
6
|
+
*
|
|
7
|
+
* @module specimen
|
|
8
|
+
*/
|
|
9
|
+
import { WOLS_CONTEXT, WOLS_VERSION, asSpecimenId, isStrain } from './types.js';
|
|
10
|
+
import { createSpecimenId } from './utils/cuid.js';
|
|
11
|
+
/**
|
|
12
|
+
* Expand a strain input to a full Strain object.
|
|
13
|
+
* String inputs are expanded to { name: string }.
|
|
14
|
+
*
|
|
15
|
+
* @param strain - String or Strain object input
|
|
16
|
+
* @returns Strain object or undefined
|
|
17
|
+
*/
|
|
18
|
+
function expandStrain(strain) {
|
|
19
|
+
if (strain === undefined) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
if (typeof strain === 'string') {
|
|
23
|
+
return { name: strain };
|
|
24
|
+
}
|
|
25
|
+
if (isStrain(strain)) {
|
|
26
|
+
return strain;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a new specimen with auto-generated ID and JSON-LD fields.
|
|
32
|
+
*
|
|
33
|
+
* This function creates a fully-formed WOLS v1.1.0 compliant Specimen
|
|
34
|
+
* with automatic ID generation, version assignment, and JSON-LD context.
|
|
35
|
+
*
|
|
36
|
+
* @param input - Specimen creation parameters
|
|
37
|
+
* @returns Fully-formed Specimen with JSON-LD fields
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Minimal specimen
|
|
42
|
+
* const culture = createSpecimen({
|
|
43
|
+
* type: 'CULTURE',
|
|
44
|
+
* species: 'Pleurotus ostreatus',
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* // Full specimen with strain info
|
|
48
|
+
* const substrate = createSpecimen({
|
|
49
|
+
* type: 'SUBSTRATE',
|
|
50
|
+
* species: 'Hericium erinaceus',
|
|
51
|
+
* strain: {
|
|
52
|
+
* name: "Lion's Mane Premium",
|
|
53
|
+
* generation: 'F2',
|
|
54
|
+
* clonalGeneration: 3,
|
|
55
|
+
* },
|
|
56
|
+
* stage: 'COLONIZATION',
|
|
57
|
+
* batch: 'batch_2025_12_001',
|
|
58
|
+
* organization: 'org_mushoh',
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* // String strain shorthand
|
|
62
|
+
* const spawn = createSpecimen({
|
|
63
|
+
* type: 'SPAWN',
|
|
64
|
+
* species: 'Ganoderma lucidum',
|
|
65
|
+
* strain: 'Reishi Red', // Auto-expands to { name: 'Reishi Red' }
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function createSpecimen(input) {
|
|
70
|
+
const id = asSpecimenId(createSpecimenId());
|
|
71
|
+
const strain = expandStrain(input.strain);
|
|
72
|
+
// Build specimen with required fields
|
|
73
|
+
const specimen = {
|
|
74
|
+
'@context': WOLS_CONTEXT,
|
|
75
|
+
'@type': 'Specimen',
|
|
76
|
+
id,
|
|
77
|
+
version: WOLS_VERSION,
|
|
78
|
+
type: input.type,
|
|
79
|
+
species: input.species,
|
|
80
|
+
};
|
|
81
|
+
// Add optional fields only if defined
|
|
82
|
+
if (strain !== undefined) {
|
|
83
|
+
specimen.strain = strain;
|
|
84
|
+
}
|
|
85
|
+
if (input.stage !== undefined) {
|
|
86
|
+
specimen.stage = input.stage;
|
|
87
|
+
}
|
|
88
|
+
if (input.batch !== undefined) {
|
|
89
|
+
specimen.batch = input.batch;
|
|
90
|
+
}
|
|
91
|
+
if (input.organization !== undefined) {
|
|
92
|
+
specimen.organization = input.organization;
|
|
93
|
+
}
|
|
94
|
+
if (input.creator !== undefined) {
|
|
95
|
+
specimen.creator = input.creator;
|
|
96
|
+
}
|
|
97
|
+
if (input.custom !== undefined) {
|
|
98
|
+
specimen.custom = input.custom;
|
|
99
|
+
}
|
|
100
|
+
return specimen;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Serialize a specimen to JSON-LD string.
|
|
104
|
+
*
|
|
105
|
+
* The output is formatted with JSON-LD fields (@context, @type) first,
|
|
106
|
+
* followed by id, version, and remaining fields. Undefined fields are omitted.
|
|
107
|
+
*
|
|
108
|
+
* @param specimen - Specimen to serialize
|
|
109
|
+
* @returns JSON string suitable for QR encoding
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const specimen = createSpecimen({
|
|
114
|
+
* type: 'CULTURE',
|
|
115
|
+
* species: 'Pleurotus ostreatus',
|
|
116
|
+
* });
|
|
117
|
+
*
|
|
118
|
+
* const json = serializeSpecimen(specimen);
|
|
119
|
+
* // Returns: '{"@context":"https://wemush.com/wols/v1","@type":"Specimen",...}'
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export function serializeSpecimen(specimen) {
|
|
123
|
+
// Build ordered object with JSON-LD fields first
|
|
124
|
+
const ordered = {
|
|
125
|
+
'@context': specimen['@context'],
|
|
126
|
+
'@type': specimen['@type'],
|
|
127
|
+
id: specimen.id,
|
|
128
|
+
version: specimen.version,
|
|
129
|
+
type: specimen.type,
|
|
130
|
+
species: specimen.species,
|
|
131
|
+
};
|
|
132
|
+
// Add optional fields in order, only if defined
|
|
133
|
+
if (specimen.strain !== undefined) {
|
|
134
|
+
ordered['strain'] = specimen.strain;
|
|
135
|
+
}
|
|
136
|
+
if (specimen.stage !== undefined) {
|
|
137
|
+
ordered['stage'] = specimen.stage;
|
|
138
|
+
}
|
|
139
|
+
if (specimen.created !== undefined) {
|
|
140
|
+
ordered['created'] = specimen.created;
|
|
141
|
+
}
|
|
142
|
+
if (specimen.batch !== undefined) {
|
|
143
|
+
ordered['batch'] = specimen.batch;
|
|
144
|
+
}
|
|
145
|
+
if (specimen.organization !== undefined) {
|
|
146
|
+
ordered['organization'] = specimen.organization;
|
|
147
|
+
}
|
|
148
|
+
if (specimen.creator !== undefined) {
|
|
149
|
+
ordered['creator'] = specimen.creator;
|
|
150
|
+
}
|
|
151
|
+
if (specimen.custom !== undefined) {
|
|
152
|
+
ordered['custom'] = specimen.custom;
|
|
153
|
+
}
|
|
154
|
+
if (specimen.signature !== undefined) {
|
|
155
|
+
ordered['signature'] = specimen.signature;
|
|
156
|
+
}
|
|
157
|
+
return JSON.stringify(ordered);
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=specimen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"specimen.js","sourceRoot":"","sources":["../../src/specimen.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WOLS TypeScript Library - Core Types
|
|
3
|
+
*
|
|
4
|
+
* This module defines all the core types, interfaces, and enums
|
|
5
|
+
* for the WeMush Open Labeling Standard (WOLS) v1.1.0.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
* @module @wemush/wols
|
|
9
|
+
*/
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// CONSTANTS
|
|
12
|
+
// =============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* WOLS specification version implemented by this library.
|
|
15
|
+
*/
|
|
16
|
+
export const WOLS_VERSION = '1.1.0';
|
|
17
|
+
/**
|
|
18
|
+
* JSON-LD context URL for WOLS specimens.
|
|
19
|
+
*/
|
|
20
|
+
export const WOLS_CONTEXT = 'https://wemush.com/wols/v1';
|
|
21
|
+
/**
|
|
22
|
+
* Create a SpecimenId from a string.
|
|
23
|
+
* Does not validate the format - use isValidSpecimenId for validation.
|
|
24
|
+
*/
|
|
25
|
+
export function asSpecimenId(id) {
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Array of valid specimen types for validation.
|
|
30
|
+
*/
|
|
31
|
+
export const SPECIMEN_TYPES = [
|
|
32
|
+
'CULTURE',
|
|
33
|
+
'SPAWN',
|
|
34
|
+
'SUBSTRATE',
|
|
35
|
+
'FRUITING',
|
|
36
|
+
'HARVEST',
|
|
37
|
+
];
|
|
38
|
+
/**
|
|
39
|
+
* Array of valid growth stages for validation.
|
|
40
|
+
*/
|
|
41
|
+
export const GROWTH_STAGES = [
|
|
42
|
+
'INOCULATION',
|
|
43
|
+
'COLONIZATION',
|
|
44
|
+
'FRUITING',
|
|
45
|
+
'HARVEST',
|
|
46
|
+
];
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// TYPE GUARDS
|
|
49
|
+
// =============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Type guard to check if a value is a valid SpecimenType.
|
|
52
|
+
*/
|
|
53
|
+
export function isSpecimenType(value) {
|
|
54
|
+
return typeof value === 'string' && SPECIMEN_TYPES.includes(value);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Type guard to check if a value is a valid GrowthStage.
|
|
58
|
+
*/
|
|
59
|
+
export function isGrowthStage(value) {
|
|
60
|
+
return typeof value === 'string' && GROWTH_STAGES.includes(value);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Type guard to check if a value is a Strain object.
|
|
64
|
+
*/
|
|
65
|
+
export function isStrain(value) {
|
|
66
|
+
return (typeof value === 'object' &&
|
|
67
|
+
value !== null &&
|
|
68
|
+
'name' in value &&
|
|
69
|
+
typeof value.name === 'string');
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAkBzD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAO,EAAgB,CAAC;AAC1B,CAAC;AAWD;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAA4B;IACrD,SAAS;IACT,OAAO;IACP,WAAW;IACX,UAAU;IACV,SAAS;CACD,CAAC;AAOX;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,aAAa;IACb,cAAc;IACd,UAAU;IACV,SAAS;CACD,CAAC;AAgPX,gFAAgF;AAChF,cAAc;AACd,gFAAgF;AAEhF;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAAC,KAAqB,CAAC,CAAC;AACrF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,aAAa,CAAC,QAAQ,CAAC,KAAoB,CAAC,CAAC;AACnF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,OAAQ,KAAgB,CAAC,IAAI,KAAK,QAAQ,CAC3C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CUID2 Generator - Zero-dependency implementation
|
|
3
|
+
*
|
|
4
|
+
* Generates collision-resistant unique identifiers following the CUID2 algorithm.
|
|
5
|
+
* This is a minimal implementation focused on the needs of WOLS specimen IDs.
|
|
6
|
+
*
|
|
7
|
+
* @module utils/cuid
|
|
8
|
+
*/
|
|
9
|
+
// Character set for base36 encoding (0-9, a-z)
|
|
10
|
+
const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
|
|
11
|
+
// Default length for generated IDs
|
|
12
|
+
const DEFAULT_LENGTH = 24;
|
|
13
|
+
// Counter for additional entropy
|
|
14
|
+
let counter = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Get cryptographically secure random bytes.
|
|
17
|
+
* Uses Web Crypto API (works in Node.js 18+ and modern browsers).
|
|
18
|
+
*/
|
|
19
|
+
function getRandomBytes(length) {
|
|
20
|
+
const bytes = new Uint8Array(length);
|
|
21
|
+
// Use globalThis.crypto for cross-platform compatibility
|
|
22
|
+
const cryptoObj = typeof globalThis !== 'undefined'
|
|
23
|
+
? globalThis.crypto
|
|
24
|
+
: undefined;
|
|
25
|
+
if (cryptoObj?.getRandomValues) {
|
|
26
|
+
cryptoObj.getRandomValues(bytes);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Fallback for older environments (should not happen with Node 18+)
|
|
30
|
+
for (let i = 0; i < length; i++) {
|
|
31
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return bytes;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert a byte array to a base36 string.
|
|
38
|
+
*/
|
|
39
|
+
function toBase36(bytes) {
|
|
40
|
+
let result = '';
|
|
41
|
+
for (const byte of bytes) {
|
|
42
|
+
result += ALPHABET[byte % 36];
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get a timestamp component for the ID.
|
|
48
|
+
* Uses high-resolution time when available.
|
|
49
|
+
*/
|
|
50
|
+
function getTimestamp() {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
return now.toString(36);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get counter component to ensure uniqueness within same millisecond.
|
|
56
|
+
*/
|
|
57
|
+
function getCounter() {
|
|
58
|
+
counter = (counter + 1) % 1679616; // Max 4 base36 chars
|
|
59
|
+
return counter.toString(36).padStart(4, '0');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate a fingerprint based on environment.
|
|
63
|
+
* In browsers, uses navigator info. In Node.js, uses process info.
|
|
64
|
+
*/
|
|
65
|
+
function getFingerprint() {
|
|
66
|
+
const parts = [];
|
|
67
|
+
// Node.js environment - use globalThis for cross-platform compatibility
|
|
68
|
+
const processObj = typeof globalThis !== 'undefined'
|
|
69
|
+
? globalThis.process
|
|
70
|
+
: undefined;
|
|
71
|
+
const navigatorObj = typeof globalThis !== 'undefined'
|
|
72
|
+
? globalThis.navigator
|
|
73
|
+
: undefined;
|
|
74
|
+
if (processObj?.pid) {
|
|
75
|
+
parts.push(processObj.pid.toString(36));
|
|
76
|
+
const hrtime = processObj.hrtime?.();
|
|
77
|
+
if (hrtime) {
|
|
78
|
+
parts.push((hrtime[1] ?? 0).toString(36));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Browser environment
|
|
82
|
+
if (navigatorObj) {
|
|
83
|
+
parts.push((navigatorObj.userAgent?.length ?? 0).toString(36));
|
|
84
|
+
parts.push((navigatorObj.language?.length ?? 0).toString(36));
|
|
85
|
+
}
|
|
86
|
+
// Fallback random
|
|
87
|
+
if (parts.length === 0) {
|
|
88
|
+
const random = getRandomBytes(4);
|
|
89
|
+
parts.push(toBase36(random));
|
|
90
|
+
}
|
|
91
|
+
return parts.join('').slice(0, 4);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Generate a CUID2-style identifier.
|
|
95
|
+
*
|
|
96
|
+
* The ID format is: [random][timestamp][counter][fingerprint][random]
|
|
97
|
+
* All components are base36 encoded for URL safety.
|
|
98
|
+
*
|
|
99
|
+
* @param length - Length of the generated ID (default: 24)
|
|
100
|
+
* @returns A unique, URL-safe identifier
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* const id = createId();
|
|
105
|
+
* // Returns something like: "clx1a2b3c4d5e6f7g8h9i0j1"
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function createId(length = DEFAULT_LENGTH) {
|
|
109
|
+
const timestamp = getTimestamp();
|
|
110
|
+
const count = getCounter();
|
|
111
|
+
const fingerprint = getFingerprint();
|
|
112
|
+
// Calculate random bytes needed
|
|
113
|
+
const fixedLength = timestamp.length + count.length + fingerprint.length;
|
|
114
|
+
const randomLength = Math.max(0, length - fixedLength);
|
|
115
|
+
// Generate random component
|
|
116
|
+
const randomBytes = getRandomBytes(randomLength);
|
|
117
|
+
const random = toBase36(randomBytes);
|
|
118
|
+
// Combine components
|
|
119
|
+
const id = (random + timestamp + count + fingerprint).slice(0, length);
|
|
120
|
+
return id;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Generate a specimen ID with the WOLS prefix.
|
|
124
|
+
*
|
|
125
|
+
* @returns A specimen ID in format "wemush:{cuid}"
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* const specimenId = createSpecimenId();
|
|
130
|
+
* // Returns: "wemush:clx1a2b3c4d5e6f7g8h9i0j1"
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export function createSpecimenId() {
|
|
134
|
+
return `wemush:${createId()}`;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Validate a specimen ID format.
|
|
138
|
+
*
|
|
139
|
+
* @param id - The ID to validate
|
|
140
|
+
* @returns true if the ID matches the expected format
|
|
141
|
+
*/
|
|
142
|
+
export function isValidSpecimenId(id) {
|
|
143
|
+
return /^wemush:[a-z0-9]+$/.test(id);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=cuid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cuid.js","sourceRoot":"","sources":["../../../src/utils/cuid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ISO 8601 Date Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides parsing and formatting for ISO 8601 date strings
|
|
5
|
+
* as used in WOLS specimens.
|
|
6
|
+
*
|
|
7
|
+
* @module utils/iso8601
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Regular expression for validating ISO 8601 date strings.
|
|
11
|
+
* Matches formats like:
|
|
12
|
+
* - 2025-12-16T10:30:00Z
|
|
13
|
+
* - 2025-12-16T10:30:00.000Z
|
|
14
|
+
* - 2025-12-16T10:30:00+00:00
|
|
15
|
+
* - 2025-12-16T10:30:00-05:00
|
|
16
|
+
*/
|
|
17
|
+
const ISO8601_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
18
|
+
/**
|
|
19
|
+
* Simple date-only regex for partial dates
|
|
20
|
+
*/
|
|
21
|
+
const DATE_ONLY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
22
|
+
/**
|
|
23
|
+
* Check if a string is a valid ISO 8601 date.
|
|
24
|
+
*
|
|
25
|
+
* @param value - The string to validate
|
|
26
|
+
* @returns true if valid ISO 8601 format
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* isValidISO8601('2025-12-16T10:30:00Z'); // true
|
|
31
|
+
* isValidISO8601('2025-12-16'); // false (date-only)
|
|
32
|
+
* isValidISO8601('invalid'); // false
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function isValidISO8601(value) {
|
|
36
|
+
if (!ISO8601_REGEX.test(value)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
// Also verify it's a parseable date
|
|
40
|
+
const date = new Date(value);
|
|
41
|
+
return !isNaN(date.getTime());
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if a string is a valid date-only format (YYYY-MM-DD).
|
|
45
|
+
*
|
|
46
|
+
* @param value - The string to validate
|
|
47
|
+
* @returns true if valid date-only format
|
|
48
|
+
*/
|
|
49
|
+
export function isValidDateOnly(value) {
|
|
50
|
+
if (!DATE_ONLY_REGEX.test(value)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const date = new Date(value + 'T00:00:00Z');
|
|
54
|
+
return !isNaN(date.getTime());
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the current timestamp in ISO 8601 format.
|
|
58
|
+
*
|
|
59
|
+
* @returns Current time as ISO 8601 string
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const now = getCurrentISO8601();
|
|
64
|
+
* // Returns: "2025-12-16T10:30:00.000Z"
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function getCurrentISO8601() {
|
|
68
|
+
return new Date().toISOString();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse an ISO 8601 date string to a Date object.
|
|
72
|
+
*
|
|
73
|
+
* @param value - The ISO 8601 string to parse
|
|
74
|
+
* @returns The parsed Date, or null if invalid
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const date = parseISO8601('2025-12-16T10:30:00Z');
|
|
79
|
+
* // Returns: Date object
|
|
80
|
+
*
|
|
81
|
+
* const invalid = parseISO8601('invalid');
|
|
82
|
+
* // Returns: null
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function parseISO8601(value) {
|
|
86
|
+
if (!isValidISO8601(value)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return new Date(value);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Format a Date object to ISO 8601 string.
|
|
93
|
+
*
|
|
94
|
+
* @param date - The Date to format
|
|
95
|
+
* @returns ISO 8601 string
|
|
96
|
+
*/
|
|
97
|
+
export function formatISO8601(date) {
|
|
98
|
+
return date.toISOString();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get Unix timestamp (seconds since epoch) from ISO 8601 string.
|
|
102
|
+
*
|
|
103
|
+
* @param value - The ISO 8601 string
|
|
104
|
+
* @returns Unix timestamp in seconds, or null if invalid
|
|
105
|
+
*/
|
|
106
|
+
export function toUnixTimestamp(value) {
|
|
107
|
+
const date = parseISO8601(value);
|
|
108
|
+
if (!date) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return Math.floor(date.getTime() / 1000);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Convert Unix timestamp to ISO 8601 string.
|
|
115
|
+
*
|
|
116
|
+
* @param timestamp - Unix timestamp in seconds
|
|
117
|
+
* @returns ISO 8601 string
|
|
118
|
+
*/
|
|
119
|
+
export function fromUnixTimestamp(timestamp) {
|
|
120
|
+
return new Date(timestamp * 1000).toISOString();
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=iso8601.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iso8601.js","sourceRoot":"","sources":["../../../src/utils/iso8601.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,0EAA0E,CAAC;AAEjG;;GAEG;AACH,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAE9C;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oCAAoC;IACpC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC,CAAC;IAC5C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAU;IACtC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,OAAO,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;AAClD,CAAC"}
|