reptree 0.2.3 → 0.3.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.
- package/README.md +24 -6
- package/dist/index.cjs +322 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -4
- package/dist/index.d.ts +46 -4
- package/dist/index.js +321 -28
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -107,13 +107,48 @@ type SchemaLike<T> = {
|
|
|
107
107
|
parse?: (input: unknown) => T;
|
|
108
108
|
shape?: Record<string, FieldSchemaLike>;
|
|
109
109
|
};
|
|
110
|
+
type AliasRule = {
|
|
111
|
+
publicKey: string;
|
|
112
|
+
internalKey: string;
|
|
113
|
+
toPublic?: (value: unknown) => unknown;
|
|
114
|
+
toInternal?: (value: unknown) => unknown;
|
|
115
|
+
};
|
|
116
|
+
declare const defaultAliases: AliasRule[];
|
|
117
|
+
type BindOptions<T> = {
|
|
118
|
+
schema?: SchemaLike<T>;
|
|
119
|
+
aliases?: AliasRule[];
|
|
120
|
+
includeInternalKeys?: boolean;
|
|
121
|
+
};
|
|
122
|
+
type BindedVertex<T> = T & {
|
|
123
|
+
/**
|
|
124
|
+
* Create a transient proxy that can be used to write transient properties.
|
|
125
|
+
*/
|
|
126
|
+
useTransient(fn: (t: T) => void): void;
|
|
127
|
+
/**
|
|
128
|
+
* Promote transient properties to persistent.
|
|
129
|
+
*/
|
|
130
|
+
commitTransients(): void;
|
|
131
|
+
/** Vertex properties (prefixed with $ to avoid conflicts) */
|
|
132
|
+
$id: string;
|
|
133
|
+
$parentId: string | null;
|
|
134
|
+
$parent: Vertex | undefined;
|
|
135
|
+
$children: Vertex[];
|
|
136
|
+
$childrenIds: string[];
|
|
137
|
+
/** Vertex methods (prefixed with $ to avoid conflicts) */
|
|
138
|
+
$moveTo(parent: Vertex | BindedVertex<any> | string): void;
|
|
139
|
+
$delete(): void;
|
|
140
|
+
$observe(listener: (events: any[]) => void): () => void;
|
|
141
|
+
$observeChildren(listener: (children: Vertex[]) => void): () => void;
|
|
142
|
+
$newChild(props?: Record<string, any> | object | null): Vertex;
|
|
143
|
+
$newNamedChild(name: string, props?: Record<string, any> | object | null): Vertex;
|
|
144
|
+
};
|
|
110
145
|
/**
|
|
111
146
|
* Returns a live object that proxies reads/writes to a vertex.
|
|
112
147
|
* - Reads reflect the latest CRDT state.
|
|
113
148
|
* - Writes persist to the CRDT.
|
|
114
149
|
* - If a schema is provided, writes are validated. If a field schema exists in `schema.shape`, field-level validation is applied.
|
|
115
150
|
*/
|
|
116
|
-
declare function bindVertex<T extends Record<string, unknown>>(tree: RepTree, id: string,
|
|
151
|
+
declare function bindVertex<T extends Record<string, unknown>>(tree: RepTree, id: string, schemaOrOptions?: SchemaLike<T> | BindOptions<T>): BindedVertex<T>;
|
|
117
152
|
|
|
118
153
|
/**
|
|
119
154
|
* A wrapper class for VertexState that provides a more convenient API
|
|
@@ -150,8 +185,15 @@ declare class Vertex {
|
|
|
150
185
|
observeChildrenAsTypedArray<T>(listener: (children: T[]) => void): () => void;
|
|
151
186
|
delete(): void;
|
|
152
187
|
moveTo(parent: Vertex): void;
|
|
153
|
-
/** Returns a live reactive object bound to this vertex.
|
|
154
|
-
bind<T extends Record<string, unknown>>(
|
|
188
|
+
/** Returns a live reactive object bound to this vertex. Accepts schema or options. */
|
|
189
|
+
bind<T extends Record<string, unknown>>(schemaOrOptions?: SchemaLike<T> | BindOptions<T>): BindedVertex<T>;
|
|
190
|
+
/**
|
|
191
|
+
* Normalizes an input props object for vertex creation:
|
|
192
|
+
* - Aliases name -> _n, createdAt -> _c (Date -> ISO string)
|
|
193
|
+
* - Filters unsupported field types with a console warning
|
|
194
|
+
* - When a name param is provided to newNamedChild, ignores conflicting name in props
|
|
195
|
+
*/
|
|
196
|
+
private static normalizePropsForCreation;
|
|
155
197
|
}
|
|
156
198
|
|
|
157
199
|
/**
|
|
@@ -355,4 +397,4 @@ declare class StateVector {
|
|
|
355
397
|
|
|
356
398
|
declare function uuid(): string;
|
|
357
399
|
|
|
358
|
-
export { type CRDTType, type MoveVertex, type OpId, type OpIdRange, RepTree, type SetVertexProperty, StateVector, TreeState, type TreeVertexId, type TreeVertexProperty, Vertex, type VertexChangeEvent, type VertexChildrenChangeEvent, type VertexMoveEvent, type VertexOperation, type VertexPropertyChangeEvent, type VertexPropertyType, type VertexPropertyTypeInOperation, VertexState, bindVertex, isAnyPropertyOp, isLWWPropertyOp, isModifyPropertyOp, isMoveVertexOp, newMoveVertexOp, newSetTransientVertexPropertyOp, newSetVertexPropertyOp, uuid };
|
|
400
|
+
export { type AliasRule, type BindOptions, type BindedVertex, type CRDTType, type MoveVertex, type OpId, type OpIdRange, RepTree, type SchemaLike, type SetVertexProperty, StateVector, TreeState, type TreeVertexId, type TreeVertexProperty, Vertex, type VertexChangeEvent, type VertexChildrenChangeEvent, type VertexMoveEvent, type VertexOperation, type VertexPropertyChangeEvent, type VertexPropertyType, type VertexPropertyTypeInOperation, VertexState, bindVertex, defaultAliases, isAnyPropertyOp, isLWWPropertyOp, isModifyPropertyOp, isMoveVertexOp, newMoveVertexOp, newSetTransientVertexPropertyOp, newSetVertexPropertyOp, uuid };
|
package/dist/index.js
CHANGED
|
@@ -355,19 +355,217 @@ function uuid() {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
// src/reactive.ts
|
|
358
|
-
|
|
358
|
+
var defaultAliases = [
|
|
359
|
+
{ publicKey: "name", internalKey: "_n" },
|
|
360
|
+
{
|
|
361
|
+
publicKey: "createdAt",
|
|
362
|
+
internalKey: "_c",
|
|
363
|
+
toPublic: (v) => typeof v === "string" ? new Date(v) : v,
|
|
364
|
+
toInternal: (v) => v instanceof Date ? v.toISOString() : v
|
|
365
|
+
}
|
|
366
|
+
];
|
|
367
|
+
function buildAliasMaps(aliases) {
|
|
368
|
+
const publicToInternal = /* @__PURE__ */ new Map();
|
|
369
|
+
const internalToPublic = /* @__PURE__ */ new Map();
|
|
370
|
+
for (const rule of aliases) {
|
|
371
|
+
publicToInternal.set(rule.publicKey, rule);
|
|
372
|
+
internalToPublic.set(rule.internalKey, rule);
|
|
373
|
+
}
|
|
374
|
+
return { publicToInternal, internalToPublic };
|
|
375
|
+
}
|
|
376
|
+
function toPublicObject(tree, id, internalToPublic) {
|
|
359
377
|
const obj = {};
|
|
360
|
-
for (const { key, value } of tree.getVertexProperties(id))
|
|
378
|
+
for (const { key, value } of tree.getVertexProperties(id)) {
|
|
379
|
+
const rule = internalToPublic.get(key);
|
|
380
|
+
if (rule) {
|
|
381
|
+
const converted = rule.toPublic ? rule.toPublic(value) : value;
|
|
382
|
+
obj[rule.publicKey] = converted;
|
|
383
|
+
} else {
|
|
384
|
+
obj[key] = value;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
361
387
|
return obj;
|
|
362
388
|
}
|
|
363
|
-
|
|
389
|
+
var RESERVED_METHOD_USE_TRANSIENT = "useTransient";
|
|
390
|
+
var RESERVED_METHOD_COMMIT_TRANSIENTS = "commitTransients";
|
|
391
|
+
var VERTEX_PROPS = ["$id", "$parentId", "$parent", "$children", "$childrenIds"];
|
|
392
|
+
var VERTEX_METHODS = ["$moveTo", "$delete", "$observe", "$observeChildren", "$newChild", "$newNamedChild"];
|
|
393
|
+
function bindVertex(tree, id, schemaOrOptions) {
|
|
394
|
+
const isOptions = typeof schemaOrOptions === "object" && schemaOrOptions !== null && (Object.prototype.hasOwnProperty.call(schemaOrOptions, "aliases") || Object.prototype.hasOwnProperty.call(schemaOrOptions, "includeInternalKeys") || Object.prototype.hasOwnProperty.call(schemaOrOptions, "schema"));
|
|
395
|
+
const options = isOptions ? schemaOrOptions : { schema: schemaOrOptions };
|
|
396
|
+
const schema = options.schema;
|
|
397
|
+
const aliases = options.aliases ?? defaultAliases;
|
|
398
|
+
const includeInternalKeys = options.includeInternalKeys ?? false;
|
|
399
|
+
const { publicToInternal, internalToPublic } = buildAliasMaps(aliases);
|
|
400
|
+
const cachedMethods = /* @__PURE__ */ new Map();
|
|
364
401
|
return new Proxy({}, {
|
|
365
402
|
get(_target, prop) {
|
|
403
|
+
if (prop === RESERVED_METHOD_USE_TRANSIENT) {
|
|
404
|
+
return (fn) => {
|
|
405
|
+
const transientProxy = new Proxy({}, {
|
|
406
|
+
get(_t, p) {
|
|
407
|
+
if (typeof p !== "string") return void 0;
|
|
408
|
+
const rule2 = publicToInternal.get(p);
|
|
409
|
+
if (rule2) {
|
|
410
|
+
const raw = tree.getVertexProperty(id, rule2.internalKey);
|
|
411
|
+
return rule2.toPublic ? rule2.toPublic(raw) : raw;
|
|
412
|
+
}
|
|
413
|
+
return tree.getVertexProperty(id, p);
|
|
414
|
+
},
|
|
415
|
+
set(_t, p, value) {
|
|
416
|
+
if (typeof p !== "string") return true;
|
|
417
|
+
if (schema?.shape && schema.shape[p]) {
|
|
418
|
+
const field = schema.shape[p];
|
|
419
|
+
if (field.safeParse) {
|
|
420
|
+
const res = field.safeParse(value);
|
|
421
|
+
if (!res.success) throw new Error(`Invalid value for ${String(p)}`);
|
|
422
|
+
value = res.data ?? value;
|
|
423
|
+
}
|
|
424
|
+
} else if (schema?.safeParse) {
|
|
425
|
+
const next = { ...toPublicObject(tree, id, internalToPublic), [p]: value };
|
|
426
|
+
const res = schema.safeParse(next);
|
|
427
|
+
if (!res.success) throw new Error(`Invalid value for ${String(p)}`);
|
|
428
|
+
const parsed = res.data;
|
|
429
|
+
if (parsed && Object.prototype.hasOwnProperty.call(parsed, p)) {
|
|
430
|
+
value = parsed[p];
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const rule2 = publicToInternal.get(p);
|
|
434
|
+
if (rule2) {
|
|
435
|
+
const converted = rule2.toInternal ? rule2.toInternal(value) : value;
|
|
436
|
+
tree.setTransientVertexProperty(id, rule2.internalKey, converted);
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
tree.setTransientVertexProperty(id, p, value);
|
|
440
|
+
return true;
|
|
441
|
+
},
|
|
442
|
+
deleteProperty(_t, p) {
|
|
443
|
+
if (typeof p !== "string") return true;
|
|
444
|
+
const rule2 = publicToInternal.get(p);
|
|
445
|
+
if (rule2) {
|
|
446
|
+
tree.setTransientVertexProperty(id, rule2.internalKey, void 0);
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
tree.setTransientVertexProperty(id, p, void 0);
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
fn(transientProxy);
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (prop === RESERVED_METHOD_COMMIT_TRANSIENTS) {
|
|
457
|
+
return () => {
|
|
458
|
+
const entries = tree.getVertexProperties(id);
|
|
459
|
+
const currentPublic = schema?.safeParse ? toPublicObject(tree, id, internalToPublic) : void 0;
|
|
460
|
+
for (const { key: internalKey, value: overlayValue } of entries) {
|
|
461
|
+
const persistentValue = tree.getVertexProperty(id, internalKey, false);
|
|
462
|
+
if (overlayValue === persistentValue) continue;
|
|
463
|
+
const aliasRule = internalToPublic.get(internalKey);
|
|
464
|
+
const publicKey = aliasRule ? aliasRule.publicKey : internalKey;
|
|
465
|
+
const publicValue = aliasRule && aliasRule.toPublic ? aliasRule.toPublic(overlayValue) : overlayValue;
|
|
466
|
+
let valueToPersistInternal = overlayValue;
|
|
467
|
+
if (schema?.shape && schema.shape[publicKey]) {
|
|
468
|
+
const field = schema.shape[publicKey];
|
|
469
|
+
if (field.safeParse) {
|
|
470
|
+
const res = field.safeParse(publicValue);
|
|
471
|
+
if (!res.success) throw new Error(`Invalid value for ${String(publicKey)}`);
|
|
472
|
+
const coerced = res.data;
|
|
473
|
+
const maybeInternal = aliasRule && aliasRule.toInternal ? aliasRule.toInternal(coerced) : coerced;
|
|
474
|
+
valueToPersistInternal = maybeInternal;
|
|
475
|
+
}
|
|
476
|
+
} else if (schema?.safeParse && currentPublic) {
|
|
477
|
+
const nextPublic = { ...currentPublic, [publicKey]: publicValue };
|
|
478
|
+
const res = schema.safeParse(nextPublic);
|
|
479
|
+
if (!res.success) throw new Error("Invalid values for commitTransients");
|
|
480
|
+
const parsed = res.data;
|
|
481
|
+
const parsedPublic = Object.prototype.hasOwnProperty.call(parsed, publicKey) ? parsed[publicKey] : publicValue;
|
|
482
|
+
const maybeInternal = aliasRule && aliasRule.toInternal ? aliasRule.toInternal(parsedPublic) : parsedPublic;
|
|
483
|
+
valueToPersistInternal = maybeInternal;
|
|
484
|
+
}
|
|
485
|
+
tree.setVertexProperty(id, internalKey, valueToPersistInternal);
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
if (typeof prop === "string") {
|
|
490
|
+
if (prop === "$id") return id;
|
|
491
|
+
if (prop === "$parentId") return tree.getVertex(id)?.parentId ?? null;
|
|
492
|
+
if (prop === "$parent") {
|
|
493
|
+
const vertex = tree.getVertex(id);
|
|
494
|
+
return vertex?.parent;
|
|
495
|
+
}
|
|
496
|
+
if (prop === "$children") return tree.getChildren(id);
|
|
497
|
+
if (prop === "$childrenIds") return tree.getChildrenIds(id);
|
|
498
|
+
if (prop === "$moveTo") {
|
|
499
|
+
if (!cachedMethods.has(prop)) {
|
|
500
|
+
cachedMethods.set(prop, (parent) => {
|
|
501
|
+
const parentId = typeof parent === "object" && parent !== null ? parent.id || parent.$id : parent;
|
|
502
|
+
tree.moveVertex(id, parentId);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
return cachedMethods.get(prop);
|
|
506
|
+
}
|
|
507
|
+
if (prop === "$delete") {
|
|
508
|
+
if (!cachedMethods.has(prop)) {
|
|
509
|
+
cachedMethods.set(prop, () => tree.deleteVertex(id));
|
|
510
|
+
}
|
|
511
|
+
return cachedMethods.get(prop);
|
|
512
|
+
}
|
|
513
|
+
if (prop === "$observe") {
|
|
514
|
+
if (!cachedMethods.has(prop)) {
|
|
515
|
+
cachedMethods.set(prop, (listener) => tree.observe(id, listener));
|
|
516
|
+
}
|
|
517
|
+
return cachedMethods.get(prop);
|
|
518
|
+
}
|
|
519
|
+
if (prop === "$observeChildren") {
|
|
520
|
+
if (!cachedMethods.has(prop)) {
|
|
521
|
+
cachedMethods.set(prop, (listener) => {
|
|
522
|
+
return tree.observe(id, (events) => {
|
|
523
|
+
if (events.some((e) => e.type === "children")) {
|
|
524
|
+
listener(tree.getChildren(id));
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
return cachedMethods.get(prop);
|
|
530
|
+
}
|
|
531
|
+
if (prop === "$newChild") {
|
|
532
|
+
if (!cachedMethods.has(prop)) {
|
|
533
|
+
cachedMethods.set(prop, (props) => {
|
|
534
|
+
const vertex = tree.getVertex(id);
|
|
535
|
+
return vertex?.newChild(props);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return cachedMethods.get(prop);
|
|
539
|
+
}
|
|
540
|
+
if (prop === "$newNamedChild") {
|
|
541
|
+
if (!cachedMethods.has(prop)) {
|
|
542
|
+
cachedMethods.set(prop, (name, props) => {
|
|
543
|
+
const vertex = tree.getVertex(id);
|
|
544
|
+
return vertex?.newNamedChild(name, props);
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return cachedMethods.get(prop);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
366
550
|
if (typeof prop !== "string") return void 0;
|
|
551
|
+
const rule = publicToInternal.get(prop);
|
|
552
|
+
if (rule) {
|
|
553
|
+
const raw = tree.getVertexProperty(id, rule.internalKey);
|
|
554
|
+
return rule.toPublic ? rule.toPublic(raw) : raw;
|
|
555
|
+
}
|
|
367
556
|
return tree.getVertexProperty(id, prop);
|
|
368
557
|
},
|
|
369
558
|
set(_target, prop, value) {
|
|
370
559
|
if (typeof prop !== "string") return true;
|
|
560
|
+
if (prop === RESERVED_METHOD_USE_TRANSIENT || prop === RESERVED_METHOD_COMMIT_TRANSIENTS) {
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
if (VERTEX_PROPS.includes(prop)) {
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
if (VERTEX_METHODS.includes(prop)) {
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
371
569
|
if (schema?.shape && schema.shape[prop]) {
|
|
372
570
|
const field = schema.shape[prop];
|
|
373
571
|
if (field.safeParse) {
|
|
@@ -376,7 +574,7 @@ function bindVertex(tree, id, schema) {
|
|
|
376
574
|
value = res.data ?? value;
|
|
377
575
|
}
|
|
378
576
|
} else if (schema?.safeParse) {
|
|
379
|
-
const next = { ...
|
|
577
|
+
const next = { ...toPublicObject(tree, id, internalToPublic), [prop]: value };
|
|
380
578
|
const res = schema.safeParse(next);
|
|
381
579
|
if (!res.success) throw new Error(`Invalid value for ${String(prop)}`);
|
|
382
580
|
const parsed = res.data;
|
|
@@ -384,20 +582,49 @@ function bindVertex(tree, id, schema) {
|
|
|
384
582
|
value = parsed[prop];
|
|
385
583
|
}
|
|
386
584
|
}
|
|
585
|
+
const rule = publicToInternal.get(prop);
|
|
586
|
+
if (rule) {
|
|
587
|
+
const converted = rule.toInternal ? rule.toInternal(value) : value;
|
|
588
|
+
tree.setVertexProperty(id, rule.internalKey, converted);
|
|
589
|
+
return true;
|
|
590
|
+
}
|
|
387
591
|
tree.setVertexProperty(id, prop, value);
|
|
388
592
|
return true;
|
|
389
593
|
},
|
|
390
594
|
deleteProperty(_target, prop) {
|
|
391
595
|
if (typeof prop !== "string") return true;
|
|
596
|
+
if (prop === RESERVED_METHOD_USE_TRANSIENT || prop === RESERVED_METHOD_COMMIT_TRANSIENTS) {
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
if (VERTEX_PROPS.includes(prop)) {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
if (VERTEX_METHODS.includes(prop)) {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
const rule = publicToInternal.get(prop);
|
|
606
|
+
if (rule) {
|
|
607
|
+
tree.setVertexProperty(id, rule.internalKey, void 0);
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
392
610
|
tree.setVertexProperty(id, prop, void 0);
|
|
393
611
|
return true;
|
|
394
612
|
},
|
|
395
613
|
has(_target, prop) {
|
|
396
614
|
if (typeof prop !== "string") return false;
|
|
397
|
-
|
|
615
|
+
if (schema?.shape && Object.prototype.hasOwnProperty.call(schema.shape, prop)) return true;
|
|
616
|
+
if (includeInternalKeys) {
|
|
617
|
+
return publicToInternal.has(prop) || internalToPublic.has(prop);
|
|
618
|
+
}
|
|
619
|
+
return false;
|
|
398
620
|
},
|
|
399
621
|
ownKeys() {
|
|
400
|
-
|
|
622
|
+
const keys = /* @__PURE__ */ new Set();
|
|
623
|
+
for (const k of Object.keys(schema?.shape ?? {})) keys.add(k);
|
|
624
|
+
if (includeInternalKeys) {
|
|
625
|
+
for (const rule of aliases) keys.add(rule.internalKey);
|
|
626
|
+
}
|
|
627
|
+
return Array.from(keys);
|
|
401
628
|
},
|
|
402
629
|
getOwnPropertyDescriptor() {
|
|
403
630
|
return { enumerable: true, configurable: true };
|
|
@@ -406,7 +633,8 @@ function bindVertex(tree, id, schema) {
|
|
|
406
633
|
}
|
|
407
634
|
|
|
408
635
|
// src/Vertex.ts
|
|
409
|
-
|
|
636
|
+
import * as Y from "yjs";
|
|
637
|
+
var Vertex = class _Vertex {
|
|
410
638
|
constructor(tree, state) {
|
|
411
639
|
this.tree = tree;
|
|
412
640
|
this.state = state;
|
|
@@ -452,12 +680,18 @@ var Vertex = class {
|
|
|
452
680
|
return this.children.map((v) => v.getAsTypedObject());
|
|
453
681
|
}
|
|
454
682
|
newChild(props) {
|
|
455
|
-
|
|
456
|
-
|
|
683
|
+
if (props && typeof props === "object" && "children" in props) {
|
|
684
|
+
throw new Error("Passing children inside props is not supported at the moment");
|
|
685
|
+
}
|
|
686
|
+
const normalized = _Vertex.normalizePropsForCreation(props);
|
|
687
|
+
return this.tree.newVertex(this.id, normalized);
|
|
457
688
|
}
|
|
458
689
|
newNamedChild(name, props) {
|
|
459
|
-
|
|
460
|
-
|
|
690
|
+
if (props && typeof props === "object" && "children" in props) {
|
|
691
|
+
throw new Error("Passing children inside props is not supported at the moment");
|
|
692
|
+
}
|
|
693
|
+
const normalized = _Vertex.normalizePropsForCreation(props, name);
|
|
694
|
+
return this.tree.newNamedVertex(this.id, name, normalized);
|
|
461
695
|
}
|
|
462
696
|
setProperty(key, value) {
|
|
463
697
|
const existingValue = this.getProperty(key, false);
|
|
@@ -523,9 +757,67 @@ var Vertex = class {
|
|
|
523
757
|
moveTo(parent) {
|
|
524
758
|
this.tree.moveVertex(this.id, parent.id);
|
|
525
759
|
}
|
|
526
|
-
/** Returns a live reactive object bound to this vertex.
|
|
527
|
-
bind(
|
|
528
|
-
return bindVertex(this.tree, this.id,
|
|
760
|
+
/** Returns a live reactive object bound to this vertex. Accepts schema or options. */
|
|
761
|
+
bind(schemaOrOptions) {
|
|
762
|
+
return bindVertex(this.tree, this.id, schemaOrOptions);
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Normalizes an input props object for vertex creation:
|
|
766
|
+
* - Aliases name -> _n, createdAt -> _c (Date -> ISO string)
|
|
767
|
+
* - Filters unsupported field types with a console warning
|
|
768
|
+
* - When a name param is provided to newNamedChild, ignores conflicting name in props
|
|
769
|
+
*/
|
|
770
|
+
static normalizePropsForCreation(props, explicitName) {
|
|
771
|
+
if (!props) return null;
|
|
772
|
+
const input = props;
|
|
773
|
+
const out = {};
|
|
774
|
+
const skipped = [];
|
|
775
|
+
for (const [rawKey, rawValue] of Object.entries(input)) {
|
|
776
|
+
if (rawValue === void 0) {
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (rawKey === "children") continue;
|
|
780
|
+
let key = rawKey;
|
|
781
|
+
if (rawKey === "name") {
|
|
782
|
+
if (explicitName !== void 0) {
|
|
783
|
+
console.warn('newNamedChild: "name" in props is ignored because a name argument was provided');
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
key = "_n";
|
|
787
|
+
} else if (rawKey === "createdAt") {
|
|
788
|
+
key = "_c";
|
|
789
|
+
}
|
|
790
|
+
let value = rawValue;
|
|
791
|
+
if (key === "_c") {
|
|
792
|
+
if (value instanceof Date) {
|
|
793
|
+
value = value.toISOString();
|
|
794
|
+
} else if (typeof value === "string") {
|
|
795
|
+
} else {
|
|
796
|
+
skipped.push(rawKey);
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const isPrimitive = (v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean";
|
|
801
|
+
if (Array.isArray(value)) {
|
|
802
|
+
if (!value.every(isPrimitive)) {
|
|
803
|
+
skipped.push(rawKey);
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
} else if (typeof value === "object" && value !== null) {
|
|
807
|
+
if (!(value instanceof Y.Doc)) {
|
|
808
|
+
skipped.push(rawKey);
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
} else if (!isPrimitive(value)) {
|
|
812
|
+
skipped.push(rawKey);
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
out[key] = value;
|
|
816
|
+
}
|
|
817
|
+
if (skipped.length > 0) {
|
|
818
|
+
console.warn(`Some fields were skipped due to unsupported types: ${skipped.join(", ")}`);
|
|
819
|
+
}
|
|
820
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
529
821
|
}
|
|
530
822
|
};
|
|
531
823
|
|
|
@@ -715,7 +1007,7 @@ var StateVector = class _StateVector {
|
|
|
715
1007
|
};
|
|
716
1008
|
|
|
717
1009
|
// src/RepTree.ts
|
|
718
|
-
import * as
|
|
1010
|
+
import * as Y2 from "yjs";
|
|
719
1011
|
var _RepTree = class _RepTree {
|
|
720
1012
|
/**
|
|
721
1013
|
* @param peerId - The peer ID of the current client. Should be unique across all peers.
|
|
@@ -877,8 +1169,8 @@ var _RepTree = class _RepTree {
|
|
|
877
1169
|
setTransientVertexProperty(vertexId, key, value) {
|
|
878
1170
|
this.lamportClock++;
|
|
879
1171
|
let opValue;
|
|
880
|
-
if (value instanceof
|
|
881
|
-
const state =
|
|
1172
|
+
if (value instanceof Y2.Doc) {
|
|
1173
|
+
const state = Y2.encodeStateAsUpdate(value);
|
|
882
1174
|
opValue = {
|
|
883
1175
|
type: "yjs",
|
|
884
1176
|
value: state
|
|
@@ -894,8 +1186,8 @@ var _RepTree = class _RepTree {
|
|
|
894
1186
|
setVertexProperty(vertexId, key, value) {
|
|
895
1187
|
this.lamportClock++;
|
|
896
1188
|
let opValue;
|
|
897
|
-
if (value instanceof
|
|
898
|
-
const state =
|
|
1189
|
+
if (value instanceof Y2.Doc) {
|
|
1190
|
+
const state = Y2.encodeStateAsUpdate(value);
|
|
899
1191
|
opValue = {
|
|
900
1192
|
type: "yjs",
|
|
901
1193
|
value: state
|
|
@@ -1067,10 +1359,10 @@ var _RepTree = class _RepTree {
|
|
|
1067
1359
|
if (!propB) {
|
|
1068
1360
|
return false;
|
|
1069
1361
|
}
|
|
1070
|
-
if (propA.value instanceof
|
|
1071
|
-
const snapshotA =
|
|
1072
|
-
const snapshotB =
|
|
1073
|
-
if (!
|
|
1362
|
+
if (propA.value instanceof Y2.Doc && propB.value instanceof Y2.Doc) {
|
|
1363
|
+
const snapshotA = Y2.snapshot(propA.value);
|
|
1364
|
+
const snapshotB = Y2.snapshot(propB.value);
|
|
1365
|
+
if (!Y2.equalSnapshots(snapshotA, snapshotB)) {
|
|
1074
1366
|
return false;
|
|
1075
1367
|
}
|
|
1076
1368
|
} else if (propA.value !== propB.value) {
|
|
@@ -1174,7 +1466,7 @@ var _RepTree = class _RepTree {
|
|
|
1174
1466
|
const propertyKey = `${key}@${vertexId}`;
|
|
1175
1467
|
if (this.yjsObservers.has(propertyKey)) {
|
|
1176
1468
|
const existingDoc = this.getVertexProperty(vertexId, key);
|
|
1177
|
-
if (existingDoc instanceof
|
|
1469
|
+
if (existingDoc instanceof Y2.Doc) {
|
|
1178
1470
|
existingDoc.off("update", this.yjsObservers.get(propertyKey));
|
|
1179
1471
|
}
|
|
1180
1472
|
this.yjsObservers.delete(propertyKey);
|
|
@@ -1254,13 +1546,13 @@ var _RepTree = class _RepTree {
|
|
|
1254
1546
|
throw new Error("Unknown CRDT type");
|
|
1255
1547
|
}
|
|
1256
1548
|
const ydoc = targetVertex.getProperty(op.key);
|
|
1257
|
-
if (ydoc instanceof
|
|
1258
|
-
|
|
1549
|
+
if (ydoc instanceof Y2.Doc) {
|
|
1550
|
+
Y2.applyUpdate(ydoc, crdtValue.value);
|
|
1259
1551
|
} else {
|
|
1260
|
-
const newDoc = new
|
|
1552
|
+
const newDoc = new Y2.Doc();
|
|
1261
1553
|
this.setupYjsObserver(newDoc, op.targetId, op.key);
|
|
1262
1554
|
this.state.setProperty(op.targetId, op.key, newDoc);
|
|
1263
|
-
|
|
1555
|
+
Y2.applyUpdate(newDoc, crdtValue.value);
|
|
1264
1556
|
}
|
|
1265
1557
|
this.propertiesAndTheirOpIds.set(`${op.key}@${op.targetId}`, op.id);
|
|
1266
1558
|
this.reportOpAsApplied(op);
|
|
@@ -1387,6 +1679,7 @@ export {
|
|
|
1387
1679
|
Vertex,
|
|
1388
1680
|
VertexState,
|
|
1389
1681
|
bindVertex,
|
|
1682
|
+
defaultAliases,
|
|
1390
1683
|
isAnyPropertyOp,
|
|
1391
1684
|
isLWWPropertyOp,
|
|
1392
1685
|
isModifyPropertyOp,
|