@loro-extended/change 0.6.0 → 0.8.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 +26 -31
- package/dist/index.d.ts +115 -53
- package/dist/index.js +743 -393
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/change.test.ts +97 -403
- package/src/derive-placeholder.test.ts +245 -0
- package/src/derive-placeholder.ts +132 -0
- package/src/discriminated-union.test.ts +74 -1
- package/src/draft-nodes/base.ts +9 -4
- package/src/draft-nodes/counter.md +31 -0
- package/src/draft-nodes/counter.test.ts +53 -0
- package/src/draft-nodes/doc.ts +27 -6
- package/src/draft-nodes/list-base.ts +24 -3
- package/src/draft-nodes/list.test.ts +43 -0
- package/src/draft-nodes/list.ts +3 -0
- package/src/draft-nodes/map.ts +57 -16
- package/src/draft-nodes/movable-list.test.ts +27 -0
- package/src/draft-nodes/movable-list.ts +5 -0
- package/src/draft-nodes/proxy-handlers.ts +87 -0
- package/src/{record.test.ts → draft-nodes/record.test.ts} +98 -18
- package/src/draft-nodes/record.ts +42 -80
- package/src/draft-nodes/utils.ts +46 -5
- package/src/equality.test.ts +19 -0
- package/src/index.ts +14 -7
- package/src/json-patch.test.ts +33 -167
- package/src/overlay.ts +46 -39
- package/src/readonly.test.ts +92 -0
- package/src/shape.ts +131 -73
- package/src/{change.ts → typed-doc.ts} +50 -28
- package/src/types.test.ts +188 -0
- package/src/types.ts +37 -5
- package/src/utils/type-guards.ts +1 -0
- package/src/validation.ts +45 -12
package/src/types.ts
CHANGED
|
@@ -5,21 +5,53 @@
|
|
|
5
5
|
|
|
6
6
|
import type { ContainerShape, DocShape, Shape } from "./shape.js"
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Infers the plain (JSON-serializable) type from any Shape.
|
|
10
|
+
*
|
|
11
|
+
* This is the recommended way to extract types from shapes.
|
|
12
|
+
* Works with DocShape, ContainerShape, and ValueShape.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const ChatSchema = Shape.doc({
|
|
17
|
+
* messages: Shape.list(Shape.map({
|
|
18
|
+
* id: Shape.plain.string(),
|
|
19
|
+
* content: Shape.text(),
|
|
20
|
+
* })),
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* // Extract the document type
|
|
24
|
+
* type ChatDoc = Infer<typeof ChatSchema>
|
|
25
|
+
* // Result: { messages: { id: string; content: string }[] }
|
|
26
|
+
*
|
|
27
|
+
* const PresenceSchema = Shape.plain.object({
|
|
28
|
+
* name: Shape.plain.string(),
|
|
29
|
+
* cursor: Shape.plain.object({ x: Shape.plain.number(), y: Shape.plain.number() }),
|
|
30
|
+
* })
|
|
31
|
+
*
|
|
32
|
+
* // Extract the presence type
|
|
33
|
+
* type Presence = Infer<typeof PresenceSchema>
|
|
34
|
+
* // Result: { name: string; cursor: { x: number; y: number } }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export type Infer<T> = T extends Shape<infer P, any, any> ? P : never
|
|
10
38
|
|
|
11
39
|
export type InferDraftType<T> = T extends Shape<any, infer D, any> ? D : never
|
|
12
40
|
|
|
13
41
|
/**
|
|
14
|
-
* Extracts the valid
|
|
42
|
+
* Extracts the valid placeholder type from a shape.
|
|
15
43
|
*
|
|
16
44
|
* For dynamic containers (list, record, etc.), this will be constrained to
|
|
17
45
|
* empty values ([] or {}) to prevent users from expecting per-entry merging.
|
|
18
46
|
*/
|
|
19
|
-
export type
|
|
20
|
-
?
|
|
47
|
+
export type InferPlaceholderType<T> = T extends Shape<any, any, infer P>
|
|
48
|
+
? P
|
|
21
49
|
: never
|
|
22
50
|
|
|
23
51
|
// Draft-specific type inference that properly handles the draft context
|
|
24
52
|
export type Draft<T extends DocShape<Record<string, ContainerShape>>> =
|
|
25
53
|
InferDraftType<T>
|
|
54
|
+
|
|
55
|
+
export type DeepReadonly<T> = {
|
|
56
|
+
readonly [P in keyof T]: DeepReadonly<T[P]>
|
|
57
|
+
}
|
package/src/utils/type-guards.ts
CHANGED
package/src/validation.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ArrayValueShape,
|
|
3
3
|
ContainerOrValueShape,
|
|
4
|
+
DiscriminatedUnionValueShape,
|
|
4
5
|
DocShape,
|
|
5
6
|
ListContainerShape,
|
|
6
7
|
MapContainerShape,
|
|
@@ -12,7 +13,7 @@ import type {
|
|
|
12
13
|
UnionValueShape,
|
|
13
14
|
ValueShape,
|
|
14
15
|
} from "./shape.js"
|
|
15
|
-
import type {
|
|
16
|
+
import type { Infer } from "./types.js"
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Validates a value against a ContainerShape or ValueShape schema
|
|
@@ -233,6 +234,38 @@ export function validateValue(
|
|
|
233
234
|
)
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
case "discriminatedUnion": {
|
|
238
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Expected object at path ${currentPath}, got ${typeof value}`,
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const unionSchema = valueSchema as DiscriminatedUnionValueShape
|
|
245
|
+
const discriminantKey = unionSchema.discriminantKey
|
|
246
|
+
const discriminantValue = (value as Record<string, unknown>)[
|
|
247
|
+
discriminantKey
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
if (typeof discriminantValue !== "string") {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`Expected string for discriminant key "${discriminantKey}" at path ${currentPath}, got ${typeof discriminantValue}`,
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const variantSchema = unionSchema.variants[discriminantValue]
|
|
257
|
+
|
|
258
|
+
if (!variantSchema) {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Invalid discriminant value "${discriminantValue}" at path ${currentPath}. Expected one of: ${Object.keys(
|
|
261
|
+
unionSchema.variants,
|
|
262
|
+
).join(", ")}`,
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return validateValue(value, variantSchema, currentPath)
|
|
267
|
+
}
|
|
268
|
+
|
|
236
269
|
default:
|
|
237
270
|
throw new Error(`Unknown value type: ${(valueSchema as any).valueType}`)
|
|
238
271
|
}
|
|
@@ -242,28 +275,28 @@ export function validateValue(
|
|
|
242
275
|
}
|
|
243
276
|
|
|
244
277
|
/**
|
|
245
|
-
* Validates
|
|
246
|
-
* Combines the functionality of
|
|
278
|
+
* Validates placeholder against schema structure without using Zod
|
|
279
|
+
* Combines the functionality of createPlaceholderValidator and createValueValidator
|
|
247
280
|
*/
|
|
248
|
-
export function
|
|
249
|
-
|
|
281
|
+
export function validatePlaceholder<T extends DocShape>(
|
|
282
|
+
placeholder: unknown,
|
|
250
283
|
schema: T,
|
|
251
|
-
):
|
|
284
|
+
): Infer<T> {
|
|
252
285
|
if (
|
|
253
|
-
!
|
|
254
|
-
typeof
|
|
255
|
-
Array.isArray(
|
|
286
|
+
!placeholder ||
|
|
287
|
+
typeof placeholder !== "object" ||
|
|
288
|
+
Array.isArray(placeholder)
|
|
256
289
|
) {
|
|
257
|
-
throw new Error("
|
|
290
|
+
throw new Error("Placeholder must be an object")
|
|
258
291
|
}
|
|
259
292
|
|
|
260
293
|
const result: Record<string, unknown> = {}
|
|
261
294
|
|
|
262
295
|
// Validate each property in the document schema
|
|
263
296
|
for (const [key, schemaValue] of Object.entries(schema.shapes)) {
|
|
264
|
-
const value = (
|
|
297
|
+
const value = (placeholder as Record<string, unknown>)[key]
|
|
265
298
|
result[key] = validateValue(value, schemaValue, key)
|
|
266
299
|
}
|
|
267
300
|
|
|
268
|
-
return result as
|
|
301
|
+
return result as Infer<T>
|
|
269
302
|
}
|