@serve.zone/interfaces 20.0.0 → 20.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.
@@ -0,0 +1,14 @@
1
+ export declare const strictCanonicalJsonRules: Readonly<{
2
+ version: 1;
3
+ encoding: "utf-8";
4
+ objectKeyOrdering: "utf-16-code-unit-ascending";
5
+ arrayOrdering: "preserved-dense";
6
+ unicodeNormalization: "preserved";
7
+ numberEncoding: "safe-integer-json-no-negative-zero";
8
+ digest: "sha256-lowercase-hex";
9
+ }>;
10
+ export type TCanonicalJsonFailure = (reasonArg: string) => never;
11
+ export declare const canonicalizeStrictJson: (valueArg: unknown, failArg: TCanonicalJsonFailure, fieldNameArg?: string) => string;
12
+ export declare const createSha256Hex: (contentsArg: Uint8Array, failArg: TCanonicalJsonFailure) => Promise<string>;
13
+ export declare const createCanonicalJsonSha256Hex: (canonicalJsonArg: string, failArg: TCanonicalJsonFailure) => Promise<string>;
14
+ export declare const deepFreezeValue: <T>(valueArg: T, seenArg?: WeakSet<object>) => T;
@@ -0,0 +1,154 @@
1
+ export const strictCanonicalJsonRules = Object.freeze({
2
+ version: 1,
3
+ encoding: 'utf-8',
4
+ objectKeyOrdering: 'utf-16-code-unit-ascending',
5
+ arrayOrdering: 'preserved-dense',
6
+ unicodeNormalization: 'preserved',
7
+ numberEncoding: 'safe-integer-json-no-negative-zero',
8
+ digest: 'sha256-lowercase-hex',
9
+ });
10
+ const assertUnicodeScalarString = (valueArg, fieldNameArg, failArg) => {
11
+ for (let index = 0; index < valueArg.length; index++) {
12
+ const codeUnit = valueArg.charCodeAt(index);
13
+ if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) {
14
+ const followingCodeUnit = valueArg.charCodeAt(index + 1);
15
+ if (!Number.isInteger(followingCodeUnit) ||
16
+ followingCodeUnit < 0xdc00 ||
17
+ followingCodeUnit > 0xdfff) {
18
+ failArg(`${fieldNameArg} must not contain an unpaired UTF-16 surrogate`);
19
+ }
20
+ index++;
21
+ continue;
22
+ }
23
+ if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) {
24
+ failArg(`${fieldNameArg} must not contain an unpaired UTF-16 surrogate`);
25
+ }
26
+ }
27
+ };
28
+ const readStrictRecord = (valueArg, fieldNameArg, failArg) => {
29
+ const prototype = Object.getPrototypeOf(valueArg);
30
+ if (prototype !== Object.prototype && prototype !== null) {
31
+ return failArg(`${fieldNameArg} must be a plain object`);
32
+ }
33
+ const record = valueArg;
34
+ const ownKeys = Reflect.ownKeys(record);
35
+ if (ownKeys.some((keyArg) => typeof keyArg !== 'string')) {
36
+ return failArg(`${fieldNameArg} must not contain symbol keys`);
37
+ }
38
+ for (const key of ownKeys) {
39
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
40
+ if (!descriptor ||
41
+ !descriptor.enumerable ||
42
+ !Object.hasOwn(descriptor, 'value')) {
43
+ return failArg(`${fieldNameArg} must contain enumerable data properties only`);
44
+ }
45
+ }
46
+ return record;
47
+ };
48
+ const readStrictArray = (valueArg, fieldNameArg, failArg) => {
49
+ if (Object.getPrototypeOf(valueArg) !== Array.prototype) {
50
+ return failArg(`${fieldNameArg} must be a dense plain array`);
51
+ }
52
+ for (let index = 0; index < valueArg.length; index++) {
53
+ if (!Object.hasOwn(valueArg, index)) {
54
+ return failArg(`${fieldNameArg} must not contain sparse entries`);
55
+ }
56
+ const descriptor = Object.getOwnPropertyDescriptor(valueArg, String(index));
57
+ if (!descriptor ||
58
+ !descriptor.enumerable ||
59
+ !Object.hasOwn(descriptor, 'value')) {
60
+ return failArg(`${fieldNameArg} must contain enumerable data entries only`);
61
+ }
62
+ }
63
+ for (const key of Reflect.ownKeys(valueArg)) {
64
+ if (key === 'length') {
65
+ continue;
66
+ }
67
+ if (typeof key !== 'string' ||
68
+ !/^(?:0|[1-9][0-9]*)$/.test(key) ||
69
+ Number(key) >= valueArg.length) {
70
+ return failArg(`${fieldNameArg} must not contain extra properties`);
71
+ }
72
+ }
73
+ return valueArg;
74
+ };
75
+ export const canonicalizeStrictJson = (valueArg, failArg, fieldNameArg = 'canonical JSON input') => {
76
+ const ancestors = new Set();
77
+ const visit = (currentValueArg, currentFieldNameArg) => {
78
+ if (currentValueArg === null) {
79
+ return 'null';
80
+ }
81
+ if (typeof currentValueArg === 'boolean') {
82
+ return currentValueArg ? 'true' : 'false';
83
+ }
84
+ if (typeof currentValueArg === 'string') {
85
+ assertUnicodeScalarString(currentValueArg, currentFieldNameArg, failArg);
86
+ return JSON.stringify(currentValueArg);
87
+ }
88
+ if (typeof currentValueArg === 'number') {
89
+ if (!Number.isSafeInteger(currentValueArg) ||
90
+ Object.is(currentValueArg, -0)) {
91
+ return failArg(`${currentFieldNameArg} must contain safe integers only`);
92
+ }
93
+ return String(currentValueArg);
94
+ }
95
+ if (!currentValueArg || typeof currentValueArg !== 'object') {
96
+ return failArg(`${currentFieldNameArg} contains a non-JSON value`);
97
+ }
98
+ if (ancestors.has(currentValueArg)) {
99
+ return failArg(`${currentFieldNameArg} contains a cycle`);
100
+ }
101
+ ancestors.add(currentValueArg);
102
+ try {
103
+ if (Array.isArray(currentValueArg)) {
104
+ const array = readStrictArray(currentValueArg, currentFieldNameArg, failArg);
105
+ return `[${array
106
+ .map((entryArg, indexArg) => visit(entryArg, `${currentFieldNameArg}[${indexArg}]`))
107
+ .join(',')}]`;
108
+ }
109
+ const record = readStrictRecord(currentValueArg, currentFieldNameArg, failArg);
110
+ const keys = Object.keys(record).sort((leftArg, rightArg) => {
111
+ return leftArg < rightArg ? -1 : leftArg > rightArg ? 1 : 0;
112
+ });
113
+ return `{${keys
114
+ .map((keyArg) => {
115
+ assertUnicodeScalarString(keyArg, `${currentFieldNameArg} key`, failArg);
116
+ return `${JSON.stringify(keyArg)}:${visit(record[keyArg], `${currentFieldNameArg}.${keyArg}`)}`;
117
+ })
118
+ .join(',')}}`;
119
+ }
120
+ finally {
121
+ ancestors.delete(currentValueArg);
122
+ }
123
+ };
124
+ return visit(valueArg, fieldNameArg);
125
+ };
126
+ export const createSha256Hex = async (contentsArg, failArg) => {
127
+ const subtle = globalThis.crypto?.subtle;
128
+ if (!subtle) {
129
+ return failArg('Web Crypto SHA-256 is unavailable in this runtime');
130
+ }
131
+ const digest = await subtle.digest('SHA-256', new Uint8Array(contentsArg));
132
+ return Array.from(new Uint8Array(digest), (byteArg) => byteArg.toString(16).padStart(2, '0')).join('');
133
+ };
134
+ export const createCanonicalJsonSha256Hex = async (canonicalJsonArg, failArg) => {
135
+ return createSha256Hex(new TextEncoder().encode(canonicalJsonArg), failArg);
136
+ };
137
+ export const deepFreezeValue = (valueArg, seenArg = new WeakSet()) => {
138
+ if (!valueArg || typeof valueArg !== 'object') {
139
+ return valueArg;
140
+ }
141
+ const object = valueArg;
142
+ if (ArrayBuffer.isView(object) || seenArg.has(object)) {
143
+ return valueArg;
144
+ }
145
+ seenArg.add(object);
146
+ for (const key of Reflect.ownKeys(object)) {
147
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
148
+ if (descriptor && Object.hasOwn(descriptor, 'value')) {
149
+ deepFreezeValue(descriptor.value, seenArg);
150
+ }
151
+ }
152
+ return Object.freeze(valueArg);
153
+ };
154
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2Fub25pY2FsanNvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL3ByaXZhdGUvY2Fub25pY2FsanNvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQUMsTUFBTSx3QkFBd0IsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQ3BELE9BQU8sRUFBRSxDQUFVO0lBQ25CLFFBQVEsRUFBRSxPQUFnQjtJQUMxQixpQkFBaUIsRUFBRSw0QkFBcUM7SUFDeEQsYUFBYSxFQUFFLGlCQUEwQjtJQUN6QyxvQkFBb0IsRUFBRSxXQUFvQjtJQUMxQyxjQUFjLEVBQUUsb0NBQTZDO0lBQzdELE1BQU0sRUFBRSxzQkFBK0I7Q0FDeEMsQ0FBQyxDQUFDO0FBSUgsTUFBTSx5QkFBeUIsR0FBRyxDQUNoQyxRQUFnQixFQUNoQixZQUFvQixFQUNwQixPQUE4QixFQUN4QixFQUFFO0lBQ1IsS0FBSyxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsS0FBSyxHQUFHLFFBQVEsQ0FBQyxNQUFNLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUNyRCxNQUFNLFFBQVEsR0FBRyxRQUFRLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzVDLElBQUksUUFBUSxJQUFJLE1BQU0sSUFBSSxRQUFRLElBQUksTUFBTSxFQUFFLENBQUM7WUFDN0MsTUFBTSxpQkFBaUIsR0FBRyxRQUFRLENBQUMsVUFBVSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQztZQUN6RCxJQUNFLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQztnQkFDcEMsaUJBQWlCLEdBQUcsTUFBTTtnQkFDMUIsaUJBQWlCLEdBQUcsTUFBTSxFQUMxQixDQUFDO2dCQUNELE9BQU8sQ0FBQyxHQUFHLFlBQVksZ0RBQWdELENBQUMsQ0FBQztZQUMzRSxDQUFDO1lBQ0QsS0FBSyxFQUFFLENBQUM7WUFDUixTQUFTO1FBQ1gsQ0FBQztRQUNELElBQUksUUFBUSxJQUFJLE1BQU0sSUFBSSxRQUFRLElBQUksTUFBTSxFQUFFLENBQUM7WUFDN0MsT0FBTyxDQUFDLEdBQUcsWUFBWSxnREFBZ0QsQ0FBQyxDQUFDO1FBQzNFLENBQUM7SUFDSCxDQUFDO0FBQ0gsQ0FBQyxDQUFDO0FBRUYsTUFBTSxnQkFBZ0IsR0FBRyxDQUN2QixRQUFnQixFQUNoQixZQUFvQixFQUNwQixPQUE4QixFQUNMLEVBQUU7SUFDM0IsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNsRCxJQUFJLFNBQVMsS0FBSyxNQUFNLENBQUMsU0FBUyxJQUFJLFNBQVMsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN6RCxPQUFPLE9BQU8sQ0FBQyxHQUFHLFlBQVkseUJBQXlCLENBQUMsQ0FBQztJQUMzRCxDQUFDO0lBQ0QsTUFBTSxNQUFNLEdBQUcsUUFBbUMsQ0FBQztJQUNuRCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3hDLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsT0FBTyxNQUFNLEtBQUssUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUN6RCxPQUFPLE9BQU8sQ0FBQyxHQUFHLFlBQVksK0JBQStCLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBQ0QsS0FBSyxNQUFNLEdBQUcsSUFBSSxPQUFtQixFQUFFLENBQUM7UUFDdEMsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNoRSxJQUNFLENBQUMsVUFBVTtZQUNYLENBQUMsVUFBVSxDQUFDLFVBQVU7WUFDdEIsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsRUFDbkMsQ0FBQztZQUNELE9BQU8sT0FBTyxDQUNaLEdBQUcsWUFBWSwrQ0FBK0MsQ0FDL0QsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxlQUFlLEdBQUcsQ0FDdEIsUUFBbUIsRUFDbkIsWUFBb0IsRUFDcEIsT0FBOEIsRUFDbkIsRUFBRTtJQUNiLElBQUksTUFBTSxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsS0FBSyxLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDeEQsT0FBTyxPQUFPLENBQUMsR0FBRyxZQUFZLDhCQUE4QixDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUNELEtBQUssSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEtBQUssR0FBRyxRQUFRLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDckQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDcEMsT0FBTyxPQUFPLENBQUMsR0FBRyxZQUFZLGtDQUFrQyxDQUFDLENBQUM7UUFDcEUsQ0FBQztRQUNELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDNUUsSUFDRSxDQUFDLFVBQVU7WUFDWCxDQUFDLFVBQVUsQ0FBQyxVQUFVO1lBQ3RCLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLEVBQ25DLENBQUM7WUFDRCxPQUFPLE9BQU8sQ0FBQyxHQUFHLFlBQVksNENBQTRDLENBQUMsQ0FBQztRQUM5RSxDQUFDO0lBQ0gsQ0FBQztJQUNELEtBQUssTUFBTSxHQUFHLElBQUksT0FBTyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQzVDLElBQUksR0FBRyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3JCLFNBQVM7UUFDWCxDQUFDO1FBQ0QsSUFDRSxPQUFPLEdBQUcsS0FBSyxRQUFRO1lBQ3ZCLENBQUMscUJBQXFCLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQztZQUNoQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksUUFBUSxDQUFDLE1BQU0sRUFDOUIsQ0FBQztZQUNELE9BQU8sT0FBTyxDQUFDLEdBQUcsWUFBWSxvQ0FBb0MsQ0FBQyxDQUFDO1FBQ3RFLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxRQUFRLENBQUM7QUFDbEIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsQ0FDcEMsUUFBaUIsRUFDakIsT0FBOEIsRUFDOUIsWUFBWSxHQUFHLHNCQUFzQixFQUM3QixFQUFFO0lBQ1YsTUFBTSxTQUFTLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztJQUVwQyxNQUFNLEtBQUssR0FBRyxDQUFDLGVBQXdCLEVBQUUsbUJBQTJCLEVBQVUsRUFBRTtRQUM5RSxJQUFJLGVBQWUsS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUM3QixPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO1FBQ0QsSUFBSSxPQUFPLGVBQWUsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN6QyxPQUFPLGVBQWUsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUM7UUFDNUMsQ0FBQztRQUNELElBQUksT0FBTyxlQUFlLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDeEMseUJBQXlCLENBQUMsZUFBZSxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ3pFLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUN6QyxDQUFDO1FBQ0QsSUFBSSxPQUFPLGVBQWUsS0FBSyxRQUFRLEVBQUUsQ0FBQztZQUN4QyxJQUNFLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxlQUFlLENBQUM7Z0JBQ3RDLE1BQU0sQ0FBQyxFQUFFLENBQUMsZUFBZSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQzlCLENBQUM7Z0JBQ0QsT0FBTyxPQUFPLENBQUMsR0FBRyxtQkFBbUIsa0NBQWtDLENBQUMsQ0FBQztZQUMzRSxDQUFDO1lBQ0QsT0FBTyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDakMsQ0FBQztRQUNELElBQUksQ0FBQyxlQUFlLElBQUksT0FBTyxlQUFlLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDNUQsT0FBTyxPQUFPLENBQUMsR0FBRyxtQkFBbUIsNEJBQTRCLENBQUMsQ0FBQztRQUNyRSxDQUFDO1FBQ0QsSUFBSSxTQUFTLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7WUFDbkMsT0FBTyxPQUFPLENBQUMsR0FBRyxtQkFBbUIsbUJBQW1CLENBQUMsQ0FBQztRQUM1RCxDQUFDO1FBRUQsU0FBUyxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUMvQixJQUFJLENBQUM7WUFDSCxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLEVBQUUsQ0FBQztnQkFDbkMsTUFBTSxLQUFLLEdBQUcsZUFBZSxDQUMzQixlQUFlLEVBQ2YsbUJBQW1CLEVBQ25CLE9BQU8sQ0FDUixDQUFDO2dCQUNGLE9BQU8sSUFBSSxLQUFLO3FCQUNiLEdBQUcsQ0FBQyxDQUFDLFFBQVEsRUFBRSxRQUFRLEVBQUUsRUFBRSxDQUMxQixLQUFLLENBQUMsUUFBUSxFQUFFLEdBQUcsbUJBQW1CLElBQUksUUFBUSxHQUFHLENBQUMsQ0FDdkQ7cUJBQ0EsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUM7WUFDbEIsQ0FBQztZQUVELE1BQU0sTUFBTSxHQUFHLGdCQUFnQixDQUM3QixlQUFlLEVBQ2YsbUJBQW1CLEVBQ25CLE9BQU8sQ0FDUixDQUFDO1lBQ0YsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFLEVBQUU7Z0JBQzFELE9BQU8sT0FBTyxHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzlELENBQUMsQ0FBQyxDQUFDO1lBQ0gsT0FBTyxJQUFJLElBQUk7aUJBQ1osR0FBRyxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUU7Z0JBQ2QseUJBQXlCLENBQ3ZCLE1BQU0sRUFDTixHQUFHLG1CQUFtQixNQUFNLEVBQzVCLE9BQU8sQ0FDUixDQUFDO2dCQUNGLE9BQU8sR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssQ0FDdkMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUNkLEdBQUcsbUJBQW1CLElBQUksTUFBTSxFQUFFLENBQ25DLEVBQUUsQ0FBQztZQUNOLENBQUMsQ0FBQztpQkFDRCxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQztRQUNsQixDQUFDO2dCQUFTLENBQUM7WUFDVCxTQUFTLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FBQyxDQUFDO1FBQ3BDLENBQUM7SUFDSCxDQUFDLENBQUM7SUFFRixPQUFPLEtBQUssQ0FBQyxRQUFRLEVBQUUsWUFBWSxDQUFDLENBQUM7QUFDdkMsQ0FBQyxDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLEtBQUssRUFDbEMsV0FBdUIsRUFDdkIsT0FBOEIsRUFDYixFQUFFO0lBQ25CLE1BQU0sTUFBTSxHQUFHLFVBQVUsQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDO0lBQ3pDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE9BQU8sT0FBTyxDQUFDLG1EQUFtRCxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUNELE1BQU0sTUFBTSxHQUFHLE1BQU0sTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsSUFBSSxVQUFVLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQztJQUMzRSxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxVQUFVLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUNwRCxPQUFPLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQ3RDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ2IsQ0FBQyxDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sNEJBQTRCLEdBQUcsS0FBSyxFQUMvQyxnQkFBd0IsRUFDeEIsT0FBOEIsRUFDYixFQUFFO0lBQ25CLE9BQU8sZUFBZSxDQUFDLElBQUksV0FBVyxFQUFFLENBQUMsTUFBTSxDQUFDLGdCQUFnQixDQUFDLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDOUUsQ0FBQyxDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLENBQzdCLFFBQVcsRUFDWCxVQUFVLElBQUksT0FBTyxFQUFVLEVBQzVCLEVBQUU7SUFDTCxJQUFJLENBQUMsUUFBUSxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQzlDLE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBRyxRQUFrQixDQUFDO0lBQ2xDLElBQUksV0FBVyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFDdEQsT0FBTyxRQUFRLENBQUM7SUFDbEIsQ0FBQztJQUNELE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDcEIsS0FBSyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7UUFDMUMsTUFBTSxVQUFVLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLE1BQU0sRUFBRSxHQUFHLENBQUMsQ0FBQztRQUNoRSxJQUFJLFVBQVUsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ3JELGVBQWUsQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzdDLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ2pDLENBQUMsQ0FBQyJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/interfaces",
3
- "version": "20.0.0",
3
+ "version": "20.1.0",
4
4
  "private": false,
