@thehonestmachine/oath-core 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.
Files changed (63) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +9 -0
  3. package/dist/bytes.d.ts +7 -0
  4. package/dist/bytes.d.ts.map +1 -0
  5. package/dist/bytes.js +46 -0
  6. package/dist/bytes.js.map +1 -0
  7. package/dist/ed25519.d.ts +11 -0
  8. package/dist/ed25519.d.ts.map +1 -0
  9. package/dist/ed25519.js +37 -0
  10. package/dist/ed25519.js.map +1 -0
  11. package/dist/entry.d.ts +27 -0
  12. package/dist/entry.d.ts.map +1 -0
  13. package/dist/entry.js +183 -0
  14. package/dist/entry.js.map +1 -0
  15. package/dist/errors.d.ts +5 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +5 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/hash.d.ts +3 -0
  20. package/dist/hash.d.ts.map +1 -0
  21. package/dist/hash.js +9 -0
  22. package/dist/hash.js.map +1 -0
  23. package/dist/index.d.ts +12 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +11 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/jcs.d.ts +19 -0
  28. package/dist/jcs.d.ts.map +1 -0
  29. package/dist/jcs.js +63 -0
  30. package/dist/jcs.js.map +1 -0
  31. package/dist/merkle.d.ts +35 -0
  32. package/dist/merkle.d.ts.map +1 -0
  33. package/dist/merkle.js +177 -0
  34. package/dist/merkle.js.map +1 -0
  35. package/dist/oath.d.ts +34 -0
  36. package/dist/oath.d.ts.map +1 -0
  37. package/dist/oath.js +128 -0
  38. package/dist/oath.js.map +1 -0
  39. package/dist/sth.d.ts +16 -0
  40. package/dist/sth.d.ts.map +1 -0
  41. package/dist/sth.js +95 -0
  42. package/dist/sth.js.map +1 -0
  43. package/dist/types.d.ts +90 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +3 -0
  46. package/dist/types.js.map +1 -0
  47. package/dist/validate.d.ts +17 -0
  48. package/dist/validate.d.ts.map +1 -0
  49. package/dist/validate.js +65 -0
  50. package/dist/validate.js.map +1 -0
  51. package/package.json +55 -0
  52. package/src/bytes.ts +46 -0
  53. package/src/ed25519.ts +40 -0
  54. package/src/entry.ts +208 -0
  55. package/src/errors.ts +4 -0
  56. package/src/hash.ts +10 -0
  57. package/src/index.ts +82 -0
  58. package/src/jcs.ts +60 -0
  59. package/src/merkle.ts +184 -0
  60. package/src/oath.ts +148 -0
  61. package/src/sth.ts +126 -0
  62. package/src/types.ts +108 -0
  63. package/src/validate.ts +81 -0
@@ -0,0 +1,81 @@
1
+ import { ProtocolError } from './errors.js';
2
+
3
+ /** Spec §1.2: lowercase alphanumeric + hyphens, 1–64 chars, no edge hyphens. */
4
+ export const HANDLE_RE = /^[a-z0-9]([a-z0-9-]{0,62}[a-z0-9])?$/;
5
+
6
+ export function isHandle(value: unknown): value is string {
7
+ return typeof value === 'string' && HANDLE_RE.test(value);
8
+ }
9
+
10
+ const TIMESTAMP_RE =
11
+ /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d{1,9})?Z$/;
12
+
13
+ const DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] as const;
14
+
15
+ function isLeapYear(year: number): boolean {
16
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
17
+ }
18
+
19
+ /**
20
+ * Spec §1.2: RFC 3339 UTC with Z suffix. Calendar validity is checked
21
+ * explicitly — Date.parse silently rolls over impossible dates like Feb 30.
22
+ */
23
+ export function isTimestamp(value: unknown): value is string {
24
+ if (typeof value !== 'string') return false;
25
+ const m = TIMESTAMP_RE.exec(value);
26
+ if (m === null) return false;
27
+ const year = Number(m[1]);
28
+ const month = Number(m[2]);
29
+ const day = Number(m[3]);
30
+ const maxDay = month === 2 && isLeapYear(year) ? 29 : DAYS_IN_MONTH[month - 1]!;
31
+ return day <= maxDay;
32
+ }
33
+
34
+ /** Lowercase hex of exactly `byteLength` bytes. */
35
+ export function isHex(value: unknown, byteLength: number): value is string {
36
+ return (
37
+ typeof value === 'string' &&
38
+ value.length === byteLength * 2 &&
39
+ /^[0-9a-f]*$/.test(value)
40
+ );
41
+ }
42
+
43
+ export function isPositiveInteger(value: unknown): value is number {
44
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= 1;
45
+ }
46
+
47
+ export function isNonNegativeInteger(value: unknown): value is number {
48
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
49
+ }
50
+
51
+ export function isNonEmptyString(value: unknown): value is string {
52
+ return typeof value === 'string' && value.length > 0;
53
+ }
54
+
55
+ export function assertPlainObject(value: unknown, label: string): Record<string, unknown> {
56
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
57
+ throw new ProtocolError(`${label} must be a JSON object`);
58
+ }
59
+ const proto = Object.getPrototypeOf(value);
60
+ if (proto !== Object.prototype && proto !== null) {
61
+ throw new ProtocolError(`${label} must be a plain JSON object`);
62
+ }
63
+ return value as Record<string, unknown>;
64
+ }
65
+
66
+ /** Spec §1.2: unknown fields MUST be rejected. */
67
+ export function assertKeys(
68
+ obj: Record<string, unknown>,
69
+ label: string,
70
+ required: readonly string[],
71
+ optional: readonly string[],
72
+ ): void {
73
+ for (const key of required) {
74
+ if (!(key in obj)) throw new ProtocolError(`${label} is missing required field "${key}"`);
75
+ }
76
+ for (const key of Object.keys(obj)) {
77
+ if (!required.includes(key) && !optional.includes(key)) {
78
+ throw new ProtocolError(`${label} has unknown field "${key}"`);
79
+ }
80
+ }
81
+ }