@majikah/majik-signature 0.2.6 → 0.2.7

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.
@@ -26,6 +26,7 @@ import { MajikSignatureEnvelope } from "../../core/envelope";
26
26
  import { FormatHandlerRegistry } from "./registry";
27
27
  import { MajikChainAnchor } from "../../anchor/types";
28
28
  import { MajikSignatureMap } from "../mjksmap";
29
+ import { SignatureOrderResult, VerifySignatureOrderOptions } from "../order";
29
30
  export interface MajikSignatureAdapter {
30
31
  toJSON(): MajikSignatureJSON;
31
32
  /**
@@ -178,6 +179,18 @@ export declare class MajikSignatureEmbed {
178
179
  * without the caller re-deriving it from the array each time.
179
180
  */
180
181
  static summarizeBatchVerification(results: FileVerifyResult[]): BatchVerifySummary;
182
+ /**
183
+ * Verify the chronological signing order of a file's embedded envelope
184
+ * against an expected sequence of signers.
185
+ * expectedOrder accepts MajikKey instances and/or ExpectedSigner objects,
186
+ * mixed freely — normalized internally.
187
+ */
188
+ static verifyFileOrder(file: Blob, expectedOrder: readonly (MajikKey | ExpectedSigner)[], MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & VerifySignatureOrderOptions): Promise<SignatureOrderResult>;
189
+ /**
190
+ * Verify the chronological signing order against a detached envelope
191
+ * (instance, JSON, MJKSIG bytes, or Blob).
192
+ */
193
+ static verifyDetachedOrder(file: Blob, envelopeInput: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON | Uint8Array | Blob, expectedOrder: readonly (MajikKey | ExpectedSigner)[], MajikSig: MajikSignatureStaticAdapter, options?: ExtractOptions & VerifySignatureOrderOptions): Promise<SignatureOrderResult>;
181
194
  /**
182
195
  * Seal a multi-sig envelope, preventing any further signatures.
183
196
  * Issuer-only / already-sealed checks are enforced by envelope.withSeal().
@@ -37,6 +37,7 @@ import { FallbackHandler } from "./fallback";
37
37
  import { bytesToBase64, hashContent } from "../hash";
38
38
  import { MajikSignatureError, MajikSignatureValidationError } from "../errors";
39
39
  import { MajikSignatureMap } from "../mjksmap";
40
+ import { verifySignatureOrder } from "../order";
40
41
  // ─── Registry ─────────────────────────────────────────────────────────────────
41
42
  const DEFAULT_REGISTRY = new FormatHandlerRegistry()
42
43
  .register(new PdfHandler())
@@ -473,6 +474,41 @@ export class MajikSignatureEmbed {
473
474
  summary.allValid = summary.verified === summary.total && summary.total > 0;
474
475
  return summary;
475
476
  }
477
+ /**
478
+ * Verify the chronological signing order of a file's embedded envelope
479
+ * against an expected sequence of signers.
480
+ * expectedOrder accepts MajikKey instances and/or ExpectedSigner objects,
481
+ * mixed freely — normalized internally.
482
+ */
483
+ static async verifyFileOrder(file, expectedOrder, MajikSig, options) {
484
+ const { bytes, handler } = await MajikSignatureEmbed._prepare(file, options);
485
+ const raw = await handler.extract(bytes);
486
+ const envelope = raw
487
+ ? MajikSignatureEnvelope.fromJSON(raw)
488
+ : MajikSignatureEnvelope.empty();
489
+ if (raw) {
490
+ const integrity = envelope.verifyAllowlistIntegrity();
491
+ if (!integrity.valid) {
492
+ throw new MajikSignatureError(integrity.reason ?? "Allowlist integrity check failed");
493
+ }
494
+ }
495
+ const originalBytes = raw ? await handler.strip(bytes) : bytes;
496
+ return verifySignatureOrder(envelope, originalBytes, expectedOrder, (content, sig, pk) => MajikSig.verify(content, sig, pk), { strict: options?.strict });
497
+ }
498
+ /**
499
+ * Verify the chronological signing order against a detached envelope
500
+ * (instance, JSON, MJKSIG bytes, or Blob).
501
+ */
502
+ static async verifyDetachedOrder(file, envelopeInput, expectedOrder, MajikSig, options) {
503
+ const { bytes, handler } = await MajikSignatureEmbed._prepare(file, options);
504
+ const envelope = await MajikSignatureEnvelope.from(envelopeInput);
505
+ const integrity = envelope.verifyAllowlistIntegrity();
506
+ if (!integrity.valid) {
507
+ throw new MajikSignatureError(integrity.reason ?? "Allowlist integrity check failed");
508
+ }
509
+ const originalBytes = await handler.strip(bytes);
510
+ return verifySignatureOrder(envelope, originalBytes, expectedOrder, (content, sig, pk) => MajikSig.verify(content, sig, pk), { strict: options?.strict });
511
+ }
476
512
  // ── seal ───────────────────────────────────────────────────────────────────
477
513
  /**
478
514
  * Seal a multi-sig envelope, preventing any further signatures.
@@ -0,0 +1,100 @@
1
+ /**
2
+ * core/order.ts
3
+ *
4
+ * Chronological signing-order verification.
5
+ *
6
+ * Pure /structural, like envelope.ts — no MajikSig-specific imports beyond a
7
+ * caller-supplied verify callback, so this has zero circular-dependency risk
8
+ * with majik-signature.ts or majik-embed.ts.
9
+ *
10
+ * Trust model:
11
+ * - tsa.payload.timestamp is server-attested (a TSA stamped a nonce) —
12
+ * preferred whenever present.
13
+ * - The bare `timestamp` field is self-reported by the signer's local
14
+ * clock. It's tamper-evident (covered by the signature) but NOT
15
+ * independently attested — a signer could set their clock to anything.
16
+ * - Every result surfaces `usesUnattestedTimestamp` so callers can show
17
+ * a caveat when order was decided using a self-reported clock.
18
+ */
19
+ import type { MajikKey } from "@majikah/majik-key";
20
+ import type { MajikSignatureEnvelope } from "./envelope";
21
+ import type { ExpectedSigner, MajikSignatureJSON, MajikSignerPublicKeys, VerificationResult } from "./types";
22
+ export type TimestampSource = "tsa" | "self-reported";
23
+ export interface SignerOrderStatus {
24
+ signerId: string;
25
+ expectedPosition: number;
26
+ hasSigned: boolean;
27
+ /** Present only when hasSigned is true */
28
+ valid?: boolean;
29
+ /** Present only when hasSigned && !valid */
30
+ reason?: string;
31
+ effectiveTimestamp?: string;
32
+ timestampSource?: TimestampSource;
33
+ }
34
+ export interface OrderViolation {
35
+ /** signerId expected to have signed earlier */
36
+ earlier: string;
37
+ /** signerId expected to have signed later */
38
+ later: string;
39
+ earlierTimestamp: string;
40
+ laterTimestamp: string;
41
+ }
42
+ export interface SoftTieWarning {
43
+ a: string;
44
+ b: string;
45
+ timestamp: string;
46
+ }
47
+ export interface SignatureOrderResult {
48
+ /** True only when everyone expected signed, every signature is valid,
49
+ * the order was respected, AND (in strict mode) no extra signers exist. */
50
+ valid: boolean;
51
+ allExpectedSigned: boolean;
52
+ allValid: boolean;
53
+ orderRespected: boolean;
54
+ strict: boolean;
55
+ /** Only populated when strict === true */
56
+ unexpectedSigners: string[];
57
+ pendingSigners: string[];
58
+ invalidSigners: string[];
59
+ violations: OrderViolation[];
60
+ /** True if any timestamp used in a comparison was self-reported rather
61
+ * than TSA-attested — order in that case is a claim, not a proof. */
62
+ usesUnattestedTimestamp: boolean;
63
+ /** Two signers landed on the identical instant — order between them is
64
+ * indistinguishable. Doesn't fail `valid`, just a note. */
65
+ softTieWarnings: SoftTieWarning[];
66
+ signers: SignerOrderStatus[];
67
+ reason?: string;
68
+ }
69
+ export interface VerifySignatureOrderOptions {
70
+ /** When true, any signer present in the envelope but absent from
71
+ * expectedOrder is reported and fails the overall result. */
72
+ strict?: boolean;
73
+ }
74
+ /** Minimal shape the order verifier needs to check one signature's crypto. */
75
+ export type OrderVerifyFn = (content: Uint8Array, signature: MajikSignatureJSON, publicKeys: MajikSignerPublicKeys) => VerificationResult;
76
+ /**
77
+ * Normalize a mixed array of MajikKey instances and/or ExpectedSigner
78
+ * objects into a plain ExpectedSigner[]. Order-preserving — position in
79
+ * the input array IS the expected signing position.
80
+ */
81
+ export declare function normalizeExpectedOrder(input: readonly (MajikKey | ExpectedSigner)[]): ExpectedSigner[];
82
+ export declare function resolveEffectiveTimestamp(sig: MajikSignatureJSON): {
83
+ timestamp: string;
84
+ source: TimestampSource;
85
+ };
86
+ /**
87
+ * Verify that an envelope's signatures were produced in the sequence given
88
+ * by expectedOrder (array position == expected chronological position).
89
+ *
90
+ * Each expected signer is verified against THEIR OWN publicly-known keys
91
+ * (as supplied in expectedOrder), not the self-asserted keys embedded in
92
+ * their own envelope entry — this ties identity to a key you actually
93
+ * trust, rather than trusting whatever key a signature claims to be signed
94
+ * with.
95
+ *
96
+ * Only signers who (a) signed and (b) verified valid are compared for
97
+ * order — an invalid signature's timestamp isn't trustworthy, so it's
98
+ * excluded from ordering but still reported via invalidSigners.
99
+ */
100
+ export declare function verifySignatureOrder(envelope: MajikSignatureEnvelope, originalBytes: Uint8Array, expectedOrder: readonly (MajikKey | ExpectedSigner)[], verifyFn: OrderVerifyFn, options?: VerifySignatureOrderOptions): SignatureOrderResult;
@@ -0,0 +1,209 @@
1
+ /**
2
+ * core/order.ts
3
+ *
4
+ * Chronological signing-order verification.
5
+ *
6
+ * Pure /structural, like envelope.ts — no MajikSig-specific imports beyond a
7
+ * caller-supplied verify callback, so this has zero circular-dependency risk
8
+ * with majik-signature.ts or majik-embed.ts.
9
+ *
10
+ * Trust model:
11
+ * - tsa.payload.timestamp is server-attested (a TSA stamped a nonce) —
12
+ * preferred whenever present.
13
+ * - The bare `timestamp` field is self-reported by the signer's local
14
+ * clock. It's tamper-evident (covered by the signature) but NOT
15
+ * independently attested — a signer could set their clock to anything.
16
+ * - Every result surfaces `usesUnattestedTimestamp` so callers can show
17
+ * a caveat when order was decided using a self-reported clock.
18
+ */
19
+ import { MajikSignatureKeyError, MajikSignatureValidationError, } from "./errors";
20
+ import { base64ToBytes, bytesToBase64 } from "./hash";
21
+ // ─── Normalization ─────────────────────────────────────────────────────────
22
+ function isExpectedSignerShape(v) {
23
+ return typeof v.edPublicKey === "string";
24
+ }
25
+ function toExpectedSigner(input) {
26
+ if (isExpectedSignerShape(input))
27
+ return input;
28
+ const key = input;
29
+ if (!key.hasSigningKeys) {
30
+ throw new MajikSignatureKeyError("MajikKey has no signing public keys — cannot build expected order entry.");
31
+ }
32
+ return {
33
+ signerId: key.fingerprint,
34
+ edPublicKey: bytesToBase64(key.edPublicKey),
35
+ mlDsaPublicKey: bytesToBase64(key.mlDsaPublicKey),
36
+ };
37
+ }
38
+ /**
39
+ * Normalize a mixed array of MajikKey instances and/or ExpectedSigner
40
+ * objects into a plain ExpectedSigner[]. Order-preserving — position in
41
+ * the input array IS the expected signing position.
42
+ */
43
+ export function normalizeExpectedOrder(input) {
44
+ return input.map(toExpectedSigner);
45
+ }
46
+ // ─── Timestamp resolution ───────────────────────────────────────────────────
47
+ export function resolveEffectiveTimestamp(sig) {
48
+ if (sig.tsa?.payload?.timestamp) {
49
+ return { timestamp: sig.tsa.payload.timestamp, source: "tsa" };
50
+ }
51
+ return { timestamp: sig.timestamp, source: "self-reported" };
52
+ }
53
+ // ─── Core verification ───────────────────────────────────────────────────────
54
+ /**
55
+ * Verify that an envelope's signatures were produced in the sequence given
56
+ * by expectedOrder (array position == expected chronological position).
57
+ *
58
+ * Each expected signer is verified against THEIR OWN publicly-known keys
59
+ * (as supplied in expectedOrder), not the self-asserted keys embedded in
60
+ * their own envelope entry — this ties identity to a key you actually
61
+ * trust, rather than trusting whatever key a signature claims to be signed
62
+ * with.
63
+ *
64
+ * Only signers who (a) signed and (b) verified valid are compared for
65
+ * order — an invalid signature's timestamp isn't trustworthy, so it's
66
+ * excluded from ordering but still reported via invalidSigners.
67
+ */
68
+ export function verifySignatureOrder(envelope, originalBytes, expectedOrder, verifyFn, options) {
69
+ const strict = options?.strict ?? false;
70
+ const normalizedExpected = normalizeExpectedOrder(expectedOrder);
71
+ if (normalizedExpected.length === 0) {
72
+ throw new MajikSignatureValidationError("expectedOrder must contain at least one signer.", "expectedOrder");
73
+ }
74
+ const seenIds = new Set();
75
+ for (const e of normalizedExpected) {
76
+ if (seenIds.has(e.signerId)) {
77
+ throw new MajikSignatureValidationError(`Duplicate signerId in expectedOrder: "${e.signerId}"`, "expectedOrder");
78
+ }
79
+ seenIds.add(e.signerId);
80
+ }
81
+ const sigsById = new Map(envelope.signatures.map((s) => [s.signerId, s]));
82
+ const signers = [];
83
+ const pendingSigners = [];
84
+ const invalidSigners = [];
85
+ let usesUnattestedTimestamp = false;
86
+ // Only expected signers who signed AND verified valid are eligible for
87
+ // order comparison — their timestamps are the only trustworthy ones.
88
+ const considered = [];
89
+ for (let i = 0; i < normalizedExpected.length; i++) {
90
+ const expected = normalizedExpected[i];
91
+ const sig = sigsById.get(expected.signerId);
92
+ if (!sig) {
93
+ pendingSigners.push(expected.signerId);
94
+ signers.push({
95
+ signerId: expected.signerId,
96
+ expectedPosition: i,
97
+ hasSigned: false,
98
+ });
99
+ continue;
100
+ }
101
+ const publicKeys = {
102
+ signerId: expected.signerId,
103
+ edPublicKey: base64ToBytes(expected.edPublicKey),
104
+ mlDsaPublicKey: base64ToBytes(expected.mlDsaPublicKey),
105
+ };
106
+ const result = verifyFn(originalBytes, sig, publicKeys);
107
+ const { timestamp, source } = resolveEffectiveTimestamp(sig);
108
+ if (!result.valid) {
109
+ invalidSigners.push(expected.signerId);
110
+ signers.push({
111
+ signerId: expected.signerId,
112
+ expectedPosition: i,
113
+ hasSigned: true,
114
+ valid: false,
115
+ reason: result.reason,
116
+ effectiveTimestamp: timestamp,
117
+ timestampSource: source,
118
+ });
119
+ continue;
120
+ }
121
+ if (source === "self-reported")
122
+ usesUnattestedTimestamp = true;
123
+ signers.push({
124
+ signerId: expected.signerId,
125
+ expectedPosition: i,
126
+ hasSigned: true,
127
+ valid: true,
128
+ effectiveTimestamp: timestamp,
129
+ timestampSource: source,
130
+ });
131
+ considered.push({
132
+ signerId: expected.signerId,
133
+ timestamp,
134
+ timeMs: Date.parse(timestamp),
135
+ });
136
+ }
137
+ // ── Order check — every pair among considered signers, not just neighbors ──
138
+ const violations = [];
139
+ const softTieWarnings = [];
140
+ for (let i = 0; i < considered.length; i++) {
141
+ for (let j = i + 1; j < considered.length; j++) {
142
+ const a = considered[i]; // expected earlier
143
+ const b = considered[j]; // expected later
144
+ if (a.timeMs > b.timeMs) {
145
+ violations.push({
146
+ earlier: a.signerId,
147
+ later: b.signerId,
148
+ earlierTimestamp: a.timestamp,
149
+ laterTimestamp: b.timestamp,
150
+ });
151
+ }
152
+ else if (a.timeMs === b.timeMs) {
153
+ softTieWarnings.push({
154
+ a: a.signerId,
155
+ b: b.signerId,
156
+ timestamp: a.timestamp,
157
+ });
158
+ }
159
+ }
160
+ }
161
+ // ── Strict mode: any actual signer absent from expectedOrder ────────────
162
+ const unexpectedSigners = [];
163
+ if (strict) {
164
+ for (const sig of envelope.signatures) {
165
+ if (!seenIds.has(sig.signerId))
166
+ unexpectedSigners.push(sig.signerId);
167
+ }
168
+ }
169
+ const allExpectedSigned = pendingSigners.length === 0;
170
+ const allValid = invalidSigners.length === 0;
171
+ const orderRespected = violations.length === 0;
172
+ const strictSatisfied = !strict || unexpectedSigners.length === 0;
173
+ const valid = allExpectedSigned && allValid && orderRespected && strictSatisfied;
174
+ let reason;
175
+ if (!allExpectedSigned) {
176
+ reason = `Missing signature(s) from: ${pendingSigners.join(", ")}`;
177
+ }
178
+ else if (!allValid) {
179
+ reason = `Invalid signature(s) from: ${invalidSigners.join(", ")}`;
180
+ }
181
+ else if (!strictSatisfied) {
182
+ reason = `Unexpected signer(s) present (strict mode): ${unexpectedSigners.join(", ")}`;
183
+ }
184
+ else if (!orderRespected) {
185
+ reason = `Signing order violated: ${violations
186
+ .map((v) => `"${v.earlier}" expected before "${v.later}" but signed after`)
187
+ .join("; ")}`;
188
+ }
189
+ else if (softTieWarnings.length > 0) {
190
+ reason = `All signatures valid and in order, but ${softTieWarnings.length} pair(s) share an identical timestamp — order cannot be distinguished for: ${softTieWarnings
191
+ .map((w) => `"${w.a}"/"${w.b}"`)
192
+ .join(", ")}`;
193
+ }
194
+ return {
195
+ valid,
196
+ allExpectedSigned,
197
+ allValid,
198
+ orderRespected,
199
+ strict,
200
+ unexpectedSigners,
201
+ pendingSigners,
202
+ invalidSigners,
203
+ violations,
204
+ usesUnattestedTimestamp,
205
+ softTieWarnings,
206
+ signers,
207
+ reason,
208
+ };
209
+ }
@@ -10,6 +10,7 @@ import type { ImageVerificationResult, ImageSignOptions, ImageSignatureStub } fr
10
10
  import { MajikChainAnchor, MajikChainAnchorMemo } from "./anchor/types";
11
11
  import { MajikSignatureEnvelope } from "./core/envelope";
12
12
  import { MajikSignatureMap } from "./core/mjksmap";
13
+ import { SignatureOrderResult } from "./core/order";
13
14
  /**
14
15
  * Majik Signature
15
16
  * ---
@@ -217,6 +218,32 @@ export declare class MajikSignature {
217
218
  static verifyFilesFromMjksMap(map: MajikSignatureMap, files: BatchVerifyInput[], publicKeys: MajikSignerPublicKeys, options?: BatchVerifyOptions, debug?: boolean): Promise<FileVerifyResult[]>;
218
219
  static verifyFilesFromMjksMapWithKey(map: MajikSignatureMap, files: BatchVerifyInput[], key: MajikKey, options?: BatchVerifyOptions, debug?: boolean): Promise<FileVerifyResult[]>;
219
220
  static summarizeBatchVerification(results: FileVerifyResult[]): ReturnType<typeof MajikSignatureEmbed.summarizeBatchVerification>;
221
+ /**
222
+ * Verify that a file's embedded signatures were produced in the given
223
+ * order. expectedOrder accepts MajikKey instances and/or ExpectedSigner
224
+ * objects, mixed freely.
225
+ *
226
+ * @example
227
+ * const result = await MajikSignature.verifyFileOrder(file, [bobKey, daveKey]);
228
+ * if (!result.valid) console.warn(result.reason);
229
+ */
230
+ static verifyFileOrder(file: Blob, expectedOrder: readonly (MajikKey | ExpectedSigner)[], options?: {
231
+ mimeType?: string;
232
+ strict?: boolean;
233
+ }): Promise<SignatureOrderResult>;
234
+ /**
235
+ * Verify signing order against a detached envelope.
236
+ */
237
+ static verifyFileDetachedOrder(file: Blob, envelope: MajikSignatureEnvelope | MajikSignatureEnvelopeJSON | Uint8Array | Blob, expectedOrder: readonly (MajikKey | ExpectedSigner)[], options?: {
238
+ mimeType?: string;
239
+ strict?: boolean;
240
+ }): Promise<SignatureOrderResult>;
241
+ /**
242
+ * Normalize a mixed array of MajikKey instances / ExpectedSigner objects
243
+ * into a plain ExpectedSigner[]. Exposed standalone in case you want to
244
+ * cache/store the normalized order without immediately verifying.
245
+ */
246
+ static normalizeExpectedOrder(expectedOrder: readonly (MajikKey | ExpectedSigner)[]): ExpectedSigner[];
220
247
  /**
221
248
  * Embed this MajikSignature instance into a file.
222
249
  * The signature must cover the original file bytes BEFORE embedding.
@@ -13,6 +13,7 @@ import { hashContent, bytesToBase64, base64ToBytes } from "./core/hash";
13
13
  import { MajikSignatureEmbed } from "./core/embed/majik-embed";
14
14
  // ── Stamp (image signing) imports ─────────────────────────────────────────────
15
15
  import { MajikImageSignature } from "./core/stamp/image-signature";
16
+ import { normalizeExpectedOrder as normalizeExpectedOrderUtil, } from "./core/order";
16
17
  const secureFill = Uint8Array.prototype.fill;
17
18
  /**
18
19
  * Majik Signature
@@ -494,6 +495,32 @@ export class MajikSignature {
494
495
  static summarizeBatchVerification(results) {
495
496
  return MajikSignatureEmbed.summarizeBatchVerification(results);
496
497
  }
498
+ /**
499
+ * Verify that a file's embedded signatures were produced in the given
500
+ * order. expectedOrder accepts MajikKey instances and/or ExpectedSigner
501
+ * objects, mixed freely.
502
+ *
503
+ * @example
504
+ * const result = await MajikSignature.verifyFileOrder(file, [bobKey, daveKey]);
505
+ * if (!result.valid) console.warn(result.reason);
506
+ */
507
+ static async verifyFileOrder(file, expectedOrder, options) {
508
+ return MajikSignatureEmbed.verifyFileOrder(file, expectedOrder, MajikSignature, options);
509
+ }
510
+ /**
511
+ * Verify signing order against a detached envelope.
512
+ */
513
+ static async verifyFileDetachedOrder(file, envelope, expectedOrder, options) {
514
+ return MajikSignatureEmbed.verifyDetachedOrder(file, envelope, expectedOrder, MajikSignature, options);
515
+ }
516
+ /**
517
+ * Normalize a mixed array of MajikKey instances / ExpectedSigner objects
518
+ * into a plain ExpectedSigner[]. Exposed standalone in case you want to
519
+ * cache/store the normalized order without immediately verifying.
520
+ */
521
+ static normalizeExpectedOrder(expectedOrder) {
522
+ return normalizeExpectedOrderUtil(expectedOrder);
523
+ }
497
524
  /**
498
525
  * Embed this MajikSignature instance into a file.
499
526
  * The signature must cover the original file bytes BEFORE embedding.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@majikah/majik-signature",
3
3
  "type": "module",
4
4
  "description": "Majik Signature is a hybrid post-quantum content signing and verification library for the Majikah ecosystem. Built on top of Majik Key, it provides tamper-proof, forgery-resistant digital signatures for any content format — using a dual-algorithm architecture that combines classical Ed25519 with post-quantum ML-DSA-87 (FIPS-204).",
5
- "version": "0.2.6",
5
+ "version": "0.2.7",
6
6
  "license": "Apache-2.0",
7
7
  "author": "Zelijah",
8
8
  "main": "./dist/index.js",