5
5
  "description": "Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.",
6
6
  "exports": {
package/readme.md CHANGED
@@ -120,6 +120,130 @@ must reject unknown manifest fields and unsupported required feature IDs before
120
120
  provisioning. Legacy `volumes` and `platformRequirements.s3` remain deprecated
121
121
  inputs for strict resolver normalization only.
122
122
 
123
+ `platform.storagemigration` defines the provider-neutral cutover contract for a
124
+ named object-storage binding. Corestore atomically owns and fences the source
125
+ binding after validating a distinct, unfenced active-object snapshot. Onebox
126
+ only receives a held candidate, stages that exact candidate, stops the matching
127
+ workload generation, and attests the quiesced state. The candidate cannot start
128
+ until `destinationBindingStartAuthorized` is true. Corestore continues returning
129
+ `consumerAction: 'startDestination'` until Onebox submits the mutation-fenced
130
+ consumer activation request and the acknowledgement becomes durable evidence.
131
+ Candidate-issued abort tombstones authorize only `startSource` and never retain
132
+ the staged candidate binding.
133
+
134
+ Every migration DTO and status has an exact, versioned runtime normalizer.
135
+ Unknown fields, provider or pool identifiers, unbounded strings, unsafe
136
+ integers, stale mutation revisions, identity drift, and lifecycle-inconsistent
137
+ fields are rejected. Migration-created digests use strict canonical JSON and a
138
+ bare lowercase 64-hex SHA-256 value; portable golden vectors cover the source
139
+ snapshot, target request, prepare intent, and candidate binding. Pre-cutover
140
+ failures may retry or abort; after the durable commit point, recovery can only
141
+ retry or roll forward. Physical pool IDs, mount details, provider receipts, and
142
+ publication capabilities remain private.
143
+
144
+ Primary exports include:
145
+
146
+ - `IObjectStorageMigrationPrepareRequest` and
147
+ `TObjectStorageMigrationStatus` for the immutable intent and status journal.
148
+ - `IObjectStorageMigrationConsumerQuiesceRequest` with
149
+ `IStorageMigrationConsumerQuiesceEvidence` for exact candidate staging and
150
+ source-workload shutdown.
151
+ - `IObjectStorageMigrationConsumerActivationRequest` with
152
+ `IStorageMigrationConsumerActivationEvidence` for durable destination-start
153
+ acknowledgement.
154
+ - `normalizeObjectStorageMigrationStatus`,
155
+ `bindObjectStorageMigrationConsumerQuiesceRequest`, and
156
+ `bindObjectStorageMigrationConsumerActivationRequest` for strict ingress and
157
+ current-revision mutation fencing.
158
+ - The `create*Sha256` helpers for source snapshots, prepare intent, target
159
+ requests, candidate/active bindings, and persisted staging or activation
160
+ evidence.
161
+
162
+ The phase and consumer-action progression is exact:
163
+
164
+ | Phase | `consumerAction` | Destination start authorized |
165
+ | --- | --- | --- |
166
+ | `preparing`, `transferring` | `wait` | No |
167
+ | `awaitingConsumerQuiesce` | `stageCandidateAndStop` | No |
168
+ | `finalizing`, `committing` | `wait` | No |
169
+ | `readyToStart` | `startDestination` | Yes |
170
+ | `cleanupPending`, `complete` | `none` | Yes; durable activation evidence is required |
171
+ | `aborting` | `wait` | No |
172
+ | `aborted` | `startSource` | No; only the active source binding may restart |
173
+
174
+ Normalize every status before acting, and bind consumer mutations to that exact
175
+ status revision:
176
+
177
+ ```typescript
178
+ const status =
179
+ await platform.storagemigration.normalizeObjectStorageMigrationStatus(
180
+ untrustedStatusPayload,
181
+ );
182
+
183
+ if (status.phase === 'awaitingConsumerQuiesce') {
184
+ const request =
185
+ await platform.storagemigration.bindObjectStorageMigrationConsumerQuiesceRequest(
186
+ untrustedQuiescePayload,
187
+ status,
188
+ );
189
+ await submitQuiesceAcknowledgement(request);
190
+ }
191
+
192
+ if (status.phase === 'readyToStart') {
193
+ if (
194
+ !status.destinationBindingStartAuthorized ||
195
+ status.consumerAction !== 'startDestination'
196
+ ) {
197
+ throw new Error('destination binding is not authorized to start');
198
+ }
199
+ await startWorkload(status.activeBinding);
200
+ const request =
201
+ await platform.storagemigration.bindObjectStorageMigrationConsumerActivationRequest(
202
+ untrustedActivationPayload,
203
+ status,
204
+ );
205
+ await submitActivationAcknowledgement(request);
206
+ }
207
+
208
+ if (status.phase === 'cleanupPending') {
209
+ // Cleanup is reachable only after this durable acknowledgement was accepted.
210
+ const durableActivation = status.consumerActivationEvidence;
211
+ }
212
+
213
+ if (status.phase === 'aborted') {
214
+ if (
215
+ status.destinationBindingStartAuthorized ||
216
+ status.consumerAction !== 'startSource'
217
+ ) {
218
+ throw new Error('invalid aborted migration status');
219
+ }
220
+ await startWorkload(status.activeBinding);
221
+ }
222
+ ```
223
+
224
+ Here `startWorkload`, `submitQuiesceAcknowledgement`, and
225
+ `submitActivationAcknowledgement` are consumer-owned operations, not package
226
+ exports. Canonical digests are produced from normalized payloads:
227
+
228
+ ```typescript
229
+ const snapshotSha256 =
230
+ await platform.storagemigration.createUnfencedObjectStorageBindingControlSnapshotSha256(
231
+ snapshotDigestPayload,
232
+ );
233
+ const migrationSha256 =
234
+ await platform.storagemigration.createObjectStorageMigrationSha256(
235
+ prepareRequest,
236
+ );
237
+ const candidateSha256 =
238
+ await platform.storagemigration.createObjectStorageMigrationBindingSha256(
239
+ candidateBinding,
240
+ );
241
+ const activationRecordSha256 =
242
+ await platform.storagemigration.createObjectStorageMigrationPersistedActivationSha256(
243
+ activationDigestPayload,
244
+ );
245
+ ```
246
+
123
247
  ## Data Contracts
124
248
 
125
249
  Use `data` when you need object shapes that are persisted, exchanged between services, or exposed through the Cloudly API.
@@ -438,6 +562,8 @@ Available platform modules:
438
562
  - `platform.pushnotification` is the deprecated legacy device-token push contract. New browser Web Push integrations use `requests.webpush`.
439
563
  - `platform.letter` for physical letter workflows.
440
564
  - `platform.ai`, `platform.database`, `platform.objectstorage`, `platform.logging`, `platform.backup`, and `platform.sip` for infrastructure and application capabilities.
565
+ - `platform.storage` for provider-neutral storage classes, requests, capabilities, and resolved bindings.
566
+ - `platform.storagemigration` for fenced object-storage migration intent, status, consumer acknowledgements, canonical digests, and strict normalizers.
441
567
  - `platform.types` plus root re-exports for shared capability, provider, binding, credential, and endpoint metadata.
442
568
 
443
569
  ## Legacy Platformservice Contracts
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/interfaces',
6
- version: '20.0.0',
6
+ version: '20.1.0',
7
7
  description: 'Shared TypeScript interfaces and TypedRequest contracts for the serve.zone ecosystem.'
8
8
  }
