@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,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WOLS Error Types
|
|
3
|
+
*
|
|
4
|
+
* This module defines all error classes and error codes
|
|
5
|
+
* used by the WOLS TypeScript library.
|
|
6
|
+
*
|
|
7
|
+
* @module errors
|
|
8
|
+
*/
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// ERROR CODES
|
|
11
|
+
// =============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* WOLS error codes (stable, for programmatic handling).
|
|
14
|
+
*/
|
|
15
|
+
export var WolsErrorCode;
|
|
16
|
+
(function (WolsErrorCode) {
|
|
17
|
+
/** General parse error */
|
|
18
|
+
WolsErrorCode["PARSE_ERROR"] = "WOLS_PARSE_ERROR";
|
|
19
|
+
/** Invalid JSON syntax */
|
|
20
|
+
WolsErrorCode["INVALID_JSON"] = "WOLS_INVALID_JSON";
|
|
21
|
+
/** Not a valid WOLS format */
|
|
22
|
+
WolsErrorCode["INVALID_FORMAT"] = "WOLS_INVALID_FORMAT";
|
|
23
|
+
/** Missing or invalid @context field */
|
|
24
|
+
WolsErrorCode["INVALID_CONTEXT"] = "WOLS_INVALID_CONTEXT";
|
|
25
|
+
/** Missing or invalid @type field */
|
|
26
|
+
WolsErrorCode["INVALID_TYPE"] = "WOLS_INVALID_TYPE";
|
|
27
|
+
/** ID doesn't match wemush:[a-z0-9]+ pattern */
|
|
28
|
+
WolsErrorCode["INVALID_ID_FORMAT"] = "WOLS_INVALID_ID_FORMAT";
|
|
29
|
+
/** Version not valid semver */
|
|
30
|
+
WolsErrorCode["INVALID_VERSION"] = "WOLS_INVALID_VERSION";
|
|
31
|
+
/** Type not in SpecimenType enum */
|
|
32
|
+
WolsErrorCode["INVALID_SPECIMEN_TYPE"] = "WOLS_INVALID_SPECIMEN_TYPE";
|
|
33
|
+
/** Required field is missing */
|
|
34
|
+
WolsErrorCode["REQUIRED_FIELD"] = "WOLS_REQUIRED_FIELD";
|
|
35
|
+
/** Date not in ISO 8601 format */
|
|
36
|
+
WolsErrorCode["INVALID_DATE_FORMAT"] = "WOLS_INVALID_DATE_FORMAT";
|
|
37
|
+
/** Strain generation format is invalid */
|
|
38
|
+
WolsErrorCode["INVALID_GENERATION"] = "WOLS_INVALID_GENERATION";
|
|
39
|
+
/** Invalid compact URL format */
|
|
40
|
+
WolsErrorCode["INVALID_URL"] = "WOLS_INVALID_URL";
|
|
41
|
+
/** Encryption operation failed */
|
|
42
|
+
WolsErrorCode["ENCRYPTION_ERROR"] = "WOLS_ENCRYPTION_ERROR";
|
|
43
|
+
/** Decryption operation failed */
|
|
44
|
+
WolsErrorCode["DECRYPTION_ERROR"] = "WOLS_DECRYPTION_ERROR";
|
|
45
|
+
/** Invalid encryption key */
|
|
46
|
+
WolsErrorCode["INVALID_KEY"] = "WOLS_INVALID_KEY";
|
|
47
|
+
/** Payload exceeds QR code capacity */
|
|
48
|
+
WolsErrorCode["SIZE_EXCEEDED"] = "WOLS_SIZE_EXCEEDED";
|
|
49
|
+
/** Unknown or unspecified error */
|
|
50
|
+
WolsErrorCode["UNKNOWN_ERROR"] = "WOLS_UNKNOWN_ERROR";
|
|
51
|
+
})(WolsErrorCode || (WolsErrorCode = {}));
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// ERROR CLASSES
|
|
54
|
+
// =============================================================================
|
|
55
|
+
/**
|
|
56
|
+
* Base WOLS error class.
|
|
57
|
+
* All WOLS-specific errors extend this class.
|
|
58
|
+
*/
|
|
59
|
+
export class WolsError extends Error {
|
|
60
|
+
/** Error code for programmatic handling */
|
|
61
|
+
code;
|
|
62
|
+
/** Additional error details */
|
|
63
|
+
details;
|
|
64
|
+
constructor(code, message, details) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.name = 'WolsError';
|
|
67
|
+
this.code = code;
|
|
68
|
+
this.details = details ?? undefined;
|
|
69
|
+
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
70
|
+
if (Error.captureStackTrace) {
|
|
71
|
+
Error.captureStackTrace(this, WolsError);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Convert error to JSON for serialization.
|
|
76
|
+
*/
|
|
77
|
+
toJSON() {
|
|
78
|
+
return {
|
|
79
|
+
name: this.name,
|
|
80
|
+
code: this.code,
|
|
81
|
+
message: this.message,
|
|
82
|
+
details: this.details,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Parse error (JSON/format issues).
|
|
88
|
+
* Thrown when parsing JSON or WOLS data fails.
|
|
89
|
+
*/
|
|
90
|
+
export class WolsParseError extends WolsError {
|
|
91
|
+
/** Character position in the input where error occurred */
|
|
92
|
+
position;
|
|
93
|
+
constructor(code, message, details) {
|
|
94
|
+
super(code, message, details);
|
|
95
|
+
this.name = 'WolsParseError';
|
|
96
|
+
this.position = details?.position ?? undefined;
|
|
97
|
+
if (Error.captureStackTrace) {
|
|
98
|
+
Error.captureStackTrace(this, WolsParseError);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validation error (schema violations).
|
|
104
|
+
* Thrown when specimen data fails validation.
|
|
105
|
+
*/
|
|
106
|
+
export class WolsValidationError extends WolsError {
|
|
107
|
+
/** Array of validation errors */
|
|
108
|
+
errors;
|
|
109
|
+
constructor(message, errors) {
|
|
110
|
+
super(WolsErrorCode.INVALID_FORMAT, message, { errors });
|
|
111
|
+
this.name = 'WolsValidationError';
|
|
112
|
+
this.errors = errors;
|
|
113
|
+
if (Error.captureStackTrace) {
|
|
114
|
+
Error.captureStackTrace(this, WolsValidationError);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Encryption/decryption error.
|
|
120
|
+
* Thrown when crypto operations fail.
|
|
121
|
+
*/
|
|
122
|
+
export class WolsEncryptionError extends WolsError {
|
|
123
|
+
constructor(code, message, details) {
|
|
124
|
+
super(code, message, details);
|
|
125
|
+
this.name = 'WolsEncryptionError';
|
|
126
|
+
if (Error.captureStackTrace) {
|
|
127
|
+
Error.captureStackTrace(this, WolsEncryptionError);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// ERROR FACTORY FUNCTIONS
|
|
133
|
+
// =============================================================================
|
|
134
|
+
/**
|
|
135
|
+
* Create a required field error.
|
|
136
|
+
*/
|
|
137
|
+
export function requiredFieldError(field, path = field) {
|
|
138
|
+
return new WolsValidationError(`Required field '${field}' is missing`, [
|
|
139
|
+
{
|
|
140
|
+
path,
|
|
141
|
+
code: WolsErrorCode.REQUIRED_FIELD,
|
|
142
|
+
message: `Field '${field}' is required`,
|
|
143
|
+
},
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Create an invalid format error.
|
|
148
|
+
*/
|
|
149
|
+
export function invalidFormatError(field, expected, path = field) {
|
|
150
|
+
return new WolsValidationError(`Invalid format for '${field}': expected ${expected}`, [
|
|
151
|
+
{
|
|
152
|
+
path,
|
|
153
|
+
code: WolsErrorCode.INVALID_FORMAT,
|
|
154
|
+
message: `Expected ${expected}`,
|
|
155
|
+
},
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Create a parse error from a caught exception.
|
|
160
|
+
*/
|
|
161
|
+
export function parseErrorFromException(error, _input) {
|
|
162
|
+
if (error instanceof SyntaxError) {
|
|
163
|
+
// Extract position from JSON.parse error message
|
|
164
|
+
const match = /position (\d+)/i.exec(error.message);
|
|
165
|
+
const position = match?.[1] ? parseInt(match[1], 10) : undefined;
|
|
166
|
+
const details = { originalError: error.message };
|
|
167
|
+
if (position !== undefined) {
|
|
168
|
+
details['position'] = position;
|
|
169
|
+
}
|
|
170
|
+
return new WolsParseError(WolsErrorCode.INVALID_JSON, error.message, details);
|
|
171
|
+
}
|
|
172
|
+
if (error instanceof WolsParseError) {
|
|
173
|
+
return error;
|
|
174
|
+
}
|
|
175
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
176
|
+
return new WolsParseError(WolsErrorCode.PARSE_ERROR, message);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @wemush/wols - Official WOLS TypeScript Library
|
|
3
|
+
*
|
|
4
|
+
* WeMush Open Labeling Standard (WOLS) v1.1.0 implementation
|
|
5
|
+
* for biological specimen labeling and traceability.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
* @module @wemush/wols
|
|
9
|
+
* @version 1.1.0
|
|
10
|
+
*/
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// =============================================================================
|
|
14
|
+
export { WOLS_VERSION, WOLS_CONTEXT } from './types.js';
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Type utilities
|
|
17
|
+
// =============================================================================
|
|
18
|
+
export { asSpecimenId, isSpecimenType, isGrowthStage, isStrain, SPECIMEN_TYPES, GROWTH_STAGES, } from './types.js';
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Errors
|
|
21
|
+
// =============================================================================
|
|
22
|
+
export { WolsErrorCode, WolsError, WolsParseError, WolsValidationError, WolsEncryptionError, } from './errors.js';
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Core Functions - Specimen
|
|
25
|
+
// =============================================================================
|
|
26
|
+
export { createSpecimen, serializeSpecimen } from './specimen.js';
|
|
27
|
+
// =============================================================================
|
|
28
|
+
// Core Functions - Validation
|
|
29
|
+
// =============================================================================
|
|
30
|
+
export { validateSpecimen } from './validator.js';
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Core Functions - Parser
|
|
33
|
+
// =============================================================================
|
|
34
|
+
export { parseSpecimen } from './parser.js';
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Core Functions - Compact URL
|
|
37
|
+
// =============================================================================
|
|
38
|
+
export { toCompactUrl, parseCompactUrl, SPECIES_CODES, getSpeciesFromCode, getCodeFromSpecies, registerSpeciesCode, } from './compact-url.js';
|
|
39
|
+
// =============================================================================
|
|
40
|
+
// Core Functions - Encryption
|
|
41
|
+
// =============================================================================
|
|
42
|
+
export { encryptSpecimen, isEncrypted, decryptSpecimen } from './crypto/index.js';
|
|
43
|
+
// =============================================================================
|
|
44
|
+
// Core Functions - QR Code Generation
|
|
45
|
+
// =============================================================================
|
|
46
|
+
export { toQRCode, toQRCodeDataURL, toQRCodeSVG, isQRAvailable, isQRAvailableSync, } from './qr/index.js';
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAoCxD,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,QAAQ,EACR,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,gFAAgF;AAChF,SAAS;AACT,gFAAgF;AAChF,OAAO,EACL,aAAa,EACb,SAAS,EACT,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,gFAAgF;AAChF,4BAA4B;AAC5B,gFAAgF;AAChF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElE,gFAAgF;AAChF,8BAA8B;AAC9B,gFAAgF;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,gFAAgF;AAChF,0BAA0B;AAC1B,gFAAgF;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAChF,OAAO,EACL,YAAY,EACZ,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,gFAAgF;AAChF,8BAA8B;AAC9B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAElF,gFAAgF;AAChF,sCAAsC;AACtC,gFAAgF;AAChF,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,GAClB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WOLS Specimen Parser
|
|
3
|
+
*
|
|
4
|
+
* This module provides parsing functionality for WOLS specimen JSON.
|
|
5
|
+
*
|
|
6
|
+
* @module parser
|
|
7
|
+
*/
|
|
8
|
+
import { parseErrorFromException, WolsErrorCode, WolsParseError, WolsValidationError, } from './errors.js';
|
|
9
|
+
import { WOLS_CONTEXT, asSpecimenId, } from './types.js';
|
|
10
|
+
import { validateSpecimen } from './validator.js';
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// HELPER FUNCTIONS
|
|
13
|
+
// =============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Check if a value is a plain object.
|
|
16
|
+
*/
|
|
17
|
+
function isPlainObject(value) {
|
|
18
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the first validation error code from a ValidationResult.
|
|
22
|
+
*/
|
|
23
|
+
function getFirstErrorCode(errors) {
|
|
24
|
+
if (errors.length === 0) {
|
|
25
|
+
return WolsErrorCode.INVALID_FORMAT;
|
|
26
|
+
}
|
|
27
|
+
const firstError = errors[0];
|
|
28
|
+
if (!firstError) {
|
|
29
|
+
return WolsErrorCode.INVALID_FORMAT;
|
|
30
|
+
}
|
|
31
|
+
const code = firstError.code;
|
|
32
|
+
// Map string codes to WolsErrorCode enum
|
|
33
|
+
if (Object.values(WolsErrorCode).includes(code)) {
|
|
34
|
+
return code;
|
|
35
|
+
}
|
|
36
|
+
return WolsErrorCode.INVALID_FORMAT;
|
|
37
|
+
}
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// MAIN PARSE FUNCTION
|
|
40
|
+
// =============================================================================
|
|
41
|
+
/**
|
|
42
|
+
* Parse a JSON string into a validated Specimen object.
|
|
43
|
+
*
|
|
44
|
+
* This function performs both JSON parsing and WOLS validation.
|
|
45
|
+
* If the JSON is invalid or the parsed object doesn't conform to WOLS,
|
|
46
|
+
* it returns a failure result with a detailed error.
|
|
47
|
+
*
|
|
48
|
+
* @param json - The JSON string to parse
|
|
49
|
+
* @returns ParseResult with either the parsed Specimen or an error
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const result = parseSpecimen('{"@context": "https://wemush.com/wols/v1", ...}');
|
|
54
|
+
* if (result.success) {
|
|
55
|
+
* console.log('Specimen ID:', result.data.id);
|
|
56
|
+
* } else {
|
|
57
|
+
* console.error('Parse error:', result.error.message);
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function parseSpecimen(json) {
|
|
62
|
+
// Step 1: Parse JSON
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = JSON.parse(json);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: parseErrorFromException(err, json),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// Step 2: Verify it's an object
|
|
74
|
+
if (!isPlainObject(parsed)) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: new WolsParseError(WolsErrorCode.INVALID_FORMAT, 'Specimen must be a JSON object', {
|
|
78
|
+
receivedType: parsed === null ? 'null' : typeof parsed,
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
// Step 3: Validate against WOLS schema
|
|
83
|
+
const validation = validateSpecimen(parsed);
|
|
84
|
+
if (!validation.valid) {
|
|
85
|
+
// Get the most specific error code
|
|
86
|
+
const errorCode = getFirstErrorCode(validation.errors);
|
|
87
|
+
const firstError = validation.errors[0];
|
|
88
|
+
// Create appropriate error
|
|
89
|
+
const errorMessage = validation.errors.map(e => `${e.path}: ${e.message}`).join('; ');
|
|
90
|
+
if (validation.errors.length === 1 && firstError) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: new WolsParseError(errorCode, firstError.message, {
|
|
94
|
+
path: firstError.path,
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
error: new WolsValidationError(`Validation failed: ${errorMessage}`, validation.errors),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// Step 4: Cast to Specimen type (validation guarantees the shape)
|
|
104
|
+
const data = parsed;
|
|
105
|
+
const specimen = {
|
|
106
|
+
'@context': WOLS_CONTEXT,
|
|
107
|
+
'@type': 'Specimen',
|
|
108
|
+
id: asSpecimenId(data['id']),
|
|
109
|
+
version: data['version'],
|
|
110
|
+
type: data['type'],
|
|
111
|
+
species: data['species'],
|
|
112
|
+
};
|
|
113
|
+
// Copy optional fields if present
|
|
114
|
+
const strainValue = data['strain'];
|
|
115
|
+
if (strainValue !== undefined && strainValue !== null) {
|
|
116
|
+
specimen.strain = strainValue;
|
|
117
|
+
}
|
|
118
|
+
const stageValue = data['stage'];
|
|
119
|
+
if (stageValue !== undefined && stageValue !== null) {
|
|
120
|
+
specimen.stage = stageValue;
|
|
121
|
+
}
|
|
122
|
+
if (data['created'] !== undefined) {
|
|
123
|
+
specimen.created = data['created'];
|
|
124
|
+
}
|
|
125
|
+
if (data['batch'] !== undefined) {
|
|
126
|
+
specimen.batch = data['batch'];
|
|
127
|
+
}
|
|
128
|
+
if (data['organization'] !== undefined) {
|
|
129
|
+
specimen.organization = data['organization'];
|
|
130
|
+
}
|
|
131
|
+
if (data['creator'] !== undefined) {
|
|
132
|
+
specimen.creator = data['creator'];
|
|
133
|
+
}
|
|
134
|
+
if (data['custom'] !== undefined) {
|
|
135
|
+
specimen.custom = data['custom'];
|
|
136
|
+
}
|
|
137
|
+
if (data['signature'] !== undefined) {
|
|
138
|
+
specimen.signature = data['signature'];
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
data: specimen,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QR Code Generation Module
|
|
3
|
+
*
|
|
4
|
+
* This module provides QR code generation functionality for WOLS specimens.
|
|
5
|
+
* Requires the 'qrcode' package as an optional peer dependency.
|
|
6
|
+
*
|
|
7
|
+
* @module qr/generate
|
|
8
|
+
*/
|
|
9
|
+
import { WolsError, WolsErrorCode } from '../errors.js';
|
|
10
|
+
import { serializeSpecimen } from '../specimen.js';
|
|
11
|
+
// Cached reference to the qrcode library
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
let qrLib = null;
|
|
14
|
+
let qrLibLoadAttempted = false;
|
|
15
|
+
/**
|
|
16
|
+
* Dynamically load the qrcode library.
|
|
17
|
+
* Returns null if the library is not installed.
|
|
18
|
+
*/
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
+
async function loadQRLib() {
|
|
21
|
+
if (qrLibLoadAttempted) {
|
|
22
|
+
return qrLib;
|
|
23
|
+
}
|
|
24
|
+
qrLibLoadAttempted = true;
|
|
25
|
+
try {
|
|
26
|
+
// Dynamic import of optional peer dependency
|
|
27
|
+
const module = await import('qrcode');
|
|
28
|
+
qrLib = module.default ?? module;
|
|
29
|
+
return qrLib;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// qrcode package not installed
|
|
33
|
+
qrLib = null;
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Ensure qrcode library is available.
|
|
39
|
+
* Throws an error if not installed.
|
|
40
|
+
*/
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
42
|
+
async function requireQRLib() {
|
|
43
|
+
const lib = await loadQRLib();
|
|
44
|
+
if (!lib) {
|
|
45
|
+
throw new WolsError(WolsErrorCode.UNKNOWN_ERROR, 'QR code generation requires the "qrcode" package. Install it with: npm install qrcode', { missingDependency: 'qrcode' });
|
|
46
|
+
}
|
|
47
|
+
return lib;
|
|
48
|
+
}
|
|
49
|
+
// =============================================================================
|
|
50
|
+
// COMPACT URL FORMAT
|
|
51
|
+
// =============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* Species code mapping for compact URLs.
|
|
54
|
+
*/
|
|
55
|
+
const SPECIES_CODES = {
|
|
56
|
+
'Pleurotus ostreatus': 'POSTR',
|
|
57
|
+
'Pleurotus citrinopileatus': 'PCITRI',
|
|
58
|
+
'Pleurotus djamor': 'PDJAR',
|
|
59
|
+
'Pleurotus eryngii': 'PERYN',
|
|
60
|
+
'Hericium erinaceus': 'HERIN',
|
|
61
|
+
'Ganoderma lucidum': 'GLUCI',
|
|
62
|
+
'Laetiporus pudens': 'LPUDE',
|
|
63
|
+
'Lentinula edodes': 'LLENC',
|
|
64
|
+
'Agrocybe polyphylla': 'APOLY',
|
|
65
|
+
'Stropharia rugosoannulata': 'STRUG',
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Growth stage to short code mapping.
|
|
69
|
+
*/
|
|
70
|
+
const STAGE_CODES = {
|
|
71
|
+
INOCULATION: 'IN',
|
|
72
|
+
COLONIZATION: 'CO',
|
|
73
|
+
FRUITING: 'FR',
|
|
74
|
+
HARVEST: 'HA',
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Convert specimen to compact URL format.
|
|
78
|
+
* Format: wemush://v1/{id}?s={species}&st={stage}&t={timestamp}
|
|
79
|
+
*/
|
|
80
|
+
function toCompactUrl(specimen) {
|
|
81
|
+
// Extract the CUID part from the full ID
|
|
82
|
+
const cuidPart = specimen.id.replace('wemush:', '');
|
|
83
|
+
// Build query params
|
|
84
|
+
const params = new URLSearchParams();
|
|
85
|
+
// Add species code
|
|
86
|
+
const speciesCode = SPECIES_CODES[specimen.species];
|
|
87
|
+
if (speciesCode) {
|
|
88
|
+
params.set('s', speciesCode);
|
|
89
|
+
}
|
|
90
|
+
// Add stage if present
|
|
91
|
+
if (specimen.stage) {
|
|
92
|
+
const stageCode = STAGE_CODES[specimen.stage];
|
|
93
|
+
if (stageCode) {
|
|
94
|
+
params.set('st', stageCode);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Add timestamp if created is present
|
|
98
|
+
if (specimen.created) {
|
|
99
|
+
const timestamp = Math.floor(new Date(specimen.created).getTime() / 1000);
|
|
100
|
+
params.set('t', String(timestamp));
|
|
101
|
+
}
|
|
102
|
+
const queryString = params.toString();
|
|
103
|
+
return `wemush://v1/${cuidPart}${queryString ? '?' + queryString : ''}`;
|
|
104
|
+
}
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// QR CODE GENERATION
|
|
107
|
+
// =============================================================================
|
|
108
|
+
/**
|
|
109
|
+
* Generate QR code content based on format option.
|
|
110
|
+
*/
|
|
111
|
+
function getQRContent(specimen, format) {
|
|
112
|
+
if (format === 'compact') {
|
|
113
|
+
return toCompactUrl(specimen);
|
|
114
|
+
}
|
|
115
|
+
return serializeSpecimen(specimen);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Map QRCodeOptions to library options.
|
|
119
|
+
*/
|
|
120
|
+
function mapOptions(options = {}) {
|
|
121
|
+
const result = {
|
|
122
|
+
errorCorrectionLevel: options.errorCorrection ?? 'M',
|
|
123
|
+
width: options.size ?? 300,
|
|
124
|
+
margin: options.margin ?? 1,
|
|
125
|
+
};
|
|
126
|
+
if (options.color) {
|
|
127
|
+
result.color = options.color;
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// PUBLIC API
|
|
133
|
+
// =============================================================================
|
|
134
|
+
/**
|
|
135
|
+
* Check if QR code generation is available.
|
|
136
|
+
* Returns true if the qrcode library is installed.
|
|
137
|
+
*/
|
|
138
|
+
export async function isQRAvailable() {
|
|
139
|
+
const lib = await loadQRLib();
|
|
140
|
+
return lib !== null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Synchronous check if QR code generation is available.
|
|
144
|
+
* Note: Only accurate after first async call to any QR function.
|
|
145
|
+
*/
|
|
146
|
+
export function isQRAvailableSync() {
|
|
147
|
+
return qrLib !== null;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Generate QR code as PNG buffer.
|
|
151
|
+
*
|
|
152
|
+
* @param specimen - The specimen to encode
|
|
153
|
+
* @param options - QR code generation options
|
|
154
|
+
* @returns PNG buffer containing the QR code
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* const buffer = await toQRCode(specimen, { size: 400 });
|
|
159
|
+
* fs.writeFileSync('specimen-qr.png', buffer);
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export async function toQRCode(specimen, options = {}) {
|
|
163
|
+
const lib = await requireQRLib();
|
|
164
|
+
const content = getQRContent(specimen, options.format ?? 'embedded');
|
|
165
|
+
const qrOptions = mapOptions(options);
|
|
166
|
+
try {
|
|
167
|
+
return await lib.toBuffer(content, { ...qrOptions, type: 'png' });
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
171
|
+
throw new WolsError(WolsErrorCode.SIZE_EXCEEDED, `Failed to generate QR code: ${message}`, {
|
|
172
|
+
originalError: message,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Generate QR code as data URL (base64 encoded PNG).
|
|
178
|
+
*
|
|
179
|
+
* @param specimen - The specimen to encode
|
|
180
|
+
* @param options - QR code generation options
|
|
181
|
+
* @returns Data URL string (data:image/png;base64,...)
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const dataUrl = await toQRCodeDataURL(specimen);
|
|
186
|
+
* // Use in HTML: <img src={dataUrl} />
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export async function toQRCodeDataURL(specimen, options = {}) {
|
|
190
|
+
const lib = await requireQRLib();
|
|
191
|
+
const content = getQRContent(specimen, options.format ?? 'embedded');
|
|
192
|
+
const qrOptions = mapOptions(options);
|
|
193
|
+
try {
|
|
194
|
+
return await lib.toDataURL(content, qrOptions);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
198
|
+
throw new WolsError(WolsErrorCode.SIZE_EXCEEDED, `Failed to generate QR code: ${message}`, {
|
|
199
|
+
originalError: message,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Generate QR code as SVG string.
|
|
205
|
+
*
|
|
206
|
+
* @param specimen - The specimen to encode
|
|
207
|
+
* @param options - QR code generation options
|
|
208
|
+
* @returns SVG string
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const svg = await toQRCodeSVG(specimen, { color: { dark: '#000', light: '#fff' } });
|
|
213
|
+
* fs.writeFileSync('specimen-qr.svg', svg);
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
export async function toQRCodeSVG(specimen, options = {}) {
|
|
217
|
+
const lib = await requireQRLib();
|
|
218
|
+
const content = getQRContent(specimen, options.format ?? 'embedded');
|
|
219
|
+
const qrOptions = mapOptions(options);
|
|
220
|
+
try {
|
|
221
|
+
const svg = await lib.toString(content, { ...qrOptions, type: 'svg' });
|
|
222
|
+
// Ensure the SVG has proper dimensions based on size option
|
|
223
|
+
const size = options.size ?? 300;
|
|
224
|
+
return svg
|
|
225
|
+
.replace(/width="[^"]*"/, `width="${size}"`)
|
|
226
|
+
.replace(/height="[^"]*"/, `height="${size}"`);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
230
|
+
throw new WolsError(WolsErrorCode.SIZE_EXCEEDED, `Failed to generate QR code: ${message}`, {
|
|
231
|
+
originalError: message,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/qr/generate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAqBnD,yCAAyC;AACzC,8DAA8D;AAC9D,IAAI,KAAK,GAAQ,IAAI,CAAC;AACtB,IAAI,kBAAkB,GAAG,KAAK,CAAC;AAE/B;;;GAGG;AACH,8DAA8D;AAC9D,KAAK,UAAU,SAAS;IACtB,IAAI,kBAAkB,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,kBAAkB,GAAG,IAAI,CAAC;IAE1B,IAAI,CAAC;QACH,6CAA6C;QAC7C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACtC,KAAK,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;QAC/B,KAAK,GAAG,IAAI,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,8DAA8D;AAC9D,KAAK,UAAU,YAAY;IACzB,MAAM,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;IAE9B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,SAAS,CACjB,aAAa,CAAC,aAAa,EAC3B,uFAAuF,EACvF,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAChC,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF;;GAEG;AACH,MAAM,aAAa,GAA2B;IAC5C,qBAAqB,EAAE,OAAO;IAC9B,2BAA2B,EAAE,QAAQ;IACrC,kBAAkB,EAAE,OAAO;IAC3B,mBAAmB,EAAE,OAAO;IAC5B,oBAAoB,EAAE,OAAO;IAC7B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,kBAAkB,EAAE,OAAO;IAC3B,qBAAqB,EAAE,OAAO;IAC9B,2BAA2B,EAAE,OAAO;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,GAA2B;IAC1C,WAAW,EAAE,IAAI;IACjB,YAAY,EAAE,IAAI;IAClB,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI;CACd,CAAC;AAEF;;;GAGG;AACH,SAAS,YAAY,CAAC,QAAkB;IACtC,yCAAyC;IACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAEpD,qBAAqB;IACrB,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IAErC,mBAAmB;IACnB,MAAM,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC/B,CAAC;IAED,uBAAuB;IACvB,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1E,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IACtC,OAAO,eAAe,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC1E,CAAC;AAED,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF;;GAEG;AACH,SAAS,YAAY,CAAC,QAAkB,EAAE,MAA8B;IACtE,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAAC,UAAyB,EAAE;IAC7C,MAAM,MAAM,GAAkB;QAC5B,oBAAoB,EAAE,OAAO,CAAC,eAAe,IAAI,GAAG;QACpD,KAAK,EAAE,OAAO,CAAC,IAAI,IAAI,GAAG;QAC1B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC;KAC5B,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;IAC/B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;IAC9B,OAAO,GAAG,KAAK,IAAI,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,KAAK,KAAK,IAAI,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,QAAkB,EAAE,UAAyB,EAAE;IAC5E,MAAM,GAAG,GAAG,MAAM,YAAY,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,SAAS,CAAC,aAAa,CAAC,aAAa,EAAE,+BAA+B,OAAO,EAAE,EAAE;YACzF,aAAa,EAAE,OAAO;SACvB,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAkB,EAClB,UAAyB,EAAE;IAE3B,MAAM,GAAG,GAAG,MAAM,YAAY,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,SAAS,CAAC,aAAa,CAAC,aAAa,EAAE,+BAA+B,OAAO,EAAE,EAAE;YACzF,aAAa,EAAE,OAAO;SACvB,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAkB,EAClB,UAAyB,EAAE;IAE3B,MAAM,GAAG,GAAG,MAAM,YAAY,EAAE,CAAC;IACjC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,CAAC;IACrE,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAEtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAEvE,4DAA4D;QAC5D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC;QACjC,OAAO,GAAG;aACP,OAAO,CAAC,eAAe,EAAE,UAAU,IAAI,GAAG,CAAC;aAC3C,OAAO,CAAC,gBAAgB,EAAE,WAAW,IAAI,GAAG,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,SAAS,CAAC,aAAa,CAAC,aAAa,EAAE,+BAA+B,OAAO,EAAE,EAAE;YACzF,aAAa,EAAE,OAAO;SACvB,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QR Code Submodule
|
|
3
|
+
*
|
|
4
|
+
* This module provides QR code generation functionality for WOLS specimens.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
* @module @wemush/wols/qr
|
|
8
|
+
*/
|
|
9
|
+
export { toQRCode, toQRCodeDataURL, toQRCodeSVG, isQRAvailable, isQRAvailableSync, } from './generate.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/qr/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,iBAAiB,GAClB,MAAM,eAAe,CAAC"}
|