@dreamboard-games/sdk 0.2.0 → 0.2.1-alpha.1
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/dist/{ThemeProvider-fy0_QzgO.d.ts → ThemeProvider-BBMVT3KG.d.ts} +1 -1
- package/dist/attributes-BeRyboMS.d.ts +279 -0
- package/dist/browser-interaction.d.ts +708 -0
- package/dist/browser-interaction.js +106 -0
- package/dist/browser-interaction.js.map +1 -0
- package/dist/{bundle-TIZcw8LB.d.ts → bundle-CDd5FKeD.d.ts} +3 -1
- package/dist/{chunk-U5C6BONG.js → chunk-326PGVAA.js} +2 -2
- package/dist/{chunk-VFTAA4WO.js → chunk-MKXPVOUT.js} +4 -2
- package/dist/chunk-MKXPVOUT.js.map +1 -0
- package/dist/{chunk-GKKBPPSW.js → chunk-MZNVHMJ5.js} +4 -4
- package/dist/{chunk-KAELH4KC.js → chunk-NKCRKGR2.js} +2 -2
- package/dist/{chunk-WN74KVNY.js → chunk-PEI3FIL2.js} +2 -2
- package/dist/chunk-PEI3FIL2.js.map +1 -0
- package/dist/chunk-QLG6VEMW.js +1691 -0
- package/dist/chunk-QLG6VEMW.js.map +1 -0
- package/dist/{chunk-WYPQ3GG5.js → chunk-WG4JQL3S.js} +4 -1
- package/dist/{chunk-WYPQ3GG5.js.map → chunk-WG4JQL3S.js.map} +1 -1
- package/dist/{chunk-7YAHLYBR.js → chunk-XV6D3ET4.js} +8 -4
- package/dist/{chunk-7YAHLYBR.js.map → chunk-XV6D3ET4.js.map} +1 -1
- package/dist/{chunk-TDSWKVZ4.js → chunk-ZABVH7AO.js} +1236 -17
- package/dist/chunk-ZABVH7AO.js.map +1 -0
- package/dist/{components-D5ZRE2Hl.d.ts → components-BoiVSYqx.d.ts} +1 -1
- package/dist/generated/runtime/primitives.d.ts +5 -4
- package/dist/generated/runtime/primitives.js +4 -3
- package/dist/generated/runtime-api.d.ts +1 -1
- package/dist/generated/runtime.d.ts +5 -4
- package/dist/generated/runtime.js +7 -6
- package/dist/generated/workspace-contract.d.ts +5 -4
- package/dist/generated/workspace-contract.js +6 -5
- package/dist/{hex-board-view-D_07hO6O.d.ts → hex-board-view-1iAyJRFn.d.ts} +1 -0
- package/dist/index.js +1 -1
- package/dist/infrastructure/reducer-bundle-abi.d.ts +113 -113
- package/dist/infrastructure/reducer-bundle-abi.js +1 -1
- package/dist/package-set.d.ts +2 -2
- package/dist/package-set.js +1 -1
- package/dist/reducer.d.ts +1 -1
- package/dist/reducer.js +305 -12
- package/dist/reducer.js.map +1 -1
- package/dist/runtime/primitives.d.ts +6 -5
- package/dist/runtime/primitives.js +4 -3
- package/dist/runtime/workspace-contract.d.ts +6 -5
- package/dist/runtime/workspace-contract.js +6 -5
- package/dist/{runtime-api-DWxvTr-O.d.ts → runtime-api-CPLm_XDG.d.ts} +6 -0
- package/dist/runtime.d.ts +5 -4
- package/dist/runtime.js +6 -5
- package/dist/testing.d.ts +2 -2
- package/dist/ui/components.d.ts +2 -2
- package/dist/ui/components.js +1 -1
- package/dist/{ui-contract-iQfTtUSL.d.ts → ui-contract-rzKBwOLC.d.ts} +5 -3
- package/dist/ui.d.ts +5 -5
- package/dist/ui.js +2 -2
- package/package.json +15 -9
- package/src/browser-interaction/attributes.ts +211 -0
- package/src/browser-interaction/canonical.ts +77 -0
- package/src/browser-interaction/constants.ts +77 -0
- package/src/browser-interaction/effects.ts +176 -0
- package/src/browser-interaction/index.ts +111 -0
- package/src/browser-interaction/normalize.ts +997 -0
- package/src/browser-interaction/registry.ts +70 -0
- package/src/browser-interaction/resolve.ts +596 -0
- package/src/browser-interaction/schemas.ts +152 -0
- package/src/browser-interaction/types.ts +304 -0
- package/src/browser-interaction.ts +1 -0
- package/src/generated/reducer-contract/wire.ts +1 -1
- package/src/generated/reducer-contract/zod.ts +3 -1
- package/src/package-set.ts +1 -1
- package/src/reducer/bundle/ingress-bundle.ts +1 -1
- package/src/reducer/bundle/trusted/interaction-types.ts +3 -0
- package/src/reducer/bundle/trusted/projection-builder.ts +337 -13
- package/src/reducer/ingress/input-codec.ts +1 -1
- package/src/reducer/ingress/session-codec.ts +1 -1
- package/src/runtime-internal/components/InteractionForm.tsx +345 -7
- package/src/runtime-internal/components/PluginRuntime.tsx +2 -0
- package/src/runtime-internal/components/board/target-layer.ts +2 -0
- package/src/runtime-internal/context/PluginStateContext.tsx +41 -0
- package/src/runtime-internal/hooks/useBoardInteractions.ts +73 -11
- package/src/runtime-internal/primitives/board.tsx +71 -0
- package/src/runtime-internal/primitives/interaction.tsx +160 -1
- package/src/runtime-internal/types/plugin-state.ts +6 -0
- package/src/runtime-internal/utils/browser-interaction-effects.ts +240 -0
- package/src/runtime-internal/utils/interaction-draft-digest.ts +252 -0
- package/src/runtime-internal/utils/semantic-projection-digest.ts +407 -0
- package/src/ui/components/board/HexGrid.tsx +3 -0
- package/src/ui/components/board/target-layer.ts +1 -0
- package/dist/chunk-TDSWKVZ4.js.map +0 -1
- package/dist/chunk-VFTAA4WO.js.map +0 -1
- package/dist/chunk-WN74KVNY.js.map +0 -1
- /package/dist/{chunk-U5C6BONG.js.map → chunk-326PGVAA.js.map} +0 -0
- /package/dist/{chunk-GKKBPPSW.js.map → chunk-MZNVHMJ5.js.map} +0 -0
- /package/dist/{chunk-KAELH4KC.js.map → chunk-NKCRKGR2.js.map} +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
gameplayAdjustResourceEffect,
|
|
3
|
+
gameplayCommitEffect,
|
|
4
|
+
gameplayInvokeEffect,
|
|
5
|
+
gameplaySetCandidateEffect,
|
|
6
|
+
gameplaySetScalarEffect,
|
|
7
|
+
type CanonicalBrowserInteractionValue,
|
|
8
|
+
type GameplayBrowserInteractionIntent,
|
|
9
|
+
type GameplaySemanticEffect,
|
|
10
|
+
type GameplaySemanticEffectPattern,
|
|
11
|
+
} from "../../browser-interaction/index.js";
|
|
12
|
+
import type {
|
|
13
|
+
InteractionDescriptor,
|
|
14
|
+
InteractionInputDescriptor,
|
|
15
|
+
InputDomain,
|
|
16
|
+
} from "../types/plugin-state.js";
|
|
17
|
+
import {
|
|
18
|
+
inputByKey,
|
|
19
|
+
isTargetDomain,
|
|
20
|
+
resolveInputDomain,
|
|
21
|
+
} from "./interaction-inputs.js";
|
|
22
|
+
|
|
23
|
+
export function gameplayCandidateMetadata(input: {
|
|
24
|
+
readonly descriptor: Pick<InteractionDescriptor, "inputs">;
|
|
25
|
+
readonly draftValues: Readonly<Record<string, unknown>>;
|
|
26
|
+
readonly inputKey: string;
|
|
27
|
+
readonly candidateValue: unknown;
|
|
28
|
+
readonly intent: Extract<
|
|
29
|
+
GameplayBrowserInteractionIntent,
|
|
30
|
+
"select" | "toggle"
|
|
31
|
+
>;
|
|
32
|
+
}): {
|
|
33
|
+
readonly semanticEffects: readonly GameplaySemanticEffect[];
|
|
34
|
+
} {
|
|
35
|
+
const inputDescriptor = resolvedInputByKey(
|
|
36
|
+
input.descriptor,
|
|
37
|
+
input.inputKey,
|
|
38
|
+
input.draftValues,
|
|
39
|
+
);
|
|
40
|
+
if (!inputDescriptor) return { semanticEffects: [] };
|
|
41
|
+
const currentValue = input.draftValues[input.inputKey];
|
|
42
|
+
const beforeSelected = isCandidateSelected(
|
|
43
|
+
currentValue,
|
|
44
|
+
input.candidateValue,
|
|
45
|
+
inputDescriptor,
|
|
46
|
+
);
|
|
47
|
+
const afterSelected =
|
|
48
|
+
input.intent === "toggle" && isManySelection(inputDescriptor)
|
|
49
|
+
? !beforeSelected
|
|
50
|
+
: true;
|
|
51
|
+
return {
|
|
52
|
+
semanticEffects: [
|
|
53
|
+
gameplaySetCandidateEffect({
|
|
54
|
+
inputKey: input.inputKey,
|
|
55
|
+
candidateValue:
|
|
56
|
+
input.candidateValue as CanonicalBrowserInteractionValue,
|
|
57
|
+
beforeSelected,
|
|
58
|
+
afterSelected,
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function gameplayResourceMetadata(input: {
|
|
65
|
+
readonly inputKey: string;
|
|
66
|
+
readonly resourceKey: unknown;
|
|
67
|
+
readonly delta: -1 | 1;
|
|
68
|
+
}): {
|
|
69
|
+
readonly semanticEffects: readonly GameplaySemanticEffect[];
|
|
70
|
+
} {
|
|
71
|
+
return {
|
|
72
|
+
semanticEffects: [
|
|
73
|
+
gameplayAdjustResourceEffect({
|
|
74
|
+
inputKey: input.inputKey,
|
|
75
|
+
resourceKey: input.resourceKey as CanonicalBrowserInteractionValue,
|
|
76
|
+
delta: input.delta,
|
|
77
|
+
}),
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function gameplayScalarStepMetadata(input: {
|
|
83
|
+
readonly inputKey: string;
|
|
84
|
+
readonly value: number;
|
|
85
|
+
}): {
|
|
86
|
+
readonly semanticEffects: readonly GameplaySemanticEffect[];
|
|
87
|
+
} {
|
|
88
|
+
return {
|
|
89
|
+
semanticEffects: [
|
|
90
|
+
gameplaySetScalarEffect({
|
|
91
|
+
inputKey: input.inputKey,
|
|
92
|
+
value: input.value,
|
|
93
|
+
}),
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function gameplayScalarFillMetadata(input: {
|
|
99
|
+
readonly inputKey: string;
|
|
100
|
+
readonly domain: Extract<InputDomain, { type: "boundedNumber" }>;
|
|
101
|
+
}): {
|
|
102
|
+
readonly acceptedEffectPatterns: readonly GameplaySemanticEffectPattern[];
|
|
103
|
+
} {
|
|
104
|
+
return {
|
|
105
|
+
acceptedEffectPatterns: [
|
|
106
|
+
boundedScalarPattern(input.inputKey, input.domain),
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function gameplaySubmitMetadata(input: {
|
|
112
|
+
readonly descriptor: Pick<InteractionDescriptor, "commit">;
|
|
113
|
+
readonly explicitParams?: boolean;
|
|
114
|
+
}): {
|
|
115
|
+
readonly intent: Extract<
|
|
116
|
+
GameplayBrowserInteractionIntent,
|
|
117
|
+
"invoke" | "submit"
|
|
118
|
+
>;
|
|
119
|
+
readonly semanticEffects: readonly GameplaySemanticEffect[];
|
|
120
|
+
} {
|
|
121
|
+
const invoke =
|
|
122
|
+
input.explicitParams === true || input.descriptor.commit.mode !== "manual";
|
|
123
|
+
return invoke
|
|
124
|
+
? { intent: "invoke", semanticEffects: [gameplayInvokeEffect()] }
|
|
125
|
+
: { intent: "submit", semanticEffects: [gameplayCommitEffect()] };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function gameplayPreparationPatternsForDescriptor(
|
|
129
|
+
descriptor: Pick<InteractionDescriptor, "inputs">,
|
|
130
|
+
draftValues: Readonly<Record<string, unknown>>,
|
|
131
|
+
): readonly GameplaySemanticEffectPattern[] {
|
|
132
|
+
const patterns: GameplaySemanticEffectPattern[] = [];
|
|
133
|
+
for (const rawInput of descriptor.inputs) {
|
|
134
|
+
const input = resolveInputDomain(rawInput, draftValues);
|
|
135
|
+
switch (input.domain.type) {
|
|
136
|
+
case "choice":
|
|
137
|
+
case "choiceList":
|
|
138
|
+
case "cardTarget":
|
|
139
|
+
case "boardTarget":
|
|
140
|
+
patterns.push({
|
|
141
|
+
kind: "match",
|
|
142
|
+
effectKind: "setCandidate",
|
|
143
|
+
fields: { inputKey: input.key },
|
|
144
|
+
});
|
|
145
|
+
break;
|
|
146
|
+
case "resourceMap":
|
|
147
|
+
patterns.push({
|
|
148
|
+
kind: "match",
|
|
149
|
+
effectKind: "adjustResource",
|
|
150
|
+
fields: { inputKey: input.key },
|
|
151
|
+
});
|
|
152
|
+
break;
|
|
153
|
+
case "boundedNumber":
|
|
154
|
+
patterns.push(boundedScalarPattern(input.key, input.domain));
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return dedupePatterns(patterns);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function boundedScalarPattern(
|
|
162
|
+
inputKey: string,
|
|
163
|
+
domain: Extract<InputDomain, { type: "boundedNumber" }>,
|
|
164
|
+
): GameplaySemanticEffectPattern {
|
|
165
|
+
const min = domain.min ?? 0;
|
|
166
|
+
const max = domain.max;
|
|
167
|
+
return {
|
|
168
|
+
kind: "match",
|
|
169
|
+
effectKind: "setScalar",
|
|
170
|
+
fields: { inputKey },
|
|
171
|
+
scalar: {
|
|
172
|
+
field: "value",
|
|
173
|
+
min,
|
|
174
|
+
...(max !== undefined && Number.isFinite(max) ? { max } : {}),
|
|
175
|
+
...(scalarIsInteger(domain) ? { integer: true } : {}),
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function resolvedInputByKey(
|
|
181
|
+
descriptor: Pick<InteractionDescriptor, "inputs">,
|
|
182
|
+
inputKey: string,
|
|
183
|
+
draftValues: Readonly<Record<string, unknown>>,
|
|
184
|
+
): InteractionInputDescriptor | undefined {
|
|
185
|
+
const input = inputByKey(descriptor, inputKey);
|
|
186
|
+
return input ? resolveInputDomain(input, draftValues) : undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isCandidateSelected(
|
|
190
|
+
currentValue: unknown,
|
|
191
|
+
candidateValue: unknown,
|
|
192
|
+
input: InteractionInputDescriptor,
|
|
193
|
+
): boolean {
|
|
194
|
+
if (isTargetDomain(input.domain) && input.domain.selection?.mode === "many") {
|
|
195
|
+
return valueArrayIncludes(currentValue, candidateValue);
|
|
196
|
+
}
|
|
197
|
+
if (input.domain.type === "choiceList") {
|
|
198
|
+
return valueArrayIncludes(currentValue, candidateValue);
|
|
199
|
+
}
|
|
200
|
+
return String(currentValue) === String(candidateValue);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isManySelection(input: InteractionInputDescriptor): boolean {
|
|
204
|
+
if (input.domain.type === "choiceList") return true;
|
|
205
|
+
return (
|
|
206
|
+
isTargetDomain(input.domain) && input.domain.selection?.mode === "many"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function valueArrayIncludes(currentValue: unknown, candidateValue: unknown) {
|
|
211
|
+
return (
|
|
212
|
+
Array.isArray(currentValue) &&
|
|
213
|
+
currentValue.some((item) => String(item) === String(candidateValue))
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function scalarIsInteger(
|
|
218
|
+
domain: Extract<InputDomain, { type: "boundedNumber" }>,
|
|
219
|
+
): boolean {
|
|
220
|
+
const step = domain.step ?? 1;
|
|
221
|
+
return (
|
|
222
|
+
Number.isInteger(step) &&
|
|
223
|
+
Number.isInteger(domain.min ?? 0) &&
|
|
224
|
+
(domain.max === undefined || Number.isInteger(domain.max))
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function dedupePatterns(
|
|
229
|
+
patterns: readonly GameplaySemanticEffectPattern[],
|
|
230
|
+
): readonly GameplaySemanticEffectPattern[] {
|
|
231
|
+
const seen = new Set<string>();
|
|
232
|
+
const result: GameplaySemanticEffectPattern[] = [];
|
|
233
|
+
for (const pattern of patterns) {
|
|
234
|
+
const key = JSON.stringify(pattern);
|
|
235
|
+
if (seen.has(key)) continue;
|
|
236
|
+
seen.add(key);
|
|
237
|
+
result.push(pattern);
|
|
238
|
+
}
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type { InteractionDescriptor } from "../types/plugin-state.js";
|
|
2
|
+
|
|
3
|
+
type CanonicalJson =
|
|
4
|
+
| null
|
|
5
|
+
| boolean
|
|
6
|
+
| number
|
|
7
|
+
| string
|
|
8
|
+
| CanonicalJson[]
|
|
9
|
+
| { [key: string]: CanonicalJson };
|
|
10
|
+
|
|
11
|
+
export function interactionDraftDigestForValues(
|
|
12
|
+
descriptor: InteractionDescriptor,
|
|
13
|
+
values: Readonly<Record<string, unknown>>,
|
|
14
|
+
): string | undefined {
|
|
15
|
+
if (
|
|
16
|
+
descriptor.descriptorDigest === undefined ||
|
|
17
|
+
descriptor.actorSeat === undefined ||
|
|
18
|
+
descriptor.draftDigest === undefined
|
|
19
|
+
) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const defaults = defaultsForDescriptor(descriptor);
|
|
23
|
+
return draftDigest({
|
|
24
|
+
actorSeat: descriptor.actorSeat,
|
|
25
|
+
descriptorDigest: descriptor.descriptorDigest,
|
|
26
|
+
interactionId: descriptor.interactionId,
|
|
27
|
+
interactionKey: descriptor.interactionKey,
|
|
28
|
+
values: {
|
|
29
|
+
...defaults,
|
|
30
|
+
...(toCanonicalJson(values) as Record<string, CanonicalJson>),
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function draftDigest({
|
|
36
|
+
actorSeat,
|
|
37
|
+
descriptorDigest,
|
|
38
|
+
interactionId,
|
|
39
|
+
interactionKey,
|
|
40
|
+
values,
|
|
41
|
+
}: {
|
|
42
|
+
actorSeat: number;
|
|
43
|
+
descriptorDigest: string;
|
|
44
|
+
interactionId: string;
|
|
45
|
+
interactionKey: string;
|
|
46
|
+
values: CanonicalJson;
|
|
47
|
+
}): string {
|
|
48
|
+
return hashJson({
|
|
49
|
+
digestVersion: "interaction-draft@2",
|
|
50
|
+
actorSeat,
|
|
51
|
+
descriptorDigest,
|
|
52
|
+
emitted: false,
|
|
53
|
+
interactionId,
|
|
54
|
+
interactionKey,
|
|
55
|
+
values,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function defaultsForDescriptor(
|
|
60
|
+
descriptor: InteractionDescriptor,
|
|
61
|
+
): Record<string, CanonicalJson> {
|
|
62
|
+
return Object.fromEntries(
|
|
63
|
+
descriptor.inputs.flatMap((input) =>
|
|
64
|
+
input.defaultValue === undefined
|
|
65
|
+
? []
|
|
66
|
+
: [[input.key, toCanonicalJson(input.defaultValue)]],
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function toCanonicalJson(value: unknown): CanonicalJson {
|
|
72
|
+
if (
|
|
73
|
+
value === null ||
|
|
74
|
+
typeof value === "boolean" ||
|
|
75
|
+
typeof value === "string"
|
|
76
|
+
) {
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
if (typeof value === "number") {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
return value.map((item) => toCanonicalJson(item));
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === "object") {
|
|
86
|
+
return Object.fromEntries(
|
|
87
|
+
Object.entries(value as Record<string, unknown>)
|
|
88
|
+
.filter(([, item]) => item !== undefined)
|
|
89
|
+
.map(([key, item]) => [key, toCanonicalJson(item)]),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function hashJson(value: CanonicalJson): string {
|
|
96
|
+
return `sha256:${sha256Hex(canonicalJson(value))}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function canonicalJson(value: CanonicalJson): string {
|
|
100
|
+
return JSON.stringify(canonicalizeJson(value));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function canonicalizeJson(value: CanonicalJson): CanonicalJson {
|
|
104
|
+
if (
|
|
105
|
+
value === null ||
|
|
106
|
+
typeof value === "boolean" ||
|
|
107
|
+
typeof value === "string"
|
|
108
|
+
) {
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === "number") {
|
|
112
|
+
if (!Number.isFinite(value)) {
|
|
113
|
+
throw new Error("canonical JSON contains a non-finite number");
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(value)) {
|
|
118
|
+
return value.map((item) => canonicalizeJson(item));
|
|
119
|
+
}
|
|
120
|
+
return Object.fromEntries(
|
|
121
|
+
Object.entries(value)
|
|
122
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
123
|
+
.map(([key, item]) => [key, canonicalizeJson(item)]),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function sha256Hex(input: string): string {
|
|
128
|
+
const bytes = utf8Bytes(input);
|
|
129
|
+
const bitLength = bytes.length * 8;
|
|
130
|
+
bytes.push(0x80);
|
|
131
|
+
while (bytes.length % 64 !== 56) {
|
|
132
|
+
bytes.push(0);
|
|
133
|
+
}
|
|
134
|
+
for (let shift = 56; shift >= 0; shift -= 8) {
|
|
135
|
+
bytes.push(Math.floor(bitLength / 2 ** shift) & 0xff);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let h0 = 0x6a09e667;
|
|
139
|
+
let h1 = 0xbb67ae85;
|
|
140
|
+
let h2 = 0x3c6ef372;
|
|
141
|
+
let h3 = 0xa54ff53a;
|
|
142
|
+
let h4 = 0x510e527f;
|
|
143
|
+
let h5 = 0x9b05688c;
|
|
144
|
+
let h6 = 0x1f83d9ab;
|
|
145
|
+
let h7 = 0x5be0cd19;
|
|
146
|
+
|
|
147
|
+
const words = new Array<number>(64);
|
|
148
|
+
for (let offset = 0; offset < bytes.length; offset += 64) {
|
|
149
|
+
for (let i = 0; i < 16; i += 1) {
|
|
150
|
+
words[i] =
|
|
151
|
+
((bytes[offset + i * 4] ?? 0) << 24) |
|
|
152
|
+
((bytes[offset + i * 4 + 1] ?? 0) << 16) |
|
|
153
|
+
((bytes[offset + i * 4 + 2] ?? 0) << 8) |
|
|
154
|
+
(bytes[offset + i * 4 + 3] ?? 0);
|
|
155
|
+
}
|
|
156
|
+
for (let i = 16; i < 64; i += 1) {
|
|
157
|
+
words[i] =
|
|
158
|
+
(smallSigma1(words[i - 2] ?? 0) +
|
|
159
|
+
(words[i - 7] ?? 0) +
|
|
160
|
+
smallSigma0(words[i - 15] ?? 0) +
|
|
161
|
+
(words[i - 16] ?? 0)) >>>
|
|
162
|
+
0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let a = h0;
|
|
166
|
+
let b = h1;
|
|
167
|
+
let c = h2;
|
|
168
|
+
let d = h3;
|
|
169
|
+
let e = h4;
|
|
170
|
+
let f = h5;
|
|
171
|
+
let g = h6;
|
|
172
|
+
let h = h7;
|
|
173
|
+
|
|
174
|
+
for (let i = 0; i < 64; i += 1) {
|
|
175
|
+
const t1 =
|
|
176
|
+
(h +
|
|
177
|
+
bigSigma1(e) +
|
|
178
|
+
ch(e, f, g) +
|
|
179
|
+
K[i] +
|
|
180
|
+
(words[i] ?? 0)) >>>
|
|
181
|
+
0;
|
|
182
|
+
const t2 = (bigSigma0(a) + maj(a, b, c)) >>> 0;
|
|
183
|
+
h = g;
|
|
184
|
+
g = f;
|
|
185
|
+
f = e;
|
|
186
|
+
e = (d + t1) >>> 0;
|
|
187
|
+
d = c;
|
|
188
|
+
c = b;
|
|
189
|
+
b = a;
|
|
190
|
+
a = (t1 + t2) >>> 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
h0 = (h0 + a) >>> 0;
|
|
194
|
+
h1 = (h1 + b) >>> 0;
|
|
195
|
+
h2 = (h2 + c) >>> 0;
|
|
196
|
+
h3 = (h3 + d) >>> 0;
|
|
197
|
+
h4 = (h4 + e) >>> 0;
|
|
198
|
+
h5 = (h5 + f) >>> 0;
|
|
199
|
+
h6 = (h6 + g) >>> 0;
|
|
200
|
+
h7 = (h7 + h) >>> 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return [h0, h1, h2, h3, h4, h5, h6, h7]
|
|
204
|
+
.map((word) => word.toString(16).padStart(8, "0"))
|
|
205
|
+
.join("");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function utf8Bytes(input: string): number[] {
|
|
209
|
+
return Array.from(new TextEncoder().encode(input));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function rightRotate(value: number, bits: number): number {
|
|
213
|
+
return (value >>> bits) | (value << (32 - bits));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function ch(x: number, y: number, z: number): number {
|
|
217
|
+
return (x & y) ^ (~x & z);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function maj(x: number, y: number, z: number): number {
|
|
221
|
+
return (x & y) ^ (x & z) ^ (y & z);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function bigSigma0(x: number): number {
|
|
225
|
+
return rightRotate(x, 2) ^ rightRotate(x, 13) ^ rightRotate(x, 22);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function bigSigma1(x: number): number {
|
|
229
|
+
return rightRotate(x, 6) ^ rightRotate(x, 11) ^ rightRotate(x, 25);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function smallSigma0(x: number): number {
|
|
233
|
+
return rightRotate(x, 7) ^ rightRotate(x, 18) ^ (x >>> 3);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function smallSigma1(x: number): number {
|
|
237
|
+
return rightRotate(x, 17) ^ rightRotate(x, 19) ^ (x >>> 10);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const K = [
|
|
241
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
242
|
+
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
243
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
244
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
245
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
|
246
|
+
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
247
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
|
248
|
+
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
249
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
250
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
251
|
+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
252
|
+
];
|