@@ -3,6 +3,13 @@ import type {
3
3
  IBackupArchiveObject,
4
4
  TIsolatedRestoreResourceMapping,
5
5
  } from './backup.js';
6
+ import {
7
+ canonicalizeStrictJson,
8
+ createCanonicalJsonSha256Hex,
9
+ createSha256Hex,
10
+ deepFreezeValue,
11
+ strictCanonicalJsonRules,
12
+ } from '../private/canonicaljson.js';
6
13
 
7
14
  /** Operations carried by a short-lived, single-operation restore grant. */
8
15
  export const isolatedRestoreGrantOperations = Object.freeze([
@@ -307,15 +314,7 @@ export class IsolatedRestoreContractError extends Error {
307
314
  }
308
315
 
309
316
  /** Cross-runtime rules for restore authority digests. */
310
- export const isolatedRestoreCanonicalJsonRules = Object.freeze({
311
- version: 1 as const,
312
- encoding: 'utf-8' as const,
313
- objectKeyOrdering: 'utf-16-code-unit-ascending' as const,
314
- arrayOrdering: 'preserved-dense' as const,
315
- unicodeNormalization: 'preserved' as const,
316
- numberEncoding: 'safe-integer-json-no-negative-zero' as const,
317
- digest: 'sha256-lowercase-hex' as const,
318
- });
317
+ export const isolatedRestoreCanonicalJsonRules = strictCanonicalJsonRules;
319
318
 
320
319
  const canonicalIdentifierPattern = /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,255}$/;
321
320
  const canonicalScopedIdPattern = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,191}$/;
