@verdant-web/common 1.11.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/LICENSE +21 -0
- package/dist/cjs/EventSubscriber.d.ts +17 -0
- package/dist/cjs/EventSubscriber.js +67 -0
- package/dist/cjs/EventSubscriber.js.map +1 -0
- package/dist/cjs/baseline.d.ts +5 -0
- package/dist/cjs/baseline.js +3 -0
- package/dist/cjs/baseline.js.map +1 -0
- package/dist/cjs/batching.d.ts +41 -0
- package/dist/cjs/batching.js +89 -0
- package/dist/cjs/batching.js.map +1 -0
- package/dist/cjs/files.d.ts +19 -0
- package/dist/cjs/files.js +19 -0
- package/dist/cjs/files.js.map +1 -0
- package/dist/cjs/files.test.d.ts +1 -0
- package/dist/cjs/files.test.js +81 -0
- package/dist/cjs/files.test.js.map +1 -0
- package/dist/cjs/index.d.ts +18 -0
- package/dist/cjs/index.js +36 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/indexes.d.ts +14 -0
- package/dist/cjs/indexes.js +116 -0
- package/dist/cjs/indexes.js.map +1 -0
- package/dist/cjs/indexes.test.d.ts +1 -0
- package/dist/cjs/indexes.test.js +128 -0
- package/dist/cjs/indexes.test.js.map +1 -0
- package/dist/cjs/migration.d.ts +98 -0
- package/dist/cjs/migration.js +128 -0
- package/dist/cjs/migration.js.map +1 -0
- package/dist/cjs/oids.d.ts +87 -0
- package/dist/cjs/oids.js +375 -0
- package/dist/cjs/oids.js.map +1 -0
- package/dist/cjs/oids.test.d.ts +1 -0
- package/dist/cjs/oids.test.js +282 -0
- package/dist/cjs/oids.test.js.map +1 -0
- package/dist/cjs/operation.d.ts +139 -0
- package/dist/cjs/operation.js +533 -0
- package/dist/cjs/operation.js.map +1 -0
- package/dist/cjs/operation.test.d.ts +1 -0
- package/dist/cjs/operation.test.js +827 -0
- package/dist/cjs/operation.test.js.map +1 -0
- package/dist/cjs/patch.d.ts +26 -0
- package/dist/cjs/patch.js +227 -0
- package/dist/cjs/patch.js.map +1 -0
- package/dist/cjs/presence.d.ts +27 -0
- package/dist/cjs/presence.js +3 -0
- package/dist/cjs/presence.js.map +1 -0
- package/dist/cjs/protocol.d.ts +145 -0
- package/dist/cjs/protocol.js +4 -0
- package/dist/cjs/protocol.js.map +1 -0
- package/dist/cjs/refs.d.ts +5 -0
- package/dist/cjs/refs.js +22 -0
- package/dist/cjs/refs.js.map +1 -0
- package/dist/cjs/replica.d.ts +28 -0
- package/dist/cjs/replica.js +29 -0
- package/dist/cjs/replica.js.map +1 -0
- package/dist/cjs/schema/defaults.d.ts +4 -0
- package/dist/cjs/schema/defaults.js +72 -0
- package/dist/cjs/schema/defaults.js.map +1 -0
- package/dist/cjs/schema/defaults.test.d.ts +1 -0
- package/dist/cjs/schema/defaults.test.js +21 -0
- package/dist/cjs/schema/defaults.test.js.map +1 -0
- package/dist/cjs/schema/index.d.ts +8 -0
- package/dist/cjs/schema/index.js +29 -0
- package/dist/cjs/schema/index.js.map +1 -0
- package/dist/cjs/schema/indexFilters.d.ts +6 -0
- package/dist/cjs/schema/indexFilters.js +31 -0
- package/dist/cjs/schema/indexFilters.js.map +1 -0
- package/dist/cjs/schema/types/collection.d.ts +51 -0
- package/dist/cjs/schema/types/collection.js +3 -0
- package/dist/cjs/schema/types/collection.js.map +1 -0
- package/dist/cjs/schema/types/compounds.d.ts +12 -0
- package/dist/cjs/schema/types/compounds.js +3 -0
- package/dist/cjs/schema/types/compounds.js.map +1 -0
- package/dist/cjs/schema/types/fields.d.ts +59 -0
- package/dist/cjs/schema/types/fields.js +3 -0
- package/dist/cjs/schema/types/fields.js.map +1 -0
- package/dist/cjs/schema/types/filters.d.ts +29 -0
- package/dist/cjs/schema/types/filters.js +3 -0
- package/dist/cjs/schema/types/filters.js.map +1 -0
- package/dist/cjs/schema/types/shapes.d.ts +21 -0
- package/dist/cjs/schema/types/shapes.js +3 -0
- package/dist/cjs/schema/types/shapes.js.map +1 -0
- package/dist/cjs/schema/types/synthetics.d.ts +28 -0
- package/dist/cjs/schema/types/synthetics.js +3 -0
- package/dist/cjs/schema/types/synthetics.js.map +1 -0
- package/dist/cjs/schema/types.d.ts +18 -0
- package/dist/cjs/schema/types.js +23 -0
- package/dist/cjs/schema/types.js.map +1 -0
- package/dist/cjs/timestamp.d.ts +48 -0
- package/dist/cjs/timestamp.js +244 -0
- package/dist/cjs/timestamp.js.map +1 -0
- package/dist/cjs/timestamp.test.d.ts +1 -0
- package/dist/cjs/timestamp.test.js +94 -0
- package/dist/cjs/timestamp.test.js.map +1 -0
- package/dist/cjs/undo.d.ts +3 -0
- package/dist/cjs/undo.js +199 -0
- package/dist/cjs/undo.js.map +1 -0
- package/dist/cjs/utils.d.ts +15 -0
- package/dist/cjs/utils.js +128 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/cjs/utils.test.d.ts +1 -0
- package/dist/cjs/utils.test.js +37 -0
- package/dist/cjs/utils.test.js.map +1 -0
- package/dist/esm/EventSubscriber.d.ts +17 -0
- package/dist/esm/EventSubscriber.js +63 -0
- package/dist/esm/EventSubscriber.js.map +1 -0
- package/dist/esm/baseline.d.ts +5 -0
- package/dist/esm/baseline.js +2 -0
- package/dist/esm/baseline.js.map +1 -0
- package/dist/esm/batching.d.ts +41 -0
- package/dist/esm/batching.js +84 -0
- package/dist/esm/batching.js.map +1 -0
- package/dist/esm/files.d.ts +19 -0
- package/dist/esm/files.js +13 -0
- package/dist/esm/files.js.map +1 -0
- package/dist/esm/files.test.d.ts +1 -0
- package/dist/esm/files.test.js +79 -0
- package/dist/esm/files.test.js.map +1 -0
- package/dist/esm/index.d.ts +18 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/indexes.d.ts +14 -0
- package/dist/esm/indexes.js +106 -0
- package/dist/esm/indexes.js.map +1 -0
- package/dist/esm/indexes.test.d.ts +1 -0
- package/dist/esm/indexes.test.js +126 -0
- package/dist/esm/indexes.test.js.map +1 -0
- package/dist/esm/migration.d.ts +98 -0
- package/dist/esm/migration.js +122 -0
- package/dist/esm/migration.js.map +1 -0
- package/dist/esm/oids.d.ts +87 -0
- package/dist/esm/oids.js +348 -0
- package/dist/esm/oids.js.map +1 -0
- package/dist/esm/oids.test.d.ts +1 -0
- package/dist/esm/oids.test.js +280 -0
- package/dist/esm/oids.test.js.map +1 -0
- package/dist/esm/operation.d.ts +139 -0
- package/dist/esm/operation.js +518 -0
- package/dist/esm/operation.js.map +1 -0
- package/dist/esm/operation.test.d.ts +1 -0
- package/dist/esm/operation.test.js +825 -0
- package/dist/esm/operation.test.js.map +1 -0
- package/dist/esm/patch.d.ts +26 -0
- package/dist/esm/patch.js +223 -0
- package/dist/esm/patch.js.map +1 -0
- package/dist/esm/presence.d.ts +27 -0
- package/dist/esm/presence.js +2 -0
- package/dist/esm/presence.js.map +1 -0
- package/dist/esm/protocol.d.ts +145 -0
- package/dist/esm/protocol.js +3 -0
- package/dist/esm/protocol.js.map +1 -0
- package/dist/esm/refs.d.ts +5 -0
- package/dist/esm/refs.js +17 -0
- package/dist/esm/refs.js.map +1 -0
- package/dist/esm/replica.d.ts +28 -0
- package/dist/esm/replica.js +26 -0
- package/dist/esm/replica.js.map +1 -0
- package/dist/esm/schema/defaults.d.ts +4 -0
- package/dist/esm/schema/defaults.js +66 -0
- package/dist/esm/schema/defaults.js.map +1 -0
- package/dist/esm/schema/defaults.test.d.ts +1 -0
- package/dist/esm/schema/defaults.test.js +19 -0
- package/dist/esm/schema/defaults.test.js.map +1 -0
- package/dist/esm/schema/index.d.ts +8 -0
- package/dist/esm/schema/index.js +10 -0
- package/dist/esm/schema/index.js.map +1 -0
- package/dist/esm/schema/indexFilters.d.ts +6 -0
- package/dist/esm/schema/indexFilters.js +23 -0
- package/dist/esm/schema/indexFilters.js.map +1 -0
- package/dist/esm/schema/types/collection.d.ts +51 -0
- package/dist/esm/schema/types/collection.js +2 -0
- package/dist/esm/schema/types/collection.js.map +1 -0
- package/dist/esm/schema/types/compounds.d.ts +12 -0
- package/dist/esm/schema/types/compounds.js +2 -0
- package/dist/esm/schema/types/compounds.js.map +1 -0
- package/dist/esm/schema/types/fields.d.ts +59 -0
- package/dist/esm/schema/types/fields.js +2 -0
- package/dist/esm/schema/types/fields.js.map +1 -0
- package/dist/esm/schema/types/filters.d.ts +29 -0
- package/dist/esm/schema/types/filters.js +2 -0
- package/dist/esm/schema/types/filters.js.map +1 -0
- package/dist/esm/schema/types/shapes.d.ts +21 -0
- package/dist/esm/schema/types/shapes.js +2 -0
- package/dist/esm/schema/types/shapes.js.map +1 -0
- package/dist/esm/schema/types/synthetics.d.ts +28 -0
- package/dist/esm/schema/types/synthetics.js +2 -0
- package/dist/esm/schema/types/synthetics.js.map +1 -0
- package/dist/esm/schema/types.d.ts +18 -0
- package/dist/esm/schema/types.js +7 -0
- package/dist/esm/schema/types.js.map +1 -0
- package/dist/esm/timestamp.d.ts +48 -0
- package/dist/esm/timestamp.js +229 -0
- package/dist/esm/timestamp.js.map +1 -0
- package/dist/esm/timestamp.test.d.ts +1 -0
- package/dist/esm/timestamp.test.js +92 -0
- package/dist/esm/timestamp.test.js.map +1 -0
- package/dist/esm/undo.d.ts +3 -0
- package/dist/esm/undo.js +195 -0
- package/dist/esm/undo.js.map +1 -0
- package/dist/esm/utils.d.ts +15 -0
- package/dist/esm/utils.js +114 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/utils.test.d.ts +1 -0
- package/dist/esm/utils.test.js +35 -0
- package/dist/esm/utils.test.js.map +1 -0
- package/dist/tsconfig-cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +38 -0
- package/src/EventSubscriber.ts +81 -0
- package/src/baseline.ts +9 -0
- package/src/batching.ts +135 -0
- package/src/files.ts +34 -0
- package/src/index.ts +26 -0
- package/src/indexes.test.ts +158 -0
- package/src/indexes.ts +151 -0
- package/src/migration.ts +391 -0
- package/src/oids.test.ts +395 -0
- package/src/oids.ts +442 -0
- package/src/operation.test.ts +966 -0
- package/src/operation.ts +755 -0
- package/src/patch.ts +277 -0
- package/src/presence.ts +29 -0
- package/src/protocol.ts +184 -0
- package/src/refs.ts +16 -0
- package/src/replica.ts +29 -0
- package/src/schema/defaults.test.ts +19 -0
- package/src/schema/defaults.ts +72 -0
- package/src/schema/index.ts +28 -0
- package/src/schema/indexFilters.ts +49 -0
- package/src/schema/types/collection.ts +133 -0
- package/src/schema/types/compounds.ts +33 -0
- package/src/schema/types/fields.ts +85 -0
- package/src/schema/types/filters.ts +39 -0
- package/src/schema/types/shapes.ts +83 -0
- package/src/schema/types/synthetics.ts +45 -0
- package/src/schema/types.ts +28 -0
- package/src/timestamp.test.ts +113 -0
- package/src/timestamp.ts +284 -0
- package/src/undo.ts +205 -0
- package/src/utils.test.ts +38 -0
- package/src/utils.ts +137 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { CollectionCompoundIndices } from './compounds.js';
|
|
2
|
+
import {
|
|
3
|
+
NestedStorageFieldSchema,
|
|
4
|
+
StorageFieldSchema,
|
|
5
|
+
StorageFieldsSchema,
|
|
6
|
+
StorageIndexableFields,
|
|
7
|
+
} from './fields.js';
|
|
8
|
+
import { StorageDocument } from './shapes.js';
|
|
9
|
+
import {
|
|
10
|
+
StorageSyntheticIndexSchema,
|
|
11
|
+
StorageSyntheticIndices,
|
|
12
|
+
} from './synthetics.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The main collection schema
|
|
16
|
+
*/
|
|
17
|
+
export type StorageCollectionSchema<
|
|
18
|
+
Fields extends StorageFieldsSchema = StorageFieldsSchema,
|
|
19
|
+
Synthetics extends StorageSyntheticIndices<Fields> = StorageSyntheticIndices<Fields>,
|
|
20
|
+
Compounds extends CollectionCompoundIndices<
|
|
21
|
+
Fields,
|
|
22
|
+
Synthetics
|
|
23
|
+
> = CollectionCompoundIndices<Fields, Synthetics>,
|
|
24
|
+
> = {
|
|
25
|
+
name: string;
|
|
26
|
+
/**
|
|
27
|
+
* Optionally provide a plural name. Useful for some code generation purposes.
|
|
28
|
+
*/
|
|
29
|
+
pluralName?: string;
|
|
30
|
+
fields: Fields;
|
|
31
|
+
synthetics?: Synthetics;
|
|
32
|
+
compounds?: Compounds;
|
|
33
|
+
primaryKey: StorageIndexablePropertyName<Fields, Synthetics>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type NamedSchema<
|
|
37
|
+
Schemas extends StorageCollectionSchema<any, any, any>,
|
|
38
|
+
Name extends string,
|
|
39
|
+
> = Schemas extends { name: Name } ? Schemas : never;
|
|
40
|
+
|
|
41
|
+
export type IndexedSchemaProperties<
|
|
42
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
43
|
+
> = {
|
|
44
|
+
[K in keyof CollectionSchemaFields<Schema> as CollectionSchemaFields<Schema>[K] extends {
|
|
45
|
+
indexed: true;
|
|
46
|
+
}
|
|
47
|
+
? K
|
|
48
|
+
: never]: CollectionSchemaFields<Schema>[K];
|
|
49
|
+
} & CollectionSchemaComputedIndexes<Schema> &
|
|
50
|
+
CollectionSchemaCompoundIndexes<Schema>;
|
|
51
|
+
|
|
52
|
+
export type CollectionIndexName<
|
|
53
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
54
|
+
> = Extract<keyof IndexedSchemaProperties<Collection>, string>;
|
|
55
|
+
|
|
56
|
+
export type CollectionProperties<
|
|
57
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
58
|
+
> = Collection['fields'] & Collection['synthetics'];
|
|
59
|
+
|
|
60
|
+
export type CollectionPropertyName<
|
|
61
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
62
|
+
> = Exclude<keyof CollectionProperties<Collection>, number | symbol>;
|
|
63
|
+
|
|
64
|
+
export type CollectionEvents<
|
|
65
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
66
|
+
> = {
|
|
67
|
+
put: (value: StorageDocument<Collection>) => void;
|
|
68
|
+
delete: (id: string) => void;
|
|
69
|
+
[key: `put:${string}`]: (value: StorageDocument<Collection>) => void;
|
|
70
|
+
[key: `delete:${string}`]: () => void;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type SchemaForCollection<
|
|
74
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
75
|
+
> = Collection;
|
|
76
|
+
|
|
77
|
+
export type CollectionSchemaFields<
|
|
78
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
79
|
+
> = Schema extends StorageCollectionSchema<infer F, any, any> ? F : never;
|
|
80
|
+
export type CollectionSchemaComputedIndexes<
|
|
81
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
82
|
+
> = Schema extends StorageCollectionSchema<any, infer S, any> ? S : never;
|
|
83
|
+
export type CollectionSchemaCompoundIndexes<
|
|
84
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
85
|
+
> = Schema extends StorageCollectionSchema<any, any, infer C> ? C : never;
|
|
86
|
+
|
|
87
|
+
export type StorageIndexableProperties<
|
|
88
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
89
|
+
> = {
|
|
90
|
+
[K in keyof CollectionSchemaFields<Schema>]: CollectionSchemaFields<Schema>[K] extends {
|
|
91
|
+
indexed: boolean;
|
|
92
|
+
}
|
|
93
|
+
? CollectionSchemaFields<Schema>[K]
|
|
94
|
+
: never;
|
|
95
|
+
} & CollectionSchemaComputedIndexes<Schema>;
|
|
96
|
+
|
|
97
|
+
export type StorageSchemaProperty<
|
|
98
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
99
|
+
> = StorageSchemaProperties<Schema>[keyof StorageSchemaProperties<Schema>];
|
|
100
|
+
|
|
101
|
+
export type StorageSchemaPropertyName<
|
|
102
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
103
|
+
> = Extract<keyof StorageSchemaProperties<Schema>, string>;
|
|
104
|
+
|
|
105
|
+
export type GetSchemaProperty<
|
|
106
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
107
|
+
Key extends StorageSchemaPropertyName<Schema>,
|
|
108
|
+
> = StorageSchemaProperties<Schema>[Key];
|
|
109
|
+
|
|
110
|
+
export type StoragePropertySchema<BaseFields extends StorageFieldsSchema> =
|
|
111
|
+
| StorageFieldSchema
|
|
112
|
+
| NestedStorageFieldSchema
|
|
113
|
+
| StorageSyntheticIndexSchema<BaseFields>;
|
|
114
|
+
|
|
115
|
+
export type StoragePropertyName<
|
|
116
|
+
Fields extends StorageFieldsSchema,
|
|
117
|
+
Synthetics extends StorageSyntheticIndices<Fields>,
|
|
118
|
+
> =
|
|
119
|
+
| Exclude<keyof Fields, number | symbol>
|
|
120
|
+
| Exclude<keyof Synthetics, number | symbol>;
|
|
121
|
+
|
|
122
|
+
export type StorageIndexablePropertyName<
|
|
123
|
+
Fields extends StorageFieldsSchema,
|
|
124
|
+
Synthetics extends StorageSyntheticIndices<Fields>,
|
|
125
|
+
> =
|
|
126
|
+
| Extract<keyof StorageIndexableFields<Fields>, string>
|
|
127
|
+
| Extract<keyof Synthetics, string>;
|
|
128
|
+
|
|
129
|
+
export type StorageSchemaProperties<
|
|
130
|
+
Schema extends StorageCollectionSchema<any, any, any>,
|
|
131
|
+
> = Schema extends StorageCollectionSchema<infer F, infer S, infer C>
|
|
132
|
+
? F & S & C
|
|
133
|
+
: never;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CollectionSchemaCompoundIndexes,
|
|
3
|
+
StorageCollectionSchema,
|
|
4
|
+
StoragePropertyName,
|
|
5
|
+
} from './collection.js';
|
|
6
|
+
import { StorageFieldsSchema, StorageObjectFieldSchema } from './fields.js';
|
|
7
|
+
import { StorageSyntheticIndices } from './synthetics.js';
|
|
8
|
+
|
|
9
|
+
type OmitObjectFields<T extends StorageFieldsSchema> = {
|
|
10
|
+
[K in keyof T]: T[K] extends StorageObjectFieldSchema ? never : T[K];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type CollectionCompoundIndex<
|
|
14
|
+
Fields extends StorageFieldsSchema,
|
|
15
|
+
Synthetics extends StorageSyntheticIndices<Fields>,
|
|
16
|
+
> = {
|
|
17
|
+
// object fields cannot be compound index inputs
|
|
18
|
+
of: StoragePropertyName<OmitObjectFields<Fields>, Synthetics>[];
|
|
19
|
+
};
|
|
20
|
+
type CompoundFieldNameTuple<Name extends string> =
|
|
21
|
+
| [Name, Name]
|
|
22
|
+
| [Name, Name, Name]
|
|
23
|
+
| [Name, Name, Name, Name]
|
|
24
|
+
| [Name, Name, Name, Name, Name];
|
|
25
|
+
|
|
26
|
+
export type CollectionCompoundIndices<
|
|
27
|
+
Fields extends StorageFieldsSchema,
|
|
28
|
+
Synthetics extends StorageSyntheticIndices<Fields>,
|
|
29
|
+
> = Record<string, CollectionCompoundIndex<Fields, Synthetics>>;
|
|
30
|
+
|
|
31
|
+
export type CollectionCompoundIndexName<
|
|
32
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
33
|
+
> = Exclude<keyof CollectionSchemaCompoundIndexes<Collection>, number | symbol>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export type StorageStringFieldSchema = {
|
|
2
|
+
type: 'string';
|
|
3
|
+
indexed?: boolean;
|
|
4
|
+
nullable?: boolean;
|
|
5
|
+
default?: string | (() => string);
|
|
6
|
+
};
|
|
7
|
+
export type StorageNumberFieldSchema = {
|
|
8
|
+
type: 'number';
|
|
9
|
+
indexed?: boolean;
|
|
10
|
+
nullable?: boolean;
|
|
11
|
+
default?: number | (() => number);
|
|
12
|
+
};
|
|
13
|
+
export type StorageBooleanFieldSchema = {
|
|
14
|
+
type: 'boolean';
|
|
15
|
+
nullable?: boolean;
|
|
16
|
+
default?: boolean | (() => boolean);
|
|
17
|
+
indexed?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export type StorageArrayFieldSchema = {
|
|
20
|
+
type: 'array';
|
|
21
|
+
items: NestedStorageFieldSchema;
|
|
22
|
+
nullable?: boolean;
|
|
23
|
+
};
|
|
24
|
+
export type StorageObjectFieldSchema = {
|
|
25
|
+
type: 'object';
|
|
26
|
+
properties: NestedStorageFieldsSchema;
|
|
27
|
+
nullable?: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type StorageAnyFieldSchema = {
|
|
30
|
+
type: 'any';
|
|
31
|
+
default?: any;
|
|
32
|
+
};
|
|
33
|
+
export type StorageMapFieldSchema<V extends NestedStorageFieldSchema> = {
|
|
34
|
+
type: 'map';
|
|
35
|
+
values: V;
|
|
36
|
+
};
|
|
37
|
+
export type StorageFileFieldSchema = {
|
|
38
|
+
type: 'file';
|
|
39
|
+
nullable?: boolean;
|
|
40
|
+
// downloadRemote?: boolean; TODO: is this a good feature? maybe later.
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type StorageFieldSchema =
|
|
44
|
+
| StorageStringFieldSchema
|
|
45
|
+
| StorageNumberFieldSchema
|
|
46
|
+
| StorageBooleanFieldSchema
|
|
47
|
+
| StorageArrayFieldSchema
|
|
48
|
+
| StorageObjectFieldSchema
|
|
49
|
+
| StorageAnyFieldSchema
|
|
50
|
+
| StorageMapFieldSchema<any>
|
|
51
|
+
| StorageFileFieldSchema;
|
|
52
|
+
|
|
53
|
+
// nested versions don't have index info
|
|
54
|
+
export type NestedStorageStringFieldSchema = {
|
|
55
|
+
type: 'string';
|
|
56
|
+
nullable?: boolean;
|
|
57
|
+
default?: string | (() => string);
|
|
58
|
+
};
|
|
59
|
+
export type NestedStorageNumberFieldSchema = {
|
|
60
|
+
type: 'number';
|
|
61
|
+
nullable?: boolean;
|
|
62
|
+
default?: number | (() => number);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type NestedStorageFieldSchema =
|
|
66
|
+
| NestedStorageStringFieldSchema
|
|
67
|
+
| NestedStorageNumberFieldSchema
|
|
68
|
+
| StorageBooleanFieldSchema
|
|
69
|
+
| StorageArrayFieldSchema
|
|
70
|
+
| StorageObjectFieldSchema
|
|
71
|
+
| StorageAnyFieldSchema
|
|
72
|
+
| StorageMapFieldSchema<any>
|
|
73
|
+
| StorageFileFieldSchema;
|
|
74
|
+
|
|
75
|
+
export type StorageFieldsSchema = Record<string, StorageFieldSchema>;
|
|
76
|
+
|
|
77
|
+
export type NestedStorageFieldsSchema = Record<
|
|
78
|
+
string,
|
|
79
|
+
NestedStorageFieldSchema
|
|
80
|
+
>;
|
|
81
|
+
|
|
82
|
+
// filters to only fields which can be indexed
|
|
83
|
+
export type StorageIndexableFields<Fields extends StorageFieldsSchema> = {
|
|
84
|
+
[K in keyof Fields]: Fields[K] extends { indexed: boolean } ? K : never;
|
|
85
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type MatchCollectionIndexFilter = {
|
|
2
|
+
where: string;
|
|
3
|
+
equals: any;
|
|
4
|
+
order?: 'asc' | 'desc';
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type RangeCollectionIndexFilter = {
|
|
8
|
+
where: string;
|
|
9
|
+
gte?: any;
|
|
10
|
+
lte?: any;
|
|
11
|
+
gt?: any;
|
|
12
|
+
lt?: any;
|
|
13
|
+
order?: 'asc' | 'desc';
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type CollectionCompoundIndexFilter = {
|
|
17
|
+
where: string;
|
|
18
|
+
match: Record<string, any>;
|
|
19
|
+
order: 'asc' | 'desc';
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type SortIndexFilter = {
|
|
23
|
+
where: string;
|
|
24
|
+
order: 'asc' | 'desc';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type StartsWithIndexFilter = {
|
|
28
|
+
where: string;
|
|
29
|
+
startsWith: string;
|
|
30
|
+
order?: 'asc' | 'desc';
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type CollectionIndexFilter =
|
|
34
|
+
| MatchCollectionIndexFilter
|
|
35
|
+
| RangeCollectionIndexFilter
|
|
36
|
+
| CollectionCompoundIndexFilter
|
|
37
|
+
| StartsWithIndexFilter
|
|
38
|
+
| SortIndexFilter;
|
|
39
|
+
export type CollectionFilter = CollectionIndexFilter;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StorageCollectionSchema,
|
|
3
|
+
StoragePropertySchema,
|
|
4
|
+
} from './collection.js';
|
|
5
|
+
import {
|
|
6
|
+
NestedStorageFieldsSchema,
|
|
7
|
+
StorageArrayFieldSchema,
|
|
8
|
+
StorageFieldsSchema,
|
|
9
|
+
StorageMapFieldSchema,
|
|
10
|
+
StorageObjectFieldSchema,
|
|
11
|
+
} from './fields.js';
|
|
12
|
+
|
|
13
|
+
type StoragePropertyIsNullable<T extends StoragePropertySchema<any>> =
|
|
14
|
+
T extends { nullable?: boolean }
|
|
15
|
+
? T['nullable'] extends boolean
|
|
16
|
+
? true
|
|
17
|
+
: false
|
|
18
|
+
: T['type'] extends 'any'
|
|
19
|
+
? true
|
|
20
|
+
: false;
|
|
21
|
+
|
|
22
|
+
export type BaseShapeFromProperty<T extends StoragePropertySchema<any>> =
|
|
23
|
+
T['type'] extends 'string'
|
|
24
|
+
? string
|
|
25
|
+
: T['type'] extends 'number'
|
|
26
|
+
? number
|
|
27
|
+
: T['type'] extends 'boolean'
|
|
28
|
+
? boolean
|
|
29
|
+
: T extends StorageArrayFieldSchema
|
|
30
|
+
? ShapeFromProperty<T['items']>[]
|
|
31
|
+
: T extends StorageObjectFieldSchema
|
|
32
|
+
? ShapeFromFields<T['properties']>
|
|
33
|
+
: T extends StorageMapFieldSchema<any>
|
|
34
|
+
? Record<string, ShapeFromProperty<T['values']>>
|
|
35
|
+
: T['type'] extends 'any'
|
|
36
|
+
? any
|
|
37
|
+
: T['type'] extends 'file'
|
|
38
|
+
? File
|
|
39
|
+
: never;
|
|
40
|
+
|
|
41
|
+
export type ShapeFromProperty<T extends StoragePropertySchema<any>> =
|
|
42
|
+
StoragePropertyIsNullable<T> extends true
|
|
43
|
+
? BaseShapeFromProperty<T> | null
|
|
44
|
+
: BaseShapeFromProperty<T>;
|
|
45
|
+
|
|
46
|
+
export type ShapeFromFields<
|
|
47
|
+
T extends StorageFieldsSchema | NestedStorageFieldsSchema,
|
|
48
|
+
> = {
|
|
49
|
+
[K in keyof T]: ShapeFromProperty<T[K]>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type StorageDocument<
|
|
53
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
54
|
+
> = ShapeFromFields<Collection['fields']>;
|
|
55
|
+
|
|
56
|
+
type StoragePropertyIsOptional<T extends StoragePropertySchema<any>> =
|
|
57
|
+
StoragePropertyIsNullable<T> extends true
|
|
58
|
+
? true
|
|
59
|
+
: T extends { default?: any }
|
|
60
|
+
? true
|
|
61
|
+
: T['type'] extends 'any'
|
|
62
|
+
? true
|
|
63
|
+
: T['type'] extends 'array'
|
|
64
|
+
? true
|
|
65
|
+
: T['type'] extends 'map'
|
|
66
|
+
? true
|
|
67
|
+
: false;
|
|
68
|
+
|
|
69
|
+
type ShapeFromFieldsWithDefaults<
|
|
70
|
+
T extends StorageFieldsSchema | NestedStorageFieldsSchema,
|
|
71
|
+
> = {
|
|
72
|
+
[K in keyof T as StoragePropertyIsOptional<T[K]> extends true
|
|
73
|
+
? K
|
|
74
|
+
: never]?: ShapeFromProperty<T[K]>;
|
|
75
|
+
} & {
|
|
76
|
+
[K in keyof T as StoragePropertyIsOptional<T[K]> extends true
|
|
77
|
+
? never
|
|
78
|
+
: K]: ShapeFromProperty<T[K]>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type StorageDocumentInit<
|
|
82
|
+
Collection extends StorageCollectionSchema<any, any, any>,
|
|
83
|
+
> = ShapeFromFieldsWithDefaults<Collection['fields']>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { StorageFieldsSchema } from './fields.js';
|
|
2
|
+
import { ShapeFromFields } from './shapes.js';
|
|
3
|
+
|
|
4
|
+
export type StorageStringSyntheticSchema<Fields extends StorageFieldsSchema> = {
|
|
5
|
+
type: 'string';
|
|
6
|
+
compute: (value: ShapeFromFields<Fields>) => string | null;
|
|
7
|
+
};
|
|
8
|
+
export type StorageNumberSyntheticSchema<Fields extends StorageFieldsSchema> = {
|
|
9
|
+
type: 'number';
|
|
10
|
+
compute: (value: ShapeFromFields<Fields>) => number | null;
|
|
11
|
+
};
|
|
12
|
+
export type StorageBooleanSyntheticSchema<Fields extends StorageFieldsSchema> =
|
|
13
|
+
{
|
|
14
|
+
type: 'boolean';
|
|
15
|
+
compute: (value: ShapeFromFields<Fields>) => boolean | null;
|
|
16
|
+
};
|
|
17
|
+
export type StorageStringArraySyntheticSchema<
|
|
18
|
+
Fields extends StorageFieldsSchema,
|
|
19
|
+
> = {
|
|
20
|
+
type: 'string[]';
|
|
21
|
+
compute: (value: ShapeFromFields<Fields>) => string[];
|
|
22
|
+
};
|
|
23
|
+
export type StorageNumberArraySyntheticSchema<
|
|
24
|
+
Fields extends StorageFieldsSchema,
|
|
25
|
+
> = {
|
|
26
|
+
type: 'number[]';
|
|
27
|
+
compute: (value: ShapeFromFields<Fields>) => number[];
|
|
28
|
+
};
|
|
29
|
+
export type StorageBooleanArraySyntheticSchema<
|
|
30
|
+
Fields extends StorageFieldsSchema,
|
|
31
|
+
> = {
|
|
32
|
+
type: 'boolean[]';
|
|
33
|
+
compute: (value: ShapeFromFields<Fields>) => boolean[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type StorageSyntheticIndices<Fields extends StorageFieldsSchema> =
|
|
37
|
+
Record<string, StorageSyntheticIndexSchema<Fields>>;
|
|
38
|
+
|
|
39
|
+
export type StorageSyntheticIndexSchema<Fields extends StorageFieldsSchema> =
|
|
40
|
+
| StorageStringSyntheticSchema<Fields>
|
|
41
|
+
| StorageNumberSyntheticSchema<Fields>
|
|
42
|
+
| StorageBooleanSyntheticSchema<Fields>
|
|
43
|
+
| StorageStringArraySyntheticSchema<Fields>
|
|
44
|
+
| StorageNumberArraySyntheticSchema<Fields>
|
|
45
|
+
| StorageBooleanArraySyntheticSchema<Fields>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { StorageCollectionSchema } from './types/collection.js';
|
|
2
|
+
|
|
3
|
+
export * from './types/collection.js';
|
|
4
|
+
export * from './types/compounds.js';
|
|
5
|
+
export * from './types/fields.js';
|
|
6
|
+
export * from './types/filters.js';
|
|
7
|
+
export * from './types/shapes.js';
|
|
8
|
+
export * from './types/synthetics.js';
|
|
9
|
+
|
|
10
|
+
export interface StorageInit<Schemas extends StorageCollectionSchema> {
|
|
11
|
+
collections: Record<string, Schemas>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type StorageSchema<
|
|
15
|
+
Collections extends {
|
|
16
|
+
[k: string]: StorageCollectionSchema;
|
|
17
|
+
} = any,
|
|
18
|
+
> = { version: number; collections: Collections };
|
|
19
|
+
|
|
20
|
+
export type SchemaCollectionName<Schema extends StorageSchema<any>> =
|
|
21
|
+
Schema extends StorageSchema<infer Cs>
|
|
22
|
+
? Exclude<keyof Cs, number | symbol>
|
|
23
|
+
: never;
|
|
24
|
+
|
|
25
|
+
export type SchemaCollection<
|
|
26
|
+
Schema extends StorageSchema<any>,
|
|
27
|
+
Name extends SchemaCollectionName<Schema>,
|
|
28
|
+
> = Schema extends StorageSchema<infer Cs> ? Cs[Name] : never;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, expect, it, vitest } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
convertOldHlcTimestamp,
|
|
4
|
+
deserializeHlcTimestamp,
|
|
5
|
+
HybridLogicalClockTimestampProvider,
|
|
6
|
+
OLD_encodeVersion,
|
|
7
|
+
OLD_serializeHlcTimestamp,
|
|
8
|
+
serializeHlcTimestamp,
|
|
9
|
+
} from './timestamp.js';
|
|
10
|
+
|
|
11
|
+
describe('the hybrid logical clock', () => {
|
|
12
|
+
it('always produces incrementing timestamps', async () => {
|
|
13
|
+
const clock = new HybridLogicalClockTimestampProvider();
|
|
14
|
+
let prev = clock.now(1);
|
|
15
|
+
|
|
16
|
+
clock.update(prev);
|
|
17
|
+
let now;
|
|
18
|
+
for (let i = 0; i < 1000; i++) {
|
|
19
|
+
now = clock.now(1);
|
|
20
|
+
expect(now > prev, `${now} not later than ${prev}`).toBe(true);
|
|
21
|
+
clock.update(now);
|
|
22
|
+
prev = now;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let time = new Date().getTime();
|
|
26
|
+
for (let i = 0; i < 1000; i++) {
|
|
27
|
+
vitest.setSystemTime(time++);
|
|
28
|
+
now = clock.now(1);
|
|
29
|
+
expect(now > prev, `${now} not later than ${prev}`).toBe(true);
|
|
30
|
+
clock.update(now);
|
|
31
|
+
prev = now;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('groups blocks of time by version', () => {
|
|
36
|
+
const clock = new HybridLogicalClockTimestampProvider();
|
|
37
|
+
let prev = clock.now(1);
|
|
38
|
+
clock.update(prev);
|
|
39
|
+
let now: string;
|
|
40
|
+
for (let i = 0; i < 10; i++) {
|
|
41
|
+
now = clock.now(1);
|
|
42
|
+
expect(now > prev, `${now} not later than ${prev}`).toBe(true);
|
|
43
|
+
clock.update(now);
|
|
44
|
+
prev = now;
|
|
45
|
+
}
|
|
46
|
+
for (let i = 0; i < 10; i++) {
|
|
47
|
+
now = clock.now(2);
|
|
48
|
+
expect(now > prev, `${now} not later than ${prev}`).toBe(true);
|
|
49
|
+
clock.update(now);
|
|
50
|
+
prev = now;
|
|
51
|
+
}
|
|
52
|
+
const startOfVersion2 = now!;
|
|
53
|
+
// jump back to 1
|
|
54
|
+
prev = clock.now(1);
|
|
55
|
+
clock.update(prev);
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < 10; i++) {
|
|
58
|
+
now = clock.now(1);
|
|
59
|
+
expect(now > prev, `${now} not later than ${prev}`).toBe(true);
|
|
60
|
+
expect(
|
|
61
|
+
now < startOfVersion2,
|
|
62
|
+
`${now} not earlier than ${startOfVersion2}`,
|
|
63
|
+
).toBe(true);
|
|
64
|
+
clock.update(now);
|
|
65
|
+
prev = now;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('is shorter than the previous format', () => {
|
|
70
|
+
const clock = new HybridLogicalClockTimestampProvider();
|
|
71
|
+
|
|
72
|
+
expect(clock.now(1).length).toBeLessThan(clock.OLD_now(1).length);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('correctly converts old to new format', () => {
|
|
76
|
+
const clock = new HybridLogicalClockTimestampProvider();
|
|
77
|
+
const oldFormat = clock.OLD_now(1);
|
|
78
|
+
const value = clock.timerState();
|
|
79
|
+
const converted = convertOldHlcTimestamp(oldFormat);
|
|
80
|
+
const newFormat = clock.get(1, value);
|
|
81
|
+
expect(converted).toEqual(newFormat);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// this is mostly to verify as I move to the new format.
|
|
85
|
+
// instead of attempting a distributed migration of timestamps, it
|
|
86
|
+
// will be easier that the new format just acts as always in the
|
|
87
|
+
// future of the old one. this will only mess with offline clients,
|
|
88
|
+
// but nobody is using verdant but me yet at time of this change (2022-12-19)
|
|
89
|
+
// which is kind of why I'm doing it now anyway.
|
|
90
|
+
it('old format always comes before new format', () => {
|
|
91
|
+
const clock = new HybridLogicalClockTimestampProvider();
|
|
92
|
+
const newFormat = clock.now(1);
|
|
93
|
+
const oldFormat = clock.OLD_now(1);
|
|
94
|
+
expect(oldFormat < newFormat).toBe(true);
|
|
95
|
+
|
|
96
|
+
// even far-future old timestamps are before new timestamps
|
|
97
|
+
const oldFormat2 = OLD_serializeHlcTimestamp({
|
|
98
|
+
counter: 999,
|
|
99
|
+
node: '9876543210',
|
|
100
|
+
time: new Date(3000, 1, 1).getTime(),
|
|
101
|
+
});
|
|
102
|
+
expect(OLD_encodeVersion(1) + oldFormat2 < newFormat).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('decodes timestamps', () => {
|
|
106
|
+
const time = new Date();
|
|
107
|
+
vitest.setSystemTime(time);
|
|
108
|
+
|
|
109
|
+
const clock = new HybridLogicalClockTimestampProvider();
|
|
110
|
+
const now = clock.now(1);
|
|
111
|
+
expect(clock.getWallClockTime(now)).toEqual(time.getTime());
|
|
112
|
+
});
|
|
113
|
+
});
|