@puzzmo/sdk 1.0.11 → 1.0.13

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 (44) hide show
  1. package/README.md +4 -4
  2. package/dist/asyncToGenerator-BlxRHn40.cjs +1 -0
  3. package/dist/asyncToGenerator-CPSNHDFw.js +25 -0
  4. package/dist/{createSimulator-BmeD2jIP.cjs → createSimulator-CGTMmToi.cjs} +28 -28
  5. package/dist/{createSimulator-BmeD2jIP.cjs.map → createSimulator-CGTMmToi.cjs.map} +1 -1
  6. package/dist/{createSimulator-D-ln1AMv.js → createSimulator-IMuPxYe-.js} +3 -2
  7. package/dist/{createSimulator-D-ln1AMv.js.map → createSimulator-IMuPxYe-.js.map} +1 -1
  8. package/dist/index.cjs +1 -1
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.js +16 -9
  11. package/dist/index.js.map +1 -1
  12. package/dist/inputs/decoder.d.ts +4 -0
  13. package/dist/inputs/decoder.d.ts.map +1 -0
  14. package/dist/inputs/encoder.d.ts +4 -0
  15. package/dist/inputs/encoder.d.ts.map +1 -0
  16. package/dist/inputs/encodings.d.ts +11 -0
  17. package/dist/inputs/encodings.d.ts.map +1 -0
  18. package/dist/inputs/index.cjs +2 -0
  19. package/dist/inputs/index.cjs.map +1 -0
  20. package/dist/inputs/index.d.ts +57 -0
  21. package/dist/inputs/index.d.ts.map +1 -0
  22. package/dist/inputs/index.js +240 -0
  23. package/dist/inputs/index.js.map +1 -0
  24. package/dist/inputs/inputStringEncoder.spec.d.ts +2 -0
  25. package/dist/inputs/inputStringEncoder.spec.d.ts.map +1 -0
  26. package/dist/inputs/migration.d.ts +3 -0
  27. package/dist/inputs/migration.d.ts.map +1 -0
  28. package/dist/inputs/types.d.ts +297 -0
  29. package/dist/inputs/types.d.ts.map +1 -0
  30. package/dist/objectSpread2-B6tAAMuy.cjs +1 -0
  31. package/dist/objectSpread2-vLYiAtaU.js +52 -0
  32. package/dist/sdk.d.ts +1 -1
  33. package/dist/sdk.d.ts.map +1 -1
  34. package/dist/simulator/index.cjs +1 -1
  35. package/dist/simulator/index.js +1 -1
  36. package/dist/simulator/standalone.cjs +1 -1
  37. package/dist/simulator/standalone.js +1 -1
  38. package/dist/vite.cjs +3 -3
  39. package/dist/vite.cjs.map +1 -1
  40. package/dist/vite.js +2 -1
  41. package/dist/vite.js.map +1 -1
  42. package/package.json +9 -1
  43. package/dist/objectSpread2-C4RR_HMd.cjs +0 -1
  44. package/dist/objectSpread2-CJo2CZQ6.js +0 -76