@@ -360,23 +359,7 @@ export const assertIsolatedRestoreControlIngressByteLength = (
360
359
  return byteLengthArg;
361
360
  };
362
361
 
363
- const deepFreeze = <T>(valueArg: T, seenArg = new WeakSet<object>()): T => {
364
- if (!valueArg || typeof valueArg !== 'object') {
365
- return valueArg;
366
- }
367
- const object = valueArg as object;
368
- if (ArrayBuffer.isView(object) || seenArg.has(object)) {
369
- return valueArg;
370
- }
371
- seenArg.add(object);
372
- for (const key of Reflect.ownKeys(object)) {
373
- const descriptor = Object.getOwnPropertyDescriptor(object, key);
374
- if (descriptor && Object.hasOwn(descriptor, 'value')) {
375
- deepFreeze(descriptor.value, seenArg);
376
- }
377
- }
378
- return Object.freeze(valueArg);
379
- };
362
+ const deepFreeze = deepFreezeValue;
380
363
 
381
364
  const verifiedIsolatedRestoreGrantCompacts = new WeakMap<object, string>();
382
365
  const verifiedIsolatedRestoreWriteBindings = new WeakSet<object>();
@@ -1419,64 +1402,7 @@ export const normalizeIsolatedRestoreControlStatusRequest = (
1419
1402
  };
