@fieldnotes/core 0.50.6 → 0.50.8
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/index.cjs +131 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +131 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -239,6 +239,17 @@ function sanitizeAttributes(el, tag) {
|
|
|
239
239
|
|
|
240
240
|
// src/core/state-serializer.ts
|
|
241
241
|
var CURRENT_VERSION = 2;
|
|
242
|
+
var ELEMENT_TYPES = [
|
|
243
|
+
"stroke",
|
|
244
|
+
"note",
|
|
245
|
+
"arrow",
|
|
246
|
+
"image",
|
|
247
|
+
"html",
|
|
248
|
+
"text",
|
|
249
|
+
"shape",
|
|
250
|
+
"grid",
|
|
251
|
+
"template"
|
|
252
|
+
];
|
|
242
253
|
function exportState(elements, camera, layers = [], activeLayerId) {
|
|
243
254
|
const state = {
|
|
244
255
|
version: CURRENT_VERSION,
|
|
@@ -264,37 +275,42 @@ function parseState(json) {
|
|
|
264
275
|
return data;
|
|
265
276
|
}
|
|
266
277
|
function validateState(data) {
|
|
267
|
-
if (!data
|
|
278
|
+
if (!isRecord(data)) {
|
|
268
279
|
throw new Error("Invalid state: expected an object");
|
|
269
280
|
}
|
|
270
281
|
const obj = data;
|
|
271
|
-
if (
|
|
282
|
+
if (!Number.isInteger(obj["version"]) || obj["version"] < 1) {
|
|
272
283
|
throw new Error("Invalid state: missing or invalid version");
|
|
273
284
|
}
|
|
274
|
-
if (
|
|
285
|
+
if (obj["version"] > CURRENT_VERSION) {
|
|
286
|
+
throw new Error(`Invalid state: unsupported version ${String(obj["version"])}`);
|
|
287
|
+
}
|
|
288
|
+
if (!isRecord(obj["camera"])) {
|
|
275
289
|
throw new Error("Invalid state: missing camera");
|
|
276
290
|
}
|
|
277
291
|
const cam = obj["camera"];
|
|
278
|
-
if (!cam["position"]
|
|
292
|
+
if (!isRecord(cam["position"])) {
|
|
279
293
|
throw new Error("Invalid state: missing camera.position");
|
|
280
294
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
throw new Error("Invalid state: camera.position must have x and y numbers");
|
|
295
|
+
if (!isPoint(cam["position"])) {
|
|
296
|
+
throw new Error("Invalid state: camera.position must have finite x and y numbers");
|
|
284
297
|
}
|
|
285
|
-
if (
|
|
286
|
-
throw new Error("Invalid state:
|
|
298
|
+
if (!isFiniteNumber(cam["zoom"]) || cam["zoom"] <= 0) {
|
|
299
|
+
throw new Error("Invalid state: camera.zoom must be a positive finite number");
|
|
287
300
|
}
|
|
288
301
|
if (!Array.isArray(obj["elements"])) {
|
|
289
302
|
throw new Error("Invalid state: elements must be an array");
|
|
290
303
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
migrateElement(el);
|
|
304
|
+
if (obj["layers"] !== void 0 && !Array.isArray(obj["layers"])) {
|
|
305
|
+
throw new Error("Invalid state: layers must be an array");
|
|
294
306
|
}
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
307
|
+
const elements = obj["elements"];
|
|
308
|
+
const hasLayers = Array.isArray(obj["layers"]) && obj["layers"].length > 0;
|
|
309
|
+
for (const el of elements) {
|
|
310
|
+
if (!isRecord(el)) throw new Error("Invalid element: expected an object");
|
|
311
|
+
migrateElement(el, !hasLayers);
|
|
312
|
+
}
|
|
313
|
+
if (!hasLayers) {
|
|
298
314
|
obj["layers"] = [
|
|
299
315
|
{
|
|
300
316
|
id: "default-layer",
|
|
@@ -306,31 +322,73 @@ function validateState(data) {
|
|
|
306
322
|
}
|
|
307
323
|
];
|
|
308
324
|
}
|
|
325
|
+
const layers = obj["layers"];
|
|
326
|
+
const layerIds = /* @__PURE__ */ new Set();
|
|
327
|
+
for (const layer of layers) {
|
|
328
|
+
validateLayer(layer);
|
|
329
|
+
if (layerIds.has(layer.id)) throw new Error(`Invalid state: duplicate layer id "${layer.id}"`);
|
|
330
|
+
layerIds.add(layer.id);
|
|
331
|
+
}
|
|
332
|
+
const elementIds = /* @__PURE__ */ new Set();
|
|
333
|
+
for (const el of elements) {
|
|
334
|
+
validateElement(el);
|
|
335
|
+
if (elementIds.has(el.id)) throw new Error(`Invalid state: duplicate element id "${el.id}"`);
|
|
336
|
+
elementIds.add(el.id);
|
|
337
|
+
if (!layerIds.has(el.layerId)) {
|
|
338
|
+
throw new Error(`Invalid element "${el.id}": unknown layerId "${el.layerId}"`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (obj["activeLayerId"] !== void 0) {
|
|
342
|
+
if (typeof obj["activeLayerId"] !== "string" || !layerIds.has(obj["activeLayerId"])) {
|
|
343
|
+
throw new Error("Invalid state: activeLayerId must reference an existing layer");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
cleanBindings(elements);
|
|
309
347
|
}
|
|
310
|
-
var VALID_TYPES = /* @__PURE__ */ new Set([
|
|
311
|
-
"stroke",
|
|
312
|
-
"note",
|
|
313
|
-
"arrow",
|
|
314
|
-
"image",
|
|
315
|
-
"html",
|
|
316
|
-
"text",
|
|
317
|
-
"shape",
|
|
318
|
-
"grid",
|
|
319
|
-
"template"
|
|
320
|
-
]);
|
|
321
348
|
function validateElement(el) {
|
|
322
|
-
if (!el
|
|
349
|
+
if (!isRecord(el)) {
|
|
323
350
|
throw new Error("Invalid element: expected an object");
|
|
324
351
|
}
|
|
325
|
-
|
|
326
|
-
if (typeof obj["id"] !== "string") {
|
|
352
|
+
if (typeof el["id"] !== "string" || el["id"].length === 0) {
|
|
327
353
|
throw new Error("Invalid element: missing id");
|
|
328
354
|
}
|
|
329
|
-
if (
|
|
330
|
-
throw new Error(`Invalid element: unknown type "${String(
|
|
355
|
+
if (!isEnum(el["type"], ELEMENT_TYPES)) {
|
|
356
|
+
throw new Error(`Invalid element: unknown type "${String(el["type"])}"`);
|
|
331
357
|
}
|
|
332
|
-
if (
|
|
333
|
-
throw new Error(
|
|
358
|
+
if (!isFiniteNumber(el["zIndex"])) {
|
|
359
|
+
throw new Error(`Invalid element "${el["id"]}": missing or invalid zIndex`);
|
|
360
|
+
}
|
|
361
|
+
if (!isPoint(el["position"]) || typeof el["locked"] !== "boolean" || typeof el["layerId"] !== "string" || !isOptional(el["groupId"], isString) || !isOptional(el["rotation"], isFiniteNumber)) {
|
|
362
|
+
throw new Error(`Invalid element "${el["id"]}": invalid base fields or geometry`);
|
|
363
|
+
}
|
|
364
|
+
const valid = validateTypeFields(el, el["type"]);
|
|
365
|
+
if (!valid) throw new Error(`Invalid element "${el["id"]}": malformed ${el["type"]} data`);
|
|
366
|
+
}
|
|
367
|
+
function validateTypeFields(el, type) {
|
|
368
|
+
switch (type) {
|
|
369
|
+
case "stroke":
|
|
370
|
+
return Array.isArray(el["points"]) && el["points"].every(isStrokePoint) && isString(el["color"]) && isFiniteNumber(el["width"]) && isFiniteNumber(el["opacity"]) && isOptionalEnum(el["blendMode"], ["multiply"]);
|
|
371
|
+
case "note":
|
|
372
|
+
return isSize(el["size"]) && isString(el["text"]) && isString(el["backgroundColor"]) && isString(el["textColor"]) && isOptional(el["fontSize"], isFiniteNumber);
|
|
373
|
+
case "arrow":
|
|
374
|
+
return isPoint(el["from"]) && isPoint(el["to"]) && isFiniteNumber(el["bend"]) && isString(el["color"]) && isFiniteNumber(el["width"]) && isOptional(el["fromBinding"], isBinding) && isOptional(el["toBinding"], isBinding) && isOptional(el["cachedControlPoint"], isPoint) && isOptional(el["label"], isString) && isOptionalEnum(el["strokeStyle"], ["solid", "dashed", "dotted"]);
|
|
375
|
+
case "image":
|
|
376
|
+
return isSize(el["size"]) && isString(el["src"]);
|
|
377
|
+
case "html":
|
|
378
|
+
return isSize(el["size"]) && isOptional(el["domId"], isString) && isOptional(el["interactive"], isBoolean) && isOptional(el["htmlType"], isString) && isOptional(el["data"], isRecord);
|
|
379
|
+
case "text":
|
|
380
|
+
return isSize(el["size"]) && isString(el["text"]) && isFiniteNumber(el["fontSize"]) && isString(el["color"]) && isEnum(el["textAlign"], ["left", "center", "right"]);
|
|
381
|
+
case "shape":
|
|
382
|
+
return isEnum(el["shape"], ["rectangle", "ellipse", "line"]) && isSize(el["size"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isString(el["fillColor"]) && isOptional(el["flip"], isBoolean);
|
|
383
|
+
case "grid":
|
|
384
|
+
return isEnum(el["gridType"], ["square", "hex"]) && isEnum(el["hexOrientation"], ["pointy", "flat"]) && isFiniteNumber(el["cellSize"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isFiniteNumber(el["opacity"]);
|
|
385
|
+
case "template":
|
|
386
|
+
return isEnum(el["templateShape"], ["circle", "cone", "line", "square", "rectangle"]) && isFiniteNumber(el["radius"]) && isFiniteNumber(el["angle"]) && isOptional(el["width"], isFiniteNumber) && isString(el["fillColor"]) && isString(el["strokeColor"]) && isFiniteNumber(el["strokeWidth"]) && isFiniteNumber(el["opacity"]) && isOptional(el["feetPerCell"], isFiniteNumber) && isOptional(el["radiusFeet"], isFiniteNumber) && isOptionalEnum(el["renderStyle"], ["cells", "geometric"]);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function validateLayer(layer) {
|
|
390
|
+
if (!isRecord(layer) || typeof layer["id"] !== "string" || layer["id"].length === 0 || typeof layer["name"] !== "string" || typeof layer["visible"] !== "boolean" || typeof layer["locked"] !== "boolean" || !isFiniteNumber(layer["order"]) || !isFiniteNumber(layer["opacity"]) || layer["opacity"] < 0 || layer["opacity"] > 1) {
|
|
391
|
+
throw new Error("Invalid state: malformed layer");
|
|
334
392
|
}
|
|
335
393
|
}
|
|
336
394
|
function cleanBindings(elements) {
|
|
@@ -347,30 +405,63 @@ function cleanBindings(elements) {
|
|
|
347
405
|
}
|
|
348
406
|
}
|
|
349
407
|
}
|
|
350
|
-
function migrateElement(obj) {
|
|
351
|
-
if (
|
|
408
|
+
function migrateElement(obj, useDefaultLayer) {
|
|
409
|
+
if (obj["layerId"] === void 0 || useDefaultLayer && obj["layerId"] === "") {
|
|
352
410
|
obj["layerId"] = "default-layer";
|
|
353
411
|
}
|
|
354
|
-
if (obj["type"] === "arrow" &&
|
|
412
|
+
if (obj["type"] === "arrow" && obj["bend"] === void 0) {
|
|
355
413
|
obj["bend"] = 0;
|
|
356
414
|
}
|
|
357
415
|
if (obj["type"] === "stroke" && Array.isArray(obj["points"])) {
|
|
358
416
|
for (const pt of obj["points"]) {
|
|
359
|
-
if (
|
|
417
|
+
if (pt["pressure"] === void 0) {
|
|
360
418
|
pt["pressure"] = 0.5;
|
|
361
419
|
}
|
|
362
420
|
}
|
|
363
421
|
}
|
|
364
|
-
if (obj["type"] === "shape" &&
|
|
422
|
+
if (obj["type"] === "shape" && obj["shape"] === void 0) {
|
|
365
423
|
obj["shape"] = "rectangle";
|
|
366
424
|
}
|
|
367
|
-
if (obj["type"] === "note" &&
|
|
425
|
+
if (obj["type"] === "note" && obj["textColor"] === void 0) {
|
|
368
426
|
obj["textColor"] = "#000000";
|
|
369
427
|
}
|
|
370
428
|
if ((obj["type"] === "note" || obj["type"] === "text") && typeof obj["text"] === "string") {
|
|
371
429
|
obj["text"] = sanitizeNoteHtml(obj["text"]);
|
|
372
430
|
}
|
|
373
431
|
}
|
|
432
|
+
function isRecord(value) {
|
|
433
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
434
|
+
}
|
|
435
|
+
function isFiniteNumber(value) {
|
|
436
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
437
|
+
}
|
|
438
|
+
function isString(value) {
|
|
439
|
+
return typeof value === "string";
|
|
440
|
+
}
|
|
441
|
+
function isBoolean(value) {
|
|
442
|
+
return typeof value === "boolean";
|
|
443
|
+
}
|
|
444
|
+
function isOptional(value, validate) {
|
|
445
|
+
return value === void 0 || validate(value);
|
|
446
|
+
}
|
|
447
|
+
function isEnum(value, values) {
|
|
448
|
+
return typeof value === "string" && values.includes(value);
|
|
449
|
+
}
|
|
450
|
+
function isOptionalEnum(value, values) {
|
|
451
|
+
return value === void 0 || isEnum(value, values);
|
|
452
|
+
}
|
|
453
|
+
function isPoint(value) {
|
|
454
|
+
return isRecord(value) && isFiniteNumber(value["x"]) && isFiniteNumber(value["y"]);
|
|
455
|
+
}
|
|
456
|
+
function isSize(value) {
|
|
457
|
+
return isRecord(value) && isFiniteNumber(value["w"]) && isFiniteNumber(value["h"]);
|
|
458
|
+
}
|
|
459
|
+
function isStrokePoint(value) {
|
|
460
|
+
return isRecord(value) && isPoint(value) && isFiniteNumber(value["pressure"]);
|
|
461
|
+
}
|
|
462
|
+
function isBinding(value) {
|
|
463
|
+
return isRecord(value) && typeof value["elementId"] === "string";
|
|
464
|
+
}
|
|
374
465
|
|
|
375
466
|
// src/core/storage/local-storage-adapter.ts
|
|
376
467
|
var LocalStorageAdapter = class {
|
|
@@ -10724,7 +10815,7 @@ var LaserTool = class {
|
|
|
10724
10815
|
};
|
|
10725
10816
|
|
|
10726
10817
|
// src/index.ts
|
|
10727
|
-
var VERSION = "0.50.
|
|
10818
|
+
var VERSION = "0.50.8";
|
|
10728
10819
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10729
10820
|
0 && (module.exports = {
|
|
10730
10821
|
ArrowTool,
|