@@ -0,0 +1,240 @@
1
+ import { t as e } from "../objectSpread2-vLYiAtaU.js";
2
+ import { compressToEncodedURIComponent as t, decompressFromEncodedURIComponent as n } from "lz-string";
3
+ function r(e) {
4
+ if (e.length === 0) return "0";
5
+ let t = "";
6
+ for (let n = 0; n < e.length; n += 4) {
7
+ let r = 0;
8
+ for (let t = 0; t < 4 && n + t < e.length; t++) e[n + t] && (r |= 1 << t);
9
+ t += r.toString(16);
10
+ }
11
+ return t || "0";
12
+ }
13
+ function i(e, t) {
14
+ let n = [];
15
+ for (let r = 0; r < t; r++) {
16
+ let t = Math.floor(r / 4), i = r % 4, a = parseInt(e[t] || "0", 16);
17
+ n.push((a & 1 << i) != 0);
18
+ }
19
+ return n;
20
+ }
21
+ function a(e) {
22
+ if (e.length === 0) return "";
23
+ let t = e.map((e, t) => ({
24
+ val: e,
25
+ idx: t
26
+ })).filter((e) => e.val !== 0);
27
+ return t.length === 0 ? "" : t.length > e.length / 2 ? e.join(",") : t.map((e) => `${e.idx}=${e.val}`).join(",");
28
+ }
29
+ function o(e, t) {
30
+ if (!e) return t ? Array.from({ length: t }, () => 0) : [];
31
+ if (e.includes("=")) {
32
+ let n = t ? Array.from({ length: t }, () => 0) : [], r = e.split(",");
33
+ for (let e of r) {
34
+ let [t, r] = e.split("="), i = Number(t), a = Number(r);
35
+ !isNaN(i) && !isNaN(a) && (n[i] = a);
36
+ }
37
+ return n;
38
+ }
39
+ return e.split(",").map((e) => Number(e));
40
+ }
41
+ function s(e, t = ",") {
42
+ return e.join(t);
43
+ }
44
+ function c(e, t = ",") {
45
+ return e ? e.split(t) : [];
46
+ }
47
+ function l(e) {
48
+ return t(JSON.stringify(e));
49
+ }
50
+ function u(e) {
51
+ if (!e) return;
52
+ let t = n(e);
53
+ return t ? JSON.parse(t) : void 0;
54
+ }
55
+ function d(e, t, n) {
56
+ let r = Object.entries(e).filter(([e, t]) => t != null);
57
+ return n ? r.map(([e, r]) => {
58
+ let i = n.indexOf(e);
59
+ return i === -1 ? null : `${i}=${t(r)}`;
60
+ }).filter((e) => e !== null).join(",") : r.map(([e, n]) => `${e}=${t(n)}`).join(";");
61
+ }
62
+ function f(e, t, n) {
63
+ if (!e) return {};
64
+ let r = {};
65
+ if (n) {
66
+ let i = e.split(",");
67
+ for (let e of i) {
68
+ if (!e) continue;
69
+ let [i, a] = e.split("="), o = Number(i);
70
+ !isNaN(o) && a && n[o] && (r[n[o]] = t(a));
71
+ }
72
+ } else {
73
+ let n = e.split(";");
74
+ for (let e of n) {
75
+ if (!e) continue;
76
+ let [n, i] = e.split("=");
77
+ n && i && (r[n] = t(i));
78
+ }
79
+ }
80
+ return r;
81
+ }
82
+ function p(e, t, n) {
83
+ switch (t.type) {
84
+ case "bitArray": return r(e);
85
+ case "intArray": return a(e);
86
+ case "int": return String(e);
87
+ case "string": return String(e);
88
+ case "stringArray": return s(e, t.delimiter);
89
+ case "sparseMap": return d(e, t.valueEncoder, n == null ? void 0 : n.keys);
90
+ case "json": return l(e);
91
+ default: throw Error(`Unknown field type: ${t.type}`);
92
+ }
93
+ }
94
+ function m(e, t, n) {
95
+ let r = e.delimiter || ":", i = Object.keys(e.fields).map((r) => {
96
+ let i = e.fields[r], a = t[r];
97
+ return p(a, i, n == null ? void 0 : n[r]);
98
+ });
99
+ return `v${e.version}${r}${i.join(r)}`;
100
+ }
101
+ function h(e, t, n) {
102
+ switch (t.type) {
103
+ case "bitArray":
104
+ if (!(n != null && n.length)) throw Error("bitArray requires length in context");
105
+ return i(e, n.length);
106
+ case "intArray": return o(e, n == null ? void 0 : n.length);
107
+ case "int": return Number(e);
108
+ case "string": return e;
109
+ case "stringArray": return c(e, t.delimiter);
110
+ case "sparseMap": return f(e, t.valueDecoder, n == null ? void 0 : n.keys);
111
+ case "json": return u(e);
112
+ default: throw Error(`Unknown field type: ${t.type}`);
113
+ }
114
+ }
115
+ function g(e, t, n) {
116
+ let r = e.delimiter || ":", i = t.split(r), a = i[0], o = a.match(/^v(\d+)$/);
117
+ if (!o) throw Error(`Invalid input string format: ${a}`);
118
+ let s = Number(o[1]);
119
+ if (s !== e.version) throw Error(`Version mismatch: expected v${e.version}, got v${s}. Make sure to run migrations first.`);
120
+ let c = Object.keys(e.fields), l = {};
121
+ for (let t = 0; t < c.length; t++) {
122
+ let r = c[t], a = e.fields[r];
123
+ l[r] = h(i[t + 1] || "", a, n == null ? void 0 : n[r]);
124
+ }
125
+ return l;
126
+ }
127
+ function _(t) {
128
+ return function(n, r) {
129
+ let i = n.match(/^v(\d+)/);
130
+ if (!i) throw Error("Invalid input string: no version found");
131
+ let a = Number(i[1]);
132
+ if (a === t.version) return n;
133
+ if (!t.migrations) throw Error(`No migrations defined for schema. Cannot migrate from v${a} to v${t.version}`);
134
+ let o = g(e(e({}, t), {}, { version: a }), n, r);
135
+ for (let e = a; e < t.version; e++) {
136
+ let n = t.migrations[e];
137
+ n && (o = n(o));
138
+ }
139
+ return m(t, o, r);
140
+ };
141
+ }
142
+ /**
143
+ * Define a typed schema for encoding/decoding game state.
144
+ *
145
+ * **Purpose:**
146
+ * - Provides type inference for encode/decode functions
147
+ * - Documents the structure of your input strings
148
+ * - Enables version migrations
149
+ *
150
+ * **Example:**
151
+ * ```typescript
152
+ * type GameData = {
153
+ * revealed: boolean[]
154
+ * score: number
155
+ * annotations: Record<string, Annotation>
156
+ * }
157
+ *
158
+ * export const schema = defineSchema<GameData>({
159
+ * version: 1,
160
+ * fields: {
161
+ * revealed: { type: 'bitArray' },
162
+ * score: { type: 'int' },
163
+ * annotations: {
164
+ * type: 'sparseMap',
165
+ * valueEncoder: (ann) => `${ann.level}.${ann.color}`,
166
+ * valueDecoder: (str) => {
167
+ * const [level, color] = str.split('.').map(Number)
168
+ * return { level, color }
169
+ * }
170
+ * }
171
+ * }
172
+ * })
173
+ * ```
174
+ */
175
+ function v(e) {
176
+ return e;
177
+ }
178
+ /**
179
+ * Create an encoder/decoder for a schema-defined data structure.
180
+ *
181
+ * **Basic usage:**
182
+ * ```typescript
183
+ * import { defineSchema, createEncoder } from '@puzzmo/sdk/inputs'
184
+ *
185
+ * const schema = defineSchema({
186
+ * version: 1,
187
+ * fields: {
188
+ * name: { type: 'string' },
189
+ * age: { type: 'int' }
190
+ * }
191
+ * })
192
+ *
193
+ * const { encode, decode } = createEncoder(schema)
194
+ *
195
+ * const data = { name: 'Alice', age: 30 }
196
+ * const inputString = encode(data) // "v1:Alice:30"
197
+ * const decoded = decode(inputString) // { name: 'Alice', age: 30 }
198
+ * ```
199
+ *
200
+ * **With context (for length-dependent fields):**
201
+ * ```typescript
202
+ * const schema = defineSchema({
203
+ * version: 1,
204
+ * fields: {
205
+ * revealed: { type: 'bitArray' },
206
+ * scores: { type: 'intArray' }
207
+ * }
208
+ * })
209
+ *
210
+ * const { encode, decode } = createEncoder(schema, {
211
+ * revealed: { length: 100 }, // Required for bitArray decoding
212
+ * scores: { length: 10 }, // Required for sparse intArray decoding
213
+ * annotations: { keys: tileIds } // Optional: optimize sparseMap with numeric indices
214
+ * })
215
+ * ```
216
+ *
217
+ * **With migrations:**
218
+ * ```typescript
219
+ * const { encode, decode, migrate } = createEncoder(schema)
220
+ *
221
+ * const oldString = "v0:Alice:30"
222
+ * const migrated = migrate(oldString) // "v1:Alice:30:default"
223
+ * const data = decode(migrated)
224
+ * ```
225
+ *
226
+ * @param schema - Schema definition created with defineSchema()
227
+ * @param context - Optional context for decoding (lengths for bitArray/intArray)
228
+ * @returns Object with encode, decode, and migrate functions
229
+ */
230
+ function y(e, t) {
231
+ let n = _(e);
232
+ return {
233
+ encode: (n) => m(e, n, t),
234
+ decode: (n) => g(e, n, t),
235
+ migrate: (e) => n(e, t)
236
+ };
237
+ }
238
+ export { y as createEncoder, v as defineSchema };
239
+
240
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/inputs/encodings.ts","../../src/inputs/encoder.ts","../../src/inputs/decoder.ts","../../src/inputs/migration.ts","../../src/inputs/types.ts","../../src/inputs/index.ts"],"sourcesContent":["import { compressToEncodedURIComponent, decompressFromEncodedURIComponent } from \"lz-string\"\n\nexport function encodeBitArrayToHex(bits: boolean[]): string {\n if (bits.length === 0) return \"0\"\n\n let hex = \"\"\n for (let i = 0; i < bits.length; i += 4) {\n let nibble = 0\n for (let j = 0; j < 4 && i + j < bits.length; j++) {\n if (bits[i + j]) {\n nibble |= 1 << j\n }\n }\n hex += nibble.toString(16)\n }\n\n return hex || \"0\"\n}\n\nexport function decodeBitArrayFromHex(hex: string, length: number): boolean[] {\n const bits: boolean[] = []\n\n for (let i = 0; i < length; i++) {\n const hexIndex = Math.floor(i / 4)\n const bitIndex = i % 4\n const nibble = parseInt(hex[hexIndex] || \"0\", 16)\n bits.push((nibble & (1 << bitIndex)) !== 0)\n }\n\n return bits\n}\n\nexport function encodeIntArray(arr: number[]): string {\n if (arr.length === 0) return \"\"\n\n const nonZero = arr.map((val, idx) => ({ val, idx })).filter((item) => item.val !== 0)\n\n if (nonZero.length === 0) return \"\"\n if (nonZero.length > arr.length / 2) {\n return arr.join(\",\")\n }\n\n return nonZero.map((item) => `${item.idx}=${item.val}`).join(\",\")\n}\n\nexport function decodeIntArray(str: string, defaultLength?: number): number[] {\n if (!str) return defaultLength ? Array.from({ length: defaultLength }, () => 0) : []\n\n if (str.includes(\"=\")) {\n const result = defaultLength ? Array.from({ length: defaultLength }, () => 0) : []\n const pairs = str.split(\",\")\n for (const pair of pairs) {\n const [idxStr, valStr] = pair.split(\"=\")\n const idx = Number(idxStr)\n const val = Number(valStr)\n if (!isNaN(idx) && !isNaN(val)) {\n result[idx] = val\n }\n }\n return result\n }\n\n return str.split(\",\").map((x) => Number(x))\n}\n\nexport function encodeStringArray(arr: string[], delimiter = \",\"): string {\n return arr.join(delimiter)\n}\n\nexport function decodeStringArray(str: string, delimiter = \",\"): string[] {\n if (!str) return []\n return str.split(delimiter)\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- JSON can encode any data\nexport function encodeJson(data: any): string {\n const json = JSON.stringify(data)\n return compressToEncodedURIComponent(json)\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- JSON can decode to any data\nexport function decodeJson(str: string): any {\n if (!str) return undefined\n const json = decompressFromEncodedURIComponent(str)\n return json ? JSON.parse(json) : undefined\n}\n\nexport function encodeSparseMap<T>(map: Record<string, T>, encodeValue: (val: T) => string, orderedKeys?: string[]): string {\n const entries = Object.entries(map).filter(([_, val]) => val !== null && val !== undefined)\n\n if (orderedKeys) {\n // Use numeric indices instead of string keys\n const indexedEntries = entries\n .map(([key, val]) => {\n const idx = orderedKeys.indexOf(key)\n if (idx === -1) return null\n return `${idx}=${encodeValue(val)}`\n })\n .filter((entry): entry is string => entry !== null)\n\n return indexedEntries.join(\",\")\n } else {\n // Fall back to string keys\n return entries.map(([key, val]) => `${key}=${encodeValue(val)}`).join(\";\")\n }\n}\n\nexport function decodeSparseMap<T>(str: string, decodeValue: (val: string) => T, orderedKeys?: string[]): Record<string, T> {\n if (!str) return {}\n\n const result: Record<string, T> = {}\n\n if (orderedKeys) {\n // Decode numeric indices\n const entries = str.split(\",\")\n for (const entry of entries) {\n if (!entry) continue\n const [idxStr, val] = entry.split(\"=\")\n const idx = Number(idxStr)\n if (!isNaN(idx) && val && orderedKeys[idx]) {\n result[orderedKeys[idx]] = decodeValue(val)\n }\n }\n } else {\n // Decode string keys\n const entries = str.split(\";\")\n for (const entry of entries) {\n if (!entry) continue\n const [key, val] = entry.split(\"=\")\n if (key && val) {\n result[key] = decodeValue(val)\n }\n }\n }\n\n return result\n}\n","import type { Schema, FieldType } from \"./types\"\nimport { encodeBitArrayToHex, encodeIntArray, encodeStringArray, encodeSparseMap, encodeJson } from \"./encodings\"\n\nexport function encodeField(value: any, fieldType: FieldType, context?: any): string {\n switch (fieldType.type) {\n case \"bitArray\":\n return encodeBitArrayToHex(value)\n\n case \"intArray\":\n return encodeIntArray(value)\n\n case \"int\":\n return String(value)\n\n case \"string\":\n return String(value)\n\n case \"stringArray\":\n return encodeStringArray(value, fieldType.delimiter)\n\n case \"sparseMap\":\n return encodeSparseMap(value, fieldType.valueEncoder, context?.keys)\n\n case \"json\":\n return encodeJson(value)\n\n default:\n throw new Error(`Unknown field type: ${(fieldType as any).type}`)\n }\n}\n\nexport function encode<TData>(schema: Schema<TData>, data: TData, context?: Record<string, any>): string {\n const delimiter = schema.delimiter || \":\"\n const fieldNames = Object.keys(schema.fields) as (keyof TData)[]\n\n const encodedFields = fieldNames.map((fieldName) => {\n const fieldType = schema.fields[fieldName]\n const value = data[fieldName]\n const fieldContext = context?.[fieldName as string]\n return encodeField(value, fieldType, fieldContext)\n })\n\n return `v${schema.version}${delimiter}${encodedFields.join(delimiter)}`\n}\n","import type { Schema, FieldType } from \"./types\"\nimport { decodeBitArrayFromHex, decodeIntArray, decodeStringArray, decodeSparseMap, decodeJson } from \"./encodings\"\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- context is dynamic and return type depends on fieldType\nexport function decodeField(value: string, fieldType: FieldType, context?: any): any {\n switch (fieldType.type) {\n case \"bitArray\":\n if (!context?.length) {\n throw new Error(\"bitArray requires length in context\")\n }\n return decodeBitArrayFromHex(value, context.length)\n\n case \"intArray\":\n return decodeIntArray(value, context?.length)\n\n case \"int\":\n return Number(value)\n\n case \"string\":\n return value\n\n case \"stringArray\":\n return decodeStringArray(value, fieldType.delimiter)\n\n case \"sparseMap\":\n return decodeSparseMap(value, fieldType.valueDecoder, context?.keys)\n\n case \"json\":\n return decodeJson(value)\n\n default:\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- used in error message for invalid type\n throw new Error(`Unknown field type: ${(fieldType as any).type}`)\n }\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- context values are dynamic\nexport function decode<TData>(schema: Schema<TData>, inputString: string, context?: Record<string, any>): TData {\n const delimiter = schema.delimiter || \":\"\n const parts = inputString.split(delimiter)\n\n const versionPart = parts[0]\n const versionMatch = versionPart.match(/^v(\\d+)$/)\n if (!versionMatch) {\n throw new Error(`Invalid input string format: ${versionPart}`)\n }\n\n const version = Number(versionMatch[1])\n if (version !== schema.version) {\n throw new Error(`Version mismatch: expected v${schema.version}, got v${version}. ` + `Make sure to run migrations first.`)\n }\n\n const fieldNames = Object.keys(schema.fields) as (keyof TData)[]\n const data = {} as TData\n\n for (let i = 0; i < fieldNames.length; i++) {\n const fieldName = fieldNames[i]\n const fieldType = schema.fields[fieldName]\n const fieldValue = parts[i + 1] || \"\"\n\n const fieldContext = context?.[fieldName as string]\n data[fieldName] = decodeField(fieldValue, fieldType, fieldContext)\n }\n\n return data\n}\n","import type { Schema } from \"./types\"\nimport { encode } from \"./encoder\"\nimport { decode } from \"./decoder\"\n\nexport function createMigrator<TData>(schema: Schema<TData>) {\n return function migrate(inputString: string, context?: Record<string, any>): string {\n const versionMatch = inputString.match(/^v(\\d+)/)\n if (!versionMatch) {\n throw new Error(\"Invalid input string: no version found\")\n }\n\n const currentVersion = Number(versionMatch[1])\n\n if (currentVersion === schema.version) {\n return inputString\n }\n\n if (!schema.migrations) {\n throw new Error(`No migrations defined for schema. Cannot migrate from v${currentVersion} to v${schema.version}`)\n }\n\n // Data type changes at each migration step (v0 → v1 → v2, etc.)\n // TypeScript can't track these transformations statically, so we use `any`\n // Migrations are developer-written and tested to maintain type safety through the chain\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let data: any = decode(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n { ...schema, version: currentVersion } as Schema<any>,\n inputString,\n context,\n )\n\n for (let v = currentVersion; v < schema.version; v++) {\n const migration = schema.migrations[v]\n if (migration) {\n data = migration(data)\n }\n }\n\n return encode(schema, data, context)\n }\n}\n","/**\n * Encodes boolean arrays as compact hexadecimal strings.\n *\n * **When to use:**\n * - Revealed/hidden state of game tiles\n * - Feature flags or settings\n * - Any boolean array where order matters\n *\n * **Encoding:**\n * - Packs 4 bits per hex character (75% size reduction)\n * - Example: [true, false, false, false, false, true, false, false] → \"12\"\n *\n * **Decoding:**\n * - Requires length in context: `{ fieldName: { length: 100 } }`\n */\nexport type BitArrayFieldType = {\n type: \"bitArray\"\n}\n\n/**\n * Encodes integer arrays with automatic sparse optimization.\n *\n * **When to use:**\n * - Arrays of numeric values (scores, counts, experience)\n * - Sparse data (mostly zeros)\n * - Dense data (will auto-fallback to CSV)\n *\n * **Encoding:**\n * - Uses decimal (base-10) numbers separated by commas\n * - Smart sparse: If >50% zeros, uses `index=value` pairs (e.g., \"2=5,4=3\")\n * - Dense fallback: If not beneficial, uses CSV (e.g., \"1,2,3,4\")\n * - Empty arrays encode as empty string\n *\n * **Decoding:**\n * - Requires length in context for sparse format: `{ fieldName: { length: 100 } }`\n * - CSV format decodes without context\n */\nexport type IntArrayFieldType = {\n type: \"intArray\"\n}\n\n/**\n * Encodes single integer values.\n *\n * **When to use:**\n * - Single numeric values (score, level, count)\n * - Non-negative integers only\n *\n * **Encoding:**\n * - Uses decimal (base-10) representation\n * - Example: 42 → \"42\"\n */\nexport type IntFieldType = {\n type: \"int\"\n}\n\n/**\n * Encodes string values as-is.\n *\n * **When to use:**\n * - Single string values (name, color, mode)\n * - Short identifiers\n *\n * **Warning:**\n * - Strings containing the schema delimiter (default ':') will break parsing\n * - Consider using a custom delimiter or different encoding for such strings\n */\nexport type StringFieldType = {\n type: \"string\"\n}\n\n/**\n * Encodes arrays of strings.\n *\n * **When to use:**\n * - Lists of tags, categories, or identifiers\n * - Ordered collections of strings\n *\n * **Encoding:**\n * - Uses comma delimiter by default\n * - Custom delimiter can be specified to avoid conflicts\n *\n * **Warning:**\n * - Strings containing the delimiter will break parsing\n * - Use custom delimiter to avoid conflicts\n */\nexport type StringArrayFieldType = {\n type: \"stringArray\"\n delimiter?: string\n}\n\n/**\n * Encodes sparse key-value maps with custom value encoding.\n *\n * **When to use:**\n * - Tile annotations, cell metadata\n * - Any sparse mapping where most keys don't have values\n * - Custom object types that need specialized encoding\n *\n * **Encoding:**\n * - With context keys: Uses numeric indices (e.g., `0=2.1,5=3.0`)\n * - Without context: Uses string keys (e.g., `key1=val1;key2=val2`)\n * - Only stores non-empty entries\n * - Keys are always strings in the data, converted to indices when context provides ordered keys\n * - valueEncoder: Convert your object to a string\n * - valueDecoder: Parse string back to your object\n *\n * **Optimization:**\n * Provide ordered keys in context to use numeric indices instead of string keys:\n * ```typescript\n * const { encode, decode } = createEncoder(schema, {\n * annotations: { keys: tileIds } // Ordered list of possible keys\n * })\n * ```\n *\n * **Example:**\n * ```typescript\n * annotations: {\n * type: 'sparseMap',\n * valueEncoder: (ann) => `${ann.level}.${ann.color}`,\n * valueDecoder: (str) => {\n * const [level, color] = str.split('.').map(Number)\n * return { level, color }\n * }\n * }\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic type parameter for value type\nexport type SparseMapFieldType<TValue = any> = {\n type: \"sparseMap\"\n valueEncoder: (value: TValue) => string\n valueDecoder: (str: string) => TValue\n}\n\n/**\n * Encodes arbitrary data as JSON with automatic compression.\n *\n * **When to use:**\n * - Complex nested structures\n * - Fallback for data that doesn't fit other types\n * - Temporary solution before implementing custom encoding\n *\n * **Encoding:**\n * - Converts data to JSON string\n * - Automatically compresses with lz-string\n * - Helps reduce size of complex objects\n *\n * **Warning:**\n * - Still larger than specialized encodings even with compression\n * - Use only when necessary\n * - Consider creating custom sparseMap encoding instead\n */\nexport type JsonFieldType = {\n type: \"json\"\n}\n\n/**\n * Union of all available field types.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic type parameter for field values\nexport type FieldType<TValue = any> =\n | BitArrayFieldType\n | IntArrayFieldType\n | IntFieldType\n | StringFieldType\n | StringArrayFieldType\n | SparseMapFieldType<TValue>\n | JsonFieldType\n\n/**\n * Function that migrates data from one version to another.\n *\n * **When to use:**\n * - Add new fields with default values\n * - Transform existing field values\n * - Rename or restructure fields\n *\n * **Important:**\n * - Migrations operate on decoded data objects, not strings\n * - Each migration goes from version N to version N+1\n * - Migrations are applied sequentially\n *\n * **Example:**\n * ```typescript\n * migrations: {\n * 0: (oldData) => ({\n * ...oldData,\n * newField: 'default value'\n * }),\n * 1: (oldData) => ({\n * ...oldData,\n * name: oldData.name.toUpperCase()\n * })\n * }\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic type parameters for migration input/output\nexport type DataMigration<TFrom = any, TTo = any> = (data: TFrom) => TTo\n\n/**\n * Map of version numbers to migration functions.\n *\n * Keys are the source version numbers (0, 1, 2, etc.)\n * Values are functions that migrate from that version to the next\n */\nexport type MigrationMap = {\n [fromVersion: number]: DataMigration\n}\n\n/**\n * Complete schema definition for encoding/decoding data.\n *\n * **Basic usage:**\n * ```typescript\n * const schema = defineSchema({\n * version: 1,\n * fields: {\n * name: { type: 'string' },\n * age: { type: 'int' }\n * }\n * })\n * ```\n *\n * **With custom delimiter:**\n * ```typescript\n * const schema = defineSchema({\n * version: 1,\n * delimiter: '|', // Use | instead of :\n * fields: { ... }\n * })\n * ```\n *\n * **With migrations:**\n * ```typescript\n * const schema = defineSchema({\n * version: 2,\n * fields: {\n * name: { type: 'string' },\n * value: { type: 'int' }\n * },\n * migrations: {\n * 0: (old) => ({ ...old, value: 0 }),\n * 1: (old) => ({ ...old, name: old.name.toUpperCase() })\n * }\n * })\n * ```\n *\n * @param version - Current schema version (increment when making breaking changes)\n * @param delimiter - Character to separate fields (default: ':')\n * @param fields - Map of field names to field type definitions\n * @param migrations - Optional map of version migrations\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generic type parameter for schema data, and field values need 'any' for flexibility\nexport type Schema<TData = any> = {\n version: number\n delimiter?: string\n fields: {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Field values can be any type\n [K in keyof TData]: FieldType<any>\n }\n migrations?: MigrationMap\n}\n\n/**\n * Result of creating an encoder from a schema.\n *\n * **Usage:**\n * ```typescript\n * const { encode, decode, migrate } = createEncoder(schema, context)\n *\n * // Encode data to string\n * const inputString = encode(data)\n *\n * // Decode string to data\n * const data = decode(inputString)\n *\n * // Migrate old version to current version\n * const currentVersion = migrate(oldInputString)\n * ```\n */\nexport type EncoderResult<TData> = {\n encode: (data: TData) => string\n decode: (str: string) => TData\n migrate: (str: string) => string\n}\n\n/**\n * Define a typed schema for encoding/decoding game state.\n *\n * **Purpose:**\n * - Provides type inference for encode/decode functions\n * - Documents the structure of your input strings\n * - Enables version migrations\n *\n * **Example:**\n * ```typescript\n * type GameData = {\n * revealed: boolean[]\n * score: number\n * annotations: Record<string, Annotation>\n * }\n *\n * export const schema = defineSchema<GameData>({\n * version: 1,\n * fields: {\n * revealed: { type: 'bitArray' },\n * score: { type: 'int' },\n * annotations: {\n * type: 'sparseMap',\n * valueEncoder: (ann) => `${ann.level}.${ann.color}`,\n * valueDecoder: (str) => {\n * const [level, color] = str.split('.').map(Number)\n * return { level, color }\n * }\n * }\n * }\n * })\n * ```\n */\nexport function defineSchema<TData>(schema: Schema<TData>): Schema<TData> {\n return schema\n}\n","import type { Schema, EncoderResult } from \"./types\"\nimport { encode } from \"./encoder\"\nimport { decode } from \"./decoder\"\nimport { createMigrator } from \"./migration\"\n\nexport { defineSchema } from \"./types\"\nexport type {\n Schema,\n EncoderResult,\n FieldType,\n BitArrayFieldType,\n IntArrayFieldType,\n IntFieldType,\n StringFieldType,\n StringArrayFieldType,\n SparseMapFieldType,\n JsonFieldType,\n DataMigration,\n MigrationMap,\n} from \"./types\"\n\n/**\n * Create an encoder/decoder for a schema-defined data structure.\n *\n * **Basic usage:**\n * ```typescript\n * import { defineSchema, createEncoder } from '@puzzmo/sdk/inputs'\n *\n * const schema = defineSchema({\n * version: 1,\n * fields: {\n * name: { type: 'string' },\n * age: { type: 'int' }\n * }\n * })\n *\n * const { encode, decode } = createEncoder(schema)\n *\n * const data = { name: 'Alice', age: 30 }\n * const inputString = encode(data) // \"v1:Alice:30\"\n * const decoded = decode(inputString) // { name: 'Alice', age: 30 }\n * ```\n *\n * **With context (for length-dependent fields):**\n * ```typescript\n * const schema = defineSchema({\n * version: 1,\n * fields: {\n * revealed: { type: 'bitArray' },\n * scores: { type: 'intArray' }\n * }\n * })\n *\n * const { encode, decode } = createEncoder(schema, {\n * revealed: { length: 100 }, // Required for bitArray decoding\n * scores: { length: 10 }, // Required for sparse intArray decoding\n * annotations: { keys: tileIds } // Optional: optimize sparseMap with numeric indices\n * })\n * ```\n *\n * **With migrations:**\n * ```typescript\n * const { encode, decode, migrate } = createEncoder(schema)\n *\n * const oldString = \"v0:Alice:30\"\n * const migrated = migrate(oldString) // \"v1:Alice:30:default\"\n * const data = decode(migrated)\n * ```\n *\n * @param schema - Schema definition created with defineSchema()\n * @param context - Optional context for decoding (lengths for bitArray/intArray)\n * @returns Object with encode, decode, and migrate functions\n */\nexport function createEncoder<TData>(\n schema: Schema<TData>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Context values can be any shape depending on field types\n context?: Record<string, any>,\n): EncoderResult<TData> {\n const migrator = createMigrator(schema)\n\n return {\n encode: (data: TData) => encode(schema, data, context),\n decode: (str: string) => decode(schema, str, context),\n migrate: (str: string) => migrator(str, context),\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inputStringEncoder.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputStringEncoder.spec.d.ts","sourceRoot":"","sources":["../../src/inputs/inputStringEncoder.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { Schema } from "./types";
2
+ export declare function createMigrator<TData>(schema: Schema<TData>): (inputString: string, context?: Record<string, any>) => string;
3
+ //# sourceMappingURL=migration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/inputs/migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAIrC,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,IACjC,aAAa,MAAM,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAG,MAAM,CAoCpF"}
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Encodes boolean arrays as compact hexadecimal strings.
3
+ *
4
+ * **When to use:**
5
+ * - Revealed/hidden state of game tiles
6
+ * - Feature flags or settings
7
+ * - Any boolean array where order matters
8
+ *
9
+ * **Encoding:**
10
+ * - Packs 4 bits per hex character (75% size reduction)
11
+ * - Example: [true, false, false, false, false, true, false, false] → "12"
12
+ *
13
+ * **Decoding:**
14
+ * - Requires length in context: `{ fieldName: { length: 100 } }`
15
+ */
16
+ export type BitArrayFieldType = {
17
+ type: "bitArray";
18
+ };
19
+ /**
20
+ * Encodes integer arrays with automatic sparse optimization.
21
+ *
22
+ * **When to use:**
23
+ * - Arrays of numeric values (scores, counts, experience)
24
+ * - Sparse data (mostly zeros)
25
+ * - Dense data (will auto-fallback to CSV)
26
+ *
27
+ * **Encoding:**
28
+ * - Uses decimal (base-10) numbers separated by commas
29
+ * - Smart sparse: If >50% zeros, uses `index=value` pairs (e.g., "2=5,4=3")
30
+ * - Dense fallback: If not beneficial, uses CSV (e.g., "1,2,3,4")
31
+ * - Empty arrays encode as empty string
32
+ *
33
+ * **Decoding:**
34
+ * - Requires length in context for sparse format: `{ fieldName: { length: 100 } }`
35
+ * - CSV format decodes without context
36
+ */
37
+ export type IntArrayFieldType = {
38
+ type: "intArray";
39
+ };
40
+ /**
41
+ * Encodes single integer values.
42
+ *
43
+ * **When to use:**
44
+ * - Single numeric values (score, level, count)
45
+ * - Non-negative integers only
46
+ *
47
+ * **Encoding:**
48
+ * - Uses decimal (base-10) representation
49
+ * - Example: 42 → "42"
50
+ */
51
+ export type IntFieldType = {
52
+ type: "int";
53
+ };
54
+ /**
55
+ * Encodes string values as-is.
56
+ *
57
+ * **When to use:**
58
+ * - Single string values (name, color, mode)
59
+ * - Short identifiers
60
+ *
61
+ * **Warning:**
62
+ * - Strings containing the schema delimiter (default ':') will break parsing
63
+ * - Consider using a custom delimiter or different encoding for such strings
64
+ */
65
+ export type StringFieldType = {
66
+ type: "string";
67
+ };
68
+ /**
69
+ * Encodes arrays of strings.
70
+ *
71
+ * **When to use:**
72
+ * - Lists of tags, categories, or identifiers
73
+ * - Ordered collections of strings
74
+ *
75
+ * **Encoding:**
76
+ * - Uses comma delimiter by default
77
+ * - Custom delimiter can be specified to avoid conflicts
78
+ *
79
+ * **Warning:**
80
+ * - Strings containing the delimiter will break parsing
81
+ * - Use custom delimiter to avoid conflicts
82
+ */
83
+ export type StringArrayFieldType = {
84
+ type: "stringArray";
85
+ delimiter?: string;
86
+ };
87
+ /**
88
+ * Encodes sparse key-value maps with custom value encoding.
89
+ *
90
+ * **When to use:**
91
+ * - Tile annotations, cell metadata
92
+ * - Any sparse mapping where most keys don't have values
93
+ * - Custom object types that need specialized encoding
94
+ *
95
+ * **Encoding:**
96
+ * - With context keys: Uses numeric indices (e.g., `0=2.1,5=3.0`)
97
+ * - Without context: Uses string keys (e.g., `key1=val1;key2=val2`)
98
+ * - Only stores non-empty entries
99
+ * - Keys are always strings in the data, converted to indices when context provides ordered keys
100
+ * - valueEncoder: Convert your object to a string
101
+ * - valueDecoder: Parse string back to your object
102
+ *
103
+ * **Optimization:**
104
+ * Provide ordered keys in context to use numeric indices instead of string keys:
105
+ * ```typescript
106
+ * const { encode, decode } = createEncoder(schema, {
107
+ * annotations: { keys: tileIds } // Ordered list of possible keys
108
+ * })
109
+ * ```
110
+ *
111
+ * **Example:**
112
+ * ```typescript
113
+ * annotations: {
114
+ * type: 'sparseMap',
115
+ * valueEncoder: (ann) => `${ann.level}.${ann.color}`,
116
+ * valueDecoder: (str) => {
117
+ * const [level, color] = str.split('.').map(Number)
118
+ * return { level, color }
119
+ * }
120
+ * }
121
+ * ```
122
+ */
123
+ export type SparseMapFieldType<TValue = any> = {
124
+ type: "sparseMap";
125
+ valueEncoder: (value: TValue) => string;
126
+ valueDecoder: (str: string) => TValue;
127
+ };
128
+ /**
129
+ * Encodes arbitrary data as JSON with automatic compression.
130
+ *
131
+ * **When to use:**
132
+ * - Complex nested structures
133
+ * - Fallback for data that doesn't fit other types
134
+ * - Temporary solution before implementing custom encoding
135
+ *
136
+ * **Encoding:**
137
+ * - Converts data to JSON string
138
+ * - Automatically compresses with lz-string
139
+ * - Helps reduce size of complex objects
140
+ *
141
+ * **Warning:**
142
+ * - Still larger than specialized encodings even with compression
143
+ * - Use only when necessary
144
+ * - Consider creating custom sparseMap encoding instead
145
+ */
146
+ export type JsonFieldType = {
147
+ type: "json";
148
+ };
149
+ /**
150
+ * Union of all available field types.
151
+ */
152
+ export type FieldType<TValue = any> = BitArrayFieldType | IntArrayFieldType | IntFieldType | StringFieldType | StringArrayFieldType | SparseMapFieldType<TValue> | JsonFieldType;
153
+ /**
154
+ * Function that migrates data from one version to another.
155
+ *
156
+ * **When to use:**
157
+ * - Add new fields with default values
158
+ * - Transform existing field values
159
+ * - Rename or restructure fields
160
+ *
161
+ * **Important:**
162
+ * - Migrations operate on decoded data objects, not strings
163
+ * - Each migration goes from version N to version N+1
164
+ * - Migrations are applied sequentially
165
+ *
166
+ * **Example:**
167
+ * ```typescript
168
+ * migrations: {
169
+ * 0: (oldData) => ({
170
+ * ...oldData,
171
+ * newField: 'default value'
172
+ * }),
173
+ * 1: (oldData) => ({
174
+ * ...oldData,
175
+ * name: oldData.name.toUpperCase()
176
+ * })
177
+ * }
178
+ * ```
179
+ */
180
+ export type DataMigration<TFrom = any, TTo = any> = (data: TFrom) => TTo;
181
+ /**
182
+ * Map of version numbers to migration functions.
183
+ *
184
+ * Keys are the source version numbers (0, 1, 2, etc.)
185
+ * Values are functions that migrate from that version to the next
186
+ */
187
+ export type MigrationMap = {
188
+ [fromVersion: number]: DataMigration;
189
+ };
190
+ /**
191
+ * Complete schema definition for encoding/decoding data.
192
+ *
193
+ * **Basic usage:**
194
+ * ```typescript
195
+ * const schema = defineSchema({
196
+ * version: 1,
197
+ * fields: {
198
+ * name: { type: 'string' },
199
+ * age: { type: 'int' }
200
+ * }
201
+ * })
202
+ * ```
203
+ *
204
+ * **With custom delimiter:**
205
+ * ```typescript
206
+ * const schema = defineSchema({
207
+ * version: 1,
208
+ * delimiter: '|', // Use | instead of :
209
+ * fields: { ... }
210
+ * })
211
+ * ```
212
+ *
213
+ * **With migrations:**
214
+ * ```typescript
215
+ * const schema = defineSchema({
216
+ * version: 2,
217
+ * fields: {
218
+ * name: { type: 'string' },
219
+ * value: { type: 'int' }
220
+ * },
221
+ * migrations: {
222
+ * 0: (old) => ({ ...old, value: 0 }),
223
+ * 1: (old) => ({ ...old, name: old.name.toUpperCase() })
224
+ * }
225
+ * })
226
+ * ```
227
+ *
228
+ * @param version - Current schema version (increment when making breaking changes)
229
+ * @param delimiter - Character to separate fields (default: ':')
230
+ * @param fields - Map of field names to field type definitions
231
+ * @param migrations - Optional map of version migrations
232
+ */
233
+ export type Schema<TData = any> = {
234
+ version: number;
235
+ delimiter?: string;
236
+ fields: {
237
+ [K in keyof TData]: FieldType<any>;
238
+ };
239
+ migrations?: MigrationMap;
240
+ };
241
+ /**
242
+ * Result of creating an encoder from a schema.
243
+ *
244
+ * **Usage:**
245
+ * ```typescript
246
+ * const { encode, decode, migrate } = createEncoder(schema, context)
247
+ *
248
+ * // Encode data to string
249
+ * const inputString = encode(data)
250
+ *
251
+ * // Decode string to data
252
+ * const data = decode(inputString)
253
+ *
254
+ * // Migrate old version to current version
255
+ * const currentVersion = migrate(oldInputString)
256
+ * ```
257
+ */
258
+ export type EncoderResult<TData> = {
259
+ encode: (data: TData) => string;
260
+ decode: (str: string) => TData;
261
+ migrate: (str: string) => string;
262
+ };
263
+ /**
264
+ * Define a typed schema for encoding/decoding game state.
265
+ *
266
+ * **Purpose:**
267
+ * - Provides type inference for encode/decode functions
268
+ * - Documents the structure of your input strings
269
+ * - Enables version migrations
270
+ *
271
+ * **Example:**
272
+ * ```typescript
273
+ * type GameData = {
274
+ * revealed: boolean[]
275
+ * score: number
276
+ * annotations: Record<string, Annotation>
277
+ * }
278
+ *
279
+ * export const schema = defineSchema<GameData>({
280
+ * version: 1,
281
+ * fields: {
282
+ * revealed: { type: 'bitArray' },
283
+ * score: { type: 'int' },
284
+ * annotations: {
285
+ * type: 'sparseMap',
286
+ * valueEncoder: (ann) => `${ann.level}.${ann.color}`,
287
+ * valueDecoder: (str) => {
288
+ * const [level, color] = str.split('.').map(Number)
289
+ * return { level, color }
290
+ * }
291
+ * }
292
+ * }
293
+ * })
294
+ * ```
295
+ */
296
+ export declare function defineSchema<TData>(schema: Schema<TData>): Schema<TData>;
297
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/inputs/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,UAAU,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,UAAU,CAAA;CACjB,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,KAAK,CAAA;CACZ,CAAA;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,QAAQ,CAAA;CACf,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,aAAa,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,MAAM,MAAM,kBAAkB,CAAC,MAAM,GAAG,GAAG,IAAI;IAC7C,IAAI,EAAE,WAAW,CAAA;IACjB,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACvC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CACtC,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;GAEG;AAEH,MAAM,MAAM,SAAS,CAAC,MAAM,GAAG,GAAG,IAC9B,iBAAiB,GACjB,iBAAiB,GACjB,YAAY,GACZ,eAAe,GACf,oBAAoB,GACpB,kBAAkB,CAAC,MAAM,CAAC,GAC1B,aAAa,CAAA;AAEjB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,MAAM,aAAa,CAAC,KAAK,GAAG,GAAG,EAAE,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,GAAG,CAAA;AAExE;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAAA;CACrC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,MAAM,MAAM,MAAM,CAAC,KAAK,GAAG,GAAG,IAAI;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE;SAEL,CAAC,IAAI,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC;KACnC,CAAA;IACD,UAAU,CAAC,EAAE,YAAY,CAAA;CAC1B,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,IAAI;IACjC,MAAM,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAA;IAC/B,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,KAAK,CAAA;IAC9B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CACjC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAExE"}
@@ -0,0 +1 @@
1
+ function e(t){"@babel/helpers - typeof";return e=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},e(t)}function t(t,n){if(e(t)!=`object`||!t)return t;var r=t[Symbol.toPrimitive];if(r!==void 0){var i=r.call(t,n||`default`);if(e(i)!=`object`)return i;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(n===`string`?String:Number)(t)}function n(n){var r=t(n,`string`);return e(r)==`symbol`?r:r+``}function r(e,t,r){return(t=n(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function a(e){for(var t=1;t<arguments.length;t++){var n=arguments[t]==null?{}:arguments[t];t%2?i(Object(n),!0).forEach(function(t){r(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):i(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return a}});
@@ -0,0 +1,52 @@
1
+ function e(t) {
2
+ "@babel/helpers - typeof";
3
+ return e = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) {
4
+ return typeof e;
5
+ } : function(e) {
6
+ return e && typeof Symbol == "function" && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e;
7
+ }, e(t);
8
+ }
9
+ function t(t, n) {
10
+ if (e(t) != "object" || !t) return t;
11
+ var r = t[Symbol.toPrimitive];
12
+ if (r !== void 0) {
13
+ var i = r.call(t, n || "default");
14
+ if (e(i) != "object") return i;
15
+ throw TypeError("@@toPrimitive must return a primitive value.");
16
+ }
17
+ return (n === "string" ? String : Number)(t);
18
+ }
19
+ function n(n) {
20
+ var r = t(n, "string");
21
+ return e(r) == "symbol" ? r : r + "";
22
+ }
23
+ function r(e, t, r) {
24
+ return (t = n(t)) in e ? Object.defineProperty(e, t, {
25
+ value: r,
26
+ enumerable: !0,
27
+ configurable: !0,
28
+ writable: !0
29
+ }) : e[t] = r, e;
30
+ }
31
+ function i(e, t) {
32
+ var n = Object.keys(e);
33
+ if (Object.getOwnPropertySymbols) {
34
+ var r = Object.getOwnPropertySymbols(e);
35
+ t && (r = r.filter(function(t) {
36
+ return Object.getOwnPropertyDescriptor(e, t).enumerable;
37
+ })), n.push.apply(n, r);
38
+ }
39
+ return n;
40
+ }
41
+ function a(e) {
42
+ for (var t = 1; t < arguments.length; t++) {
43
+ var n = arguments[t] == null ? {} : arguments[t];
44
+ t % 2 ? i(Object(n), !0).forEach(function(t) {
45
+ r(e, t, n[t]);
46
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : i(Object(n)).forEach(function(t) {
47
+ Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t));
48
+ });
49
+ }
50
+ return e;
51
+ }
52
+ export { a as t };
package/dist/sdk.d.ts CHANGED
@@ -50,7 +50,7 @@ export declare const createPuzzmoSDK: (options?: PuzzmoSDKOptions) => {
50
50
  timer: SDKTimer;
51
51
  gameReady: () => Promise<{
52
52
  puzzleString: string;
53
- boardState: string | null;
53
+ inputString: string | null;
54
54
  theme: Theme | null;
55
55
  completed: boolean;
56
56
  readyData: MessagesReceived["READY_DATA"] | null;