1420
1403
 
1421
1404
  const canonicalizeJson = (valueArg: unknown): string => {
1422
- const ancestors = new Set<object>();
1423
-
1424
- const visit = (currentValueArg: unknown, fieldNameArg: string): string => {
1425
- if (currentValueArg === null) {
1426
- return 'null';
1427
- }
1428
- if (typeof currentValueArg === 'boolean') {
1429
- return currentValueArg ? 'true' : 'false';
1430
- }
1431
- if (typeof currentValueArg === 'string') {
1432
- assertUnicodeScalarString(currentValueArg, fieldNameArg);
1433
- return JSON.stringify(currentValueArg);
1434
- }
1435
- if (typeof currentValueArg === 'number') {
1436
- if (
1437
- !Number.isSafeInteger(currentValueArg) ||
1438
- Object.is(currentValueArg, -0)
1439
- ) {
1440
- return fail(`${fieldNameArg} must contain safe integers only`);
1441
- }
1442
- return String(currentValueArg);
1443
- }
1444
- if (typeof currentValueArg !== 'object') {
1445
- return fail(`${fieldNameArg} contains a non-JSON value`);
1446
- }
1447
- if (ancestors.has(currentValueArg)) {
1448
- return fail(`${fieldNameArg} contains a cycle`);
1449
- }
1450
-
1451
- ancestors.add(currentValueArg);
1452
- try {
1453
- if (Array.isArray(currentValueArg)) {
1454
- return `[${currentValueArg
1455
- .map((entryArg, indexArg) =>
1456
- visit(entryArg, `${fieldNameArg}[${indexArg}]`),
1457
- )
1458
- .join(',')}]`;
1459
- }
1460
-
1461
- const record = readRecord(currentValueArg, fieldNameArg);
1462
- const keys = Object.keys(record).sort((leftArg, rightArg) => {
1463
- return leftArg < rightArg ? -1 : leftArg > rightArg ? 1 : 0;
1464
- });
1465
- return `{${keys
1466
- .map((keyArg) => {
1467
- assertUnicodeScalarString(keyArg, `${fieldNameArg} key`);
1468
- return `${JSON.stringify(keyArg)}:${visit(
1469
- record[keyArg],
1470
- `${fieldNameArg}.${keyArg}`,
1471
- )}`;
1472
- })
1473
- .join(',')}}`;
1474
- } finally {
1475
- ancestors.delete(currentValueArg);
1476
- }
1477
- };
1478
-
1479
- return visit(valueArg, 'canonical JSON input');
1405
+ return canonicalizeStrictJson(valueArg, fail);
1480
1406
  };
