@docukit/docsync 0.0.1-alpha.1
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.md +20 -0
- package/README.md +1 -0
- package/dist/src/client/index.d.ts +158 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +832 -0
- package/dist/src/client/index.js.map +1 -0
- package/dist/src/client/providers/indexeddb.d.ts +15 -0
- package/dist/src/client/providers/indexeddb.d.ts.map +1 -0
- package/dist/src/client/providers/indexeddb.js +79 -0
- package/dist/src/client/providers/indexeddb.js.map +1 -0
- package/dist/src/exports/client.d.ts +4 -0
- package/dist/src/exports/client.d.ts.map +1 -0
- package/dist/src/exports/client.js +3 -0
- package/dist/src/exports/client.js.map +1 -0
- package/dist/src/exports/docnode.d.ts +2 -0
- package/dist/src/exports/docnode.d.ts.map +1 -0
- package/dist/src/exports/docnode.js +2 -0
- package/dist/src/exports/docnode.js.map +1 -0
- package/dist/src/exports/index.d.ts +3 -0
- package/dist/src/exports/index.d.ts.map +1 -0
- package/dist/src/exports/index.js +3 -0
- package/dist/src/exports/index.js.map +1 -0
- package/dist/src/exports/server.d.ts +5 -0
- package/dist/src/exports/server.d.ts.map +1 -0
- package/dist/src/exports/server.js +4 -0
- package/dist/src/exports/server.js.map +1 -0
- package/dist/src/exports/testing.d.ts +7 -0
- package/dist/src/exports/testing.d.ts.map +1 -0
- package/dist/src/exports/testing.js +6 -0
- package/dist/src/exports/testing.js.map +1 -0
- package/dist/src/server/cli.d.ts +2 -0
- package/dist/src/server/cli.d.ts.map +1 -0
- package/dist/src/server/cli.js +10 -0
- package/dist/src/server/cli.js.map +1 -0
- package/dist/src/server/index.d.ts +37 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +450 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/providers/memory.d.ts +17 -0
- package/dist/src/server/providers/memory.d.ts.map +1 -0
- package/dist/src/server/providers/memory.js +72 -0
- package/dist/src/server/providers/memory.js.map +1 -0
- package/dist/src/server/providers/postgres/drizzle.config.d.ts +3 -0
- package/dist/src/server/providers/postgres/drizzle.config.d.ts.map +1 -0
- package/dist/src/server/providers/postgres/drizzle.config.js +10 -0
- package/dist/src/server/providers/postgres/drizzle.config.js.map +1 -0
- package/dist/src/server/providers/postgres/index.d.ts +6 -0
- package/dist/src/server/providers/postgres/index.d.ts.map +1 -0
- package/dist/src/server/providers/postgres/index.js +83 -0
- package/dist/src/server/providers/postgres/index.js.map +1 -0
- package/dist/src/server/providers/postgres/schema.d.ts +159 -0
- package/dist/src/server/providers/postgres/schema.d.ts.map +1 -0
- package/dist/src/server/providers/postgres/schema.js +34 -0
- package/dist/src/server/providers/postgres/schema.js.map +1 -0
- package/dist/src/shared/debounce.d.ts +2 -0
- package/dist/src/shared/debounce.d.ts.map +1 -0
- package/dist/src/shared/debounce.js +10 -0
- package/dist/src/shared/debounce.js.map +1 -0
- package/dist/src/shared/docBinding.d.ts +17 -0
- package/dist/src/shared/docBinding.d.ts.map +1 -0
- package/dist/src/shared/docBinding.js +41 -0
- package/dist/src/shared/docBinding.js.map +1 -0
- package/dist/src/shared/throttle.d.ts +30 -0
- package/dist/src/shared/throttle.d.ts.map +1 -0
- package/dist/src/shared/throttle.js +51 -0
- package/dist/src/shared/throttle.js.map +1 -0
- package/dist/src/shared/types.d.ts +387 -0
- package/dist/src/shared/types.d.ts.map +1 -0
- package/dist/src/shared/types.js +6 -0
- package/dist/src/shared/types.js.map +1 -0
- package/dist/src/shared/utils.d.ts +2 -0
- package/dist/src/shared/utils.d.ts.map +1 -0
- package/dist/src/shared/utils.js +11 -0
- package/dist/src/shared/utils.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { queryClient } from "./schema.js";
|
|
2
|
+
import { drizzle } from "drizzle-orm/postgres-js";
|
|
3
|
+
import * as schema from "./schema.js";
|
|
4
|
+
import { eq, gt, and } from "drizzle-orm";
|
|
5
|
+
export class PostgresProvider {
|
|
6
|
+
_db = drizzle(queryClient, { schema });
|
|
7
|
+
async transaction(_mode, callback) {
|
|
8
|
+
return await this._db.transaction(async (tx) => {
|
|
9
|
+
const ctx = {
|
|
10
|
+
getSerializedDoc: async (docId) => {
|
|
11
|
+
const doc = await tx.query.documents.findFirst({
|
|
12
|
+
where: eq(schema.documents.docId, docId),
|
|
13
|
+
});
|
|
14
|
+
return doc
|
|
15
|
+
? {
|
|
16
|
+
serializedDoc: doc.doc,
|
|
17
|
+
clock: doc.clock.getTime(),
|
|
18
|
+
}
|
|
19
|
+
: undefined;
|
|
20
|
+
},
|
|
21
|
+
getOperations: async ({ docId, clock }) => {
|
|
22
|
+
const clockDate = new Date(clock);
|
|
23
|
+
const serverOps = await tx
|
|
24
|
+
.select({ operations: schema.operations.operations })
|
|
25
|
+
.from(schema.operations)
|
|
26
|
+
.where(and(eq(schema.operations.docId, docId), gt(schema.operations.clock, clockDate)))
|
|
27
|
+
.orderBy(schema.operations.clock);
|
|
28
|
+
return serverOps.map((r) => r.operations);
|
|
29
|
+
},
|
|
30
|
+
deleteOperations: async ({ docId, count }) => {
|
|
31
|
+
// Get the first `count` operations ordered by clock (oldest first)
|
|
32
|
+
const toDelete = await tx
|
|
33
|
+
.select({
|
|
34
|
+
docId: schema.operations.docId,
|
|
35
|
+
clock: schema.operations.clock,
|
|
36
|
+
})
|
|
37
|
+
.from(schema.operations)
|
|
38
|
+
.where(eq(schema.operations.docId, docId))
|
|
39
|
+
.orderBy(schema.operations.clock)
|
|
40
|
+
.limit(count);
|
|
41
|
+
if (toDelete.length > 0) {
|
|
42
|
+
// Delete operations using their composite primary key (docId, clock)
|
|
43
|
+
for (const op of toDelete) {
|
|
44
|
+
await tx
|
|
45
|
+
.delete(schema.operations)
|
|
46
|
+
.where(and(eq(schema.operations.docId, op.docId), eq(schema.operations.clock, op.clock)));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
saveOperations: async ({ docId, operations }) => {
|
|
51
|
+
if (operations.length === 0) {
|
|
52
|
+
// Return the latest clock for this docId
|
|
53
|
+
const latestOp = await tx.query.operations.findFirst({
|
|
54
|
+
where: eq(schema.operations.docId, docId),
|
|
55
|
+
orderBy: (ops, { desc }) => [desc(ops.clock)],
|
|
56
|
+
});
|
|
57
|
+
return latestOp?.clock.getTime() ?? 0;
|
|
58
|
+
}
|
|
59
|
+
// Insert operations and return the DB-generated timestamp
|
|
60
|
+
const inserted = await tx
|
|
61
|
+
.insert(schema.operations)
|
|
62
|
+
.values({
|
|
63
|
+
docId,
|
|
64
|
+
operations: operations,
|
|
65
|
+
})
|
|
66
|
+
.returning({ clock: schema.operations.clock });
|
|
67
|
+
return inserted[0].clock.getTime();
|
|
68
|
+
},
|
|
69
|
+
saveSerializedDoc: async (_arg) => {
|
|
70
|
+
// TODO: saveSerializedDoc needs userId to work with Postgres schema.
|
|
71
|
+
// This will be called during operation squashing (not implemented yet).
|
|
72
|
+
// Options:
|
|
73
|
+
// 1. Pass userId through TransactionContext
|
|
74
|
+
// 2. Query userId from operations table
|
|
75
|
+
// 3. Refactor schema to not require userId on documents table
|
|
76
|
+
throw new Error("saveSerializedDoc not implemented for PostgresProvider yet - requires userId context");
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
return callback(ctx);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/server/providers/postgres/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import postgres from "postgres";
|
|
2
|
+
export declare const queryClient: postgres.Sql<{}>;
|
|
3
|
+
export declare const documents: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
4
|
+
name: "docsync-documents";
|
|
5
|
+
schema: undefined;
|
|
6
|
+
columns: {
|
|
7
|
+
userId: import("drizzle-orm/pg-core").PgColumn<{
|
|
8
|
+
name: "userId";
|
|
9
|
+
tableName: "docsync-documents";
|
|
10
|
+
dataType: "string";
|
|
11
|
+
columnType: "PgVarchar";
|
|
12
|
+
data: string;
|
|
13
|
+
driverParam: string;
|
|
14
|
+
notNull: true;
|
|
15
|
+
hasDefault: false;
|
|
16
|
+
isPrimaryKey: false;
|
|
17
|
+
isAutoincrement: false;
|
|
18
|
+
hasRuntimeDefault: false;
|
|
19
|
+
enumValues: [string, ...string[]];
|
|
20
|
+
baseColumn: never;
|
|
21
|
+
identity: undefined;
|
|
22
|
+
generated: undefined;
|
|
23
|
+
}, {}, {
|
|
24
|
+
length: 26;
|
|
25
|
+
}>;
|
|
26
|
+
docId: import("drizzle-orm/pg-core").PgColumn<{
|
|
27
|
+
name: "docId";
|
|
28
|
+
tableName: "docsync-documents";
|
|
29
|
+
dataType: "string";
|
|
30
|
+
columnType: "PgVarchar";
|
|
31
|
+
data: string;
|
|
32
|
+
driverParam: string;
|
|
33
|
+
notNull: true;
|
|
34
|
+
hasDefault: false;
|
|
35
|
+
isPrimaryKey: true;
|
|
36
|
+
isAutoincrement: false;
|
|
37
|
+
hasRuntimeDefault: false;
|
|
38
|
+
enumValues: [string, ...string[]];
|
|
39
|
+
baseColumn: never;
|
|
40
|
+
identity: undefined;
|
|
41
|
+
generated: undefined;
|
|
42
|
+
}, {}, {
|
|
43
|
+
length: 26;
|
|
44
|
+
}>;
|
|
45
|
+
doc: import("drizzle-orm/pg-core").PgColumn<{
|
|
46
|
+
name: "doc";
|
|
47
|
+
tableName: "docsync-documents";
|
|
48
|
+
dataType: "json";
|
|
49
|
+
columnType: "PgJsonb";
|
|
50
|
+
data: unknown;
|
|
51
|
+
driverParam: unknown;
|
|
52
|
+
notNull: true;
|
|
53
|
+
hasDefault: false;
|
|
54
|
+
isPrimaryKey: false;
|
|
55
|
+
isAutoincrement: false;
|
|
56
|
+
hasRuntimeDefault: false;
|
|
57
|
+
enumValues: undefined;
|
|
58
|
+
baseColumn: never;
|
|
59
|
+
identity: undefined;
|
|
60
|
+
generated: undefined;
|
|
61
|
+
}, {}, {}>;
|
|
62
|
+
clock: import("drizzle-orm/pg-core").PgColumn<{
|
|
63
|
+
name: "clock";
|
|
64
|
+
tableName: "docsync-documents";
|
|
65
|
+
dataType: "date";
|
|
66
|
+
columnType: "PgTimestamp";
|
|
67
|
+
data: Date;
|
|
68
|
+
driverParam: string;
|
|
69
|
+
notNull: true;
|
|
70
|
+
hasDefault: false;
|
|
71
|
+
isPrimaryKey: false;
|
|
72
|
+
isAutoincrement: false;
|
|
73
|
+
hasRuntimeDefault: false;
|
|
74
|
+
enumValues: undefined;
|
|
75
|
+
baseColumn: never;
|
|
76
|
+
identity: undefined;
|
|
77
|
+
generated: undefined;
|
|
78
|
+
}, {}, {}>;
|
|
79
|
+
permissions: import("drizzle-orm/pg-core").PgColumn<{
|
|
80
|
+
name: "permissions";
|
|
81
|
+
tableName: "docsync-documents";
|
|
82
|
+
dataType: "json";
|
|
83
|
+
columnType: "PgJsonb";
|
|
84
|
+
data: unknown;
|
|
85
|
+
driverParam: unknown;
|
|
86
|
+
notNull: false;
|
|
87
|
+
hasDefault: false;
|
|
88
|
+
isPrimaryKey: false;
|
|
89
|
+
isAutoincrement: false;
|
|
90
|
+
hasRuntimeDefault: false;
|
|
91
|
+
enumValues: undefined;
|
|
92
|
+
baseColumn: never;
|
|
93
|
+
identity: undefined;
|
|
94
|
+
generated: undefined;
|
|
95
|
+
}, {}, {}>;
|
|
96
|
+
};
|
|
97
|
+
dialect: "pg";
|
|
98
|
+
}>;
|
|
99
|
+
export declare const operations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
100
|
+
name: "docsync-operations";
|
|
101
|
+
schema: undefined;
|
|
102
|
+
columns: {
|
|
103
|
+
docId: import("drizzle-orm/pg-core").PgColumn<{
|
|
104
|
+
name: "docId";
|
|
105
|
+
tableName: "docsync-operations";
|
|
106
|
+
dataType: "string";
|
|
107
|
+
columnType: "PgVarchar";
|
|
108
|
+
data: string;
|
|
109
|
+
driverParam: string;
|
|
110
|
+
notNull: true;
|
|
111
|
+
hasDefault: false;
|
|
112
|
+
isPrimaryKey: false;
|
|
113
|
+
isAutoincrement: false;
|
|
114
|
+
hasRuntimeDefault: false;
|
|
115
|
+
enumValues: [string, ...string[]];
|
|
116
|
+
baseColumn: never;
|
|
117
|
+
identity: undefined;
|
|
118
|
+
generated: undefined;
|
|
119
|
+
}, {}, {
|
|
120
|
+
length: 26;
|
|
121
|
+
}>;
|
|
122
|
+
operations: import("drizzle-orm/pg-core").PgColumn<{
|
|
123
|
+
name: "operations";
|
|
124
|
+
tableName: "docsync-operations";
|
|
125
|
+
dataType: "json";
|
|
126
|
+
columnType: "PgJsonb";
|
|
127
|
+
data: unknown;
|
|
128
|
+
driverParam: unknown;
|
|
129
|
+
notNull: true;
|
|
130
|
+
hasDefault: false;
|
|
131
|
+
isPrimaryKey: false;
|
|
132
|
+
isAutoincrement: false;
|
|
133
|
+
hasRuntimeDefault: false;
|
|
134
|
+
enumValues: undefined;
|
|
135
|
+
baseColumn: never;
|
|
136
|
+
identity: undefined;
|
|
137
|
+
generated: undefined;
|
|
138
|
+
}, {}, {}>;
|
|
139
|
+
clock: import("drizzle-orm/pg-core").PgColumn<{
|
|
140
|
+
name: "clock";
|
|
141
|
+
tableName: "docsync-operations";
|
|
142
|
+
dataType: "date";
|
|
143
|
+
columnType: "PgTimestamp";
|
|
144
|
+
data: Date;
|
|
145
|
+
driverParam: string;
|
|
146
|
+
notNull: true;
|
|
147
|
+
hasDefault: true;
|
|
148
|
+
isPrimaryKey: false;
|
|
149
|
+
isAutoincrement: false;
|
|
150
|
+
hasRuntimeDefault: false;
|
|
151
|
+
enumValues: undefined;
|
|
152
|
+
baseColumn: never;
|
|
153
|
+
identity: undefined;
|
|
154
|
+
generated: undefined;
|
|
155
|
+
}, {}, {}>;
|
|
156
|
+
};
|
|
157
|
+
dialect: "pg";
|
|
158
|
+
}>;
|
|
159
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../../src/server/providers/postgres/schema.ts"],"names":[],"mappings":"AAOA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,eAAO,MAAM,WAAW,kBAAwC,CAAC;AAEjE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMpB,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUtB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsonb, pgTable, primaryKey, timestamp, varchar, } from "drizzle-orm/pg-core";
|
|
2
|
+
import postgres from "postgres";
|
|
3
|
+
export const queryClient = postgres(process.env.DOCNODE_DB_URL);
|
|
4
|
+
export const documents = pgTable("docsync-documents", {
|
|
5
|
+
userId: varchar("userId", { length: 26 }).notNull(),
|
|
6
|
+
docId: varchar("docId", { length: 26 }).notNull().primaryKey(),
|
|
7
|
+
doc: jsonb("doc").notNull(),
|
|
8
|
+
clock: timestamp("clock", { precision: 3, withTimezone: true }).notNull(),
|
|
9
|
+
permissions: jsonb("permissions"), // ??
|
|
10
|
+
});
|
|
11
|
+
export const operations = pgTable("docsync-operations", {
|
|
12
|
+
docId: varchar("docId", { length: 26 }).notNull(),
|
|
13
|
+
operations: jsonb("operations").notNull(),
|
|
14
|
+
clock: timestamp("clock", { precision: 3, withTimezone: true })
|
|
15
|
+
.notNull()
|
|
16
|
+
.defaultNow(),
|
|
17
|
+
}, (table) => [primaryKey({ columns: [table.docId, table.clock] })]);
|
|
18
|
+
// TODO: decide how to implement version history
|
|
19
|
+
// const snapshots_deltas = pgTable(
|
|
20
|
+
// "snapshots_deltas",
|
|
21
|
+
// {
|
|
22
|
+
// docId: varchar("docId", { length: 26 }).notNull(),
|
|
23
|
+
// docnodeId: varchar("docnodeId", { length: 26 }).notNull(),
|
|
24
|
+
// data: jsonb("data"),
|
|
25
|
+
// // Cada vez que un nodo es modificado, se guarda en la tabla snapshots-deltas con un snapshotTimestamp vacío.
|
|
26
|
+
// // Cuando el snapshot "cierra", a todos los nodos del documento que no tienen un snapshotTimestamp, se les asigna el timestamp del snapshot.
|
|
27
|
+
// // Suena como una feature que podría implementar una BD. Básicamente si quiero volver a un estado X, puedo pedirle al proveedor de la BD que
|
|
28
|
+
// // me de un backup de la tabla (aunque eso me restauraría todos los documentos, no solo uno). Además, hay que ver si el proveedor de la BD lo
|
|
29
|
+
// // provee a nivel de tabla (duplica) o a nivel de fila (duplica solo los nodos que cambiaron).
|
|
30
|
+
// snapshot_timestamp: timestamp("snapshot_timestamp").defaultNow(),
|
|
31
|
+
// },
|
|
32
|
+
// (table) => [primaryKey({ columns: [table.docId, table.docnodeId] })],
|
|
33
|
+
// );
|
|
34
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../../../src/server/providers/postgres/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,OAAO,EACP,UAAU,EACV,SAAS,EACT,OAAO,GACR,MAAM,qBAAqB,CAAC;AAC7B,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAe,CAAC,CAAC;AAEjE,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,mBAAmB,EAAE;IACpD,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE;IACnD,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE;IAC9D,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE;IAC3B,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IACzE,WAAW,EAAE,KAAK,CAAC,aAAa,CAAC,EAAE,KAAK;CACzC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAC/B,oBAAoB,EACpB;IACE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE;IACjD,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IACzC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;SAC5D,OAAO,EAAE;SACT,UAAU,EAAE;CAChB,EACD,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CACjE,CAAC;AAEF,gDAAgD;AAChD,oCAAoC;AACpC,wBAAwB;AACxB,MAAM;AACN,yDAAyD;AACzD,iEAAiE;AACjE,2BAA2B;AAE3B,oHAAoH;AACpH,mJAAmJ;AACnJ,mJAAmJ;AACnJ,oJAAoJ;AACpJ,qGAAqG;AACrG,wEAAwE;AACxE,OAAO;AACP,0EAA0E;AAC1E,KAAK"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.d.ts","sourceRoot":"","sources":["../../../src/shared/debounce.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACxD,EAAE,EAAE,CAAC,EACL,IAAI,EAAE,MAAM,GACX,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAMlC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
export function debounce(fn, wait) {
|
|
4
|
+
let timeout;
|
|
5
|
+
return (...args) => {
|
|
6
|
+
clearTimeout(timeout);
|
|
7
|
+
timeout = setTimeout(() => fn(...args), wait);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=debounce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debounce.js","sourceRoot":"","sources":["../../../src/shared/debounce.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,uDAAuD;AAEvD,MAAM,UAAU,QAAQ,CACtB,EAAK,EACL,IAAY;IAEZ,IAAI,OAAsC,CAAC;IAC3C,OAAO,CAAC,GAAG,IAAmB,EAAE,EAAE;QAChC,YAAY,CAAC,OAAO,CAAC,CAAC;QACtB,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Doc, type DocConfig, type Operations } from "@docukit/docnode";
|
|
2
|
+
export interface DocBinding<D extends {} = {}, S extends {} = {}, O extends {} = {}> {
|
|
3
|
+
create(type: string, id?: string): {
|
|
4
|
+
doc: D;
|
|
5
|
+
docId: string;
|
|
6
|
+
};
|
|
7
|
+
deserialize(serializedDoc: S): D;
|
|
8
|
+
serialize(doc: D): S;
|
|
9
|
+
onChange(doc: D, cb: (ev: {
|
|
10
|
+
operations: O;
|
|
11
|
+
}) => void): void;
|
|
12
|
+
applyOperations(doc: D, operations: O): void;
|
|
13
|
+
dispose(doc: D): void;
|
|
14
|
+
}
|
|
15
|
+
export declare const createDocBinding: <D extends {}, S extends {}, O extends {} = {}>(docBinding: DocBinding<D, S, O>) => DocBinding<D, S, O>;
|
|
16
|
+
export declare const DocNodeBinding: (docConfigs: DocConfig[]) => DocBinding<Doc, import("@docukit/docnode").JsonDoc, Operations>;
|
|
17
|
+
//# sourceMappingURL=docBinding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docBinding.d.ts","sourceRoot":"","sources":["../../../src/shared/docBinding.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGxE,MAAM,WAAW,UAAU,CACzB,CAAC,SAAS,EAAE,GAAG,EAAE,EACjB,CAAC,SAAS,EAAE,GAAG,EAAE,EACjB,CAAC,SAAS,EAAE,GAAG,EAAE;IAGjB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,WAAW,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC;IACjC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE;QAAE,UAAU,EAAE,CAAC,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC5D,eAAe,CAAC,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,IAAI,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;CACvB;AAED,eAAO,MAAM,gBAAgB,GAAI,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,EAC5E,YAAY,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC9B,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAEpB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,YAAY,SAAS,EAAE,oEAmCrD,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
2
|
+
import { Doc } from "@docukit/docnode";
|
|
3
|
+
export const createDocBinding = (docBinding) => {
|
|
4
|
+
return docBinding;
|
|
5
|
+
};
|
|
6
|
+
export const DocNodeBinding = (docConfigs) => {
|
|
7
|
+
const docConfigsMap = new Map();
|
|
8
|
+
docConfigs.forEach((docConfig) => {
|
|
9
|
+
const type = docConfig.type ?? "";
|
|
10
|
+
if (docConfigsMap.has(type)) {
|
|
11
|
+
throw new Error(`Duplicate doc type: ${type}`);
|
|
12
|
+
}
|
|
13
|
+
docConfigsMap.set(type, docConfig);
|
|
14
|
+
});
|
|
15
|
+
return createDocBinding({
|
|
16
|
+
create: (type, id) => {
|
|
17
|
+
const docConfig = docConfigsMap.get(type);
|
|
18
|
+
if (!docConfig)
|
|
19
|
+
throw new Error(`Unknown type: ${type}`);
|
|
20
|
+
const doc = new Doc({ ...docConfig, id });
|
|
21
|
+
return { doc, docId: doc.root.id };
|
|
22
|
+
},
|
|
23
|
+
serialize: (doc) => doc.toJSON({ unsafe: true }),
|
|
24
|
+
deserialize: (serializedDoc) => {
|
|
25
|
+
const type = serializedDoc[1];
|
|
26
|
+
const docConfig = docConfigsMap.get(type);
|
|
27
|
+
if (!docConfig)
|
|
28
|
+
throw new Error(`Unknown type: ${type}`);
|
|
29
|
+
const doc = Doc.fromJSON(docConfig, serializedDoc);
|
|
30
|
+
doc.forceCommit();
|
|
31
|
+
return doc;
|
|
32
|
+
},
|
|
33
|
+
onChange: (doc, cb) => doc.onChange(cb),
|
|
34
|
+
applyOperations: (doc, operations) => {
|
|
35
|
+
doc.applyOperations(operations);
|
|
36
|
+
doc.forceCommit();
|
|
37
|
+
},
|
|
38
|
+
dispose: (doc) => doc.dispose(),
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=docBinding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docBinding.js","sourceRoot":"","sources":["../../../src/shared/docBinding.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,OAAO,EAAE,GAAG,EAAmC,MAAM,kBAAkB,CAAC;AAiBxE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,UAA+B,EACV,EAAE;IACvB,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAuB,EAAE,EAAE;IACxD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAqB,CAAC;IAEnD,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;QAC/B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC;QAClC,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,OAAO,gBAAgB,CAAC;QACtB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE;YACnB,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;YAC1C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACrC,CAAC;QACD,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAChD,WAAW,EAAE,CAAC,aAAa,EAAE,EAAE;YAC7B,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YACnD,GAAG,CAAC,WAAW,EAAE,CAAC;YAClB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,QAAQ,EAAE,CAAC,GAAG,EAAE,EAA4C,EAAE,EAAE,CAC9D,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClB,eAAe,EAAE,CAAC,GAAG,EAAE,UAAU,EAAE,EAAE;YACnC,GAAG,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YAChC,GAAG,CAAC,WAAW,EAAE,CAAC;QACpB,CAAC;QACD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE;KAChC,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a throttle function that batches items by key and executes a callback
|
|
3
|
+
* after a specified delay. Multiple calls with the same key within the delay
|
|
4
|
+
* period will be batched together.
|
|
5
|
+
*
|
|
6
|
+
* @param callback - Function to execute with the key and batched items
|
|
7
|
+
* @param options - Configuration options
|
|
8
|
+
* @param options.wait - Delay in milliseconds before executing the callback
|
|
9
|
+
* @returns A function that accepts a key and item to be throttled
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const throttledSave = createThrottleByKey(
|
|
14
|
+
* async (userId, operations) => {
|
|
15
|
+
* await saveToDatabase(userId, operations);
|
|
16
|
+
* },
|
|
17
|
+
* { wait: 50 }
|
|
18
|
+
* );
|
|
19
|
+
*
|
|
20
|
+
* // These will be batched together
|
|
21
|
+
* throttledSave('user1', op1);
|
|
22
|
+
* throttledSave('user1', op2);
|
|
23
|
+
* throttledSave('user1', op3);
|
|
24
|
+
* // After 50ms, callback is called once with [op1, op2, op3]
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function createThrottleByKey<K, T>(callback: (key: K, items: T[]) => void | Promise<void>, options: {
|
|
28
|
+
wait: number;
|
|
29
|
+
}): (key: K, item: T) => void;
|
|
30
|
+
//# sourceMappingURL=throttle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.d.ts","sourceRoot":"","sources":["../../../src/shared/throttle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EACtC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACtD,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,IAKjB,KAAK,CAAC,EAAE,MAAM,CAAC,UAwBxB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a throttle function that batches items by key and executes a callback
|
|
3
|
+
* after a specified delay. Multiple calls with the same key within the delay
|
|
4
|
+
* period will be batched together.
|
|
5
|
+
*
|
|
6
|
+
* @param callback - Function to execute with the key and batched items
|
|
7
|
+
* @param options - Configuration options
|
|
8
|
+
* @param options.wait - Delay in milliseconds before executing the callback
|
|
9
|
+
* @returns A function that accepts a key and item to be throttled
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const throttledSave = createThrottleByKey(
|
|
14
|
+
* async (userId, operations) => {
|
|
15
|
+
* await saveToDatabase(userId, operations);
|
|
16
|
+
* },
|
|
17
|
+
* { wait: 50 }
|
|
18
|
+
* );
|
|
19
|
+
*
|
|
20
|
+
* // These will be batched together
|
|
21
|
+
* throttledSave('user1', op1);
|
|
22
|
+
* throttledSave('user1', op2);
|
|
23
|
+
* throttledSave('user1', op3);
|
|
24
|
+
* // After 50ms, callback is called once with [op1, op2, op3]
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function createThrottleByKey(callback, options) {
|
|
28
|
+
const queues = new Map();
|
|
29
|
+
const timeouts = new Map();
|
|
30
|
+
return (key, item) => {
|
|
31
|
+
// Add item to queue
|
|
32
|
+
const queue = queues.get(key) ?? [];
|
|
33
|
+
queue.push(item);
|
|
34
|
+
queues.set(key, queue);
|
|
35
|
+
// If there's already a pending timeout for this key, just wait
|
|
36
|
+
if (timeouts.has(key)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Schedule the callback
|
|
40
|
+
const timeout = setTimeout(() => {
|
|
41
|
+
timeouts.delete(key);
|
|
42
|
+
const items = queues.get(key);
|
|
43
|
+
queues.delete(key);
|
|
44
|
+
if (items && items.length > 0) {
|
|
45
|
+
void callback(key, items);
|
|
46
|
+
}
|
|
47
|
+
}, options.wait);
|
|
48
|
+
timeouts.set(key, timeout);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=throttle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"throttle.js","sourceRoot":"","sources":["../../../src/shared/throttle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAsD,EACtD,OAAyB;IAEzB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoC,CAAC;IAE7D,OAAO,CAAC,GAAM,EAAE,IAAO,EAAE,EAAE;QACzB,oBAAoB;QACpB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEvB,+DAA+D;QAC/D,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,wBAAwB;QACxB,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACrB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAEnB,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,KAAK,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAEjB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7B,CAAC,CAAC;AACJ,CAAC"}
|