1481
1407
 
1482
1408
  export const canonicalizeIsolatedRestoreResourceMappings = (
@@ -1618,20 +1544,13 @@ export const canonicalizeIsolatedRestoreAuthorityProjection = (
1618
1544
  };
1619
1545
 
1620
1546
  const createSha256 = async (contentsArg: Uint8Array): Promise<string> => {
1621
- const subtle = globalThis.crypto?.subtle;
1622
- if (!subtle) {
1623
- return fail('Web Crypto SHA-256 is unavailable in this runtime');
1624
- }
1625
- const digest = await subtle.digest('SHA-256', new Uint8Array(contentsArg));
1626
- return Array.from(new Uint8Array(digest), (byteArg) =>
1627
- byteArg.toString(16).padStart(2, '0'),
1628
- ).join('');
1547
+ return createSha256Hex(contentsArg, fail);
1629
1548
  };
1630
1549
 
1631
1550
  const createCanonicalJsonSha256 = async (
1632
1551
  canonicalJsonArg: string,
1633
1552
  ): Promise<string> => {
1634
- return createSha256(new TextEncoder().encode(canonicalJsonArg));
1553
+ return createCanonicalJsonSha256Hex(canonicalJsonArg, fail);
1635
1554
  };
1636
1555
 
1637
1556
  export const createIsolatedRestoreBytesSha256 = async (
@@ -9,6 +9,7 @@ import * as pushnotification from './pushnotification.js';
9
9
  import * as sip from './sip.js';
10
10
  import * as sms from './sms.js';
11
11
  import * as storage from './storage.js';
12
+ import * as storagemigration from './storagemigration.js';
12
13
  import * as types from './types.js';
13
14
 
14
15
  export {
@@ -23,8 +24,10 @@ export {
23
24
  sip,
24
25
  sms,
25
26
  storage,
27
+ storagemigration,
26
28
  types,
27
29
  };
28
30
 
29
31
  export * from './storage.js';
32
+ export * from './storagemigration.js';
30
33
  export * from './types.js';