@kyro-cms/admin 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/README.md +149 -51
  2. package/package.json +54 -5
  3. package/src/collections/auth/index.ts +2 -2
  4. package/src/collections/portfolio/index.ts +343 -0
  5. package/src/components/ActionBar.tsx +153 -16
  6. package/src/components/Admin.tsx +137 -28
  7. package/src/components/ApiExplorer.tsx +325 -0
  8. package/src/components/ApiKeysManager.tsx +563 -0
  9. package/src/components/AuditLogsPage.tsx +664 -0
  10. package/src/components/AutoForm.tsx +2155 -770
  11. package/src/components/BrandingHub.tsx +267 -0
  12. package/src/components/BulkActionsBar.tsx +3 -3
  13. package/src/components/CreateView.tsx +4 -4
  14. package/src/components/Dashboard.tsx +393 -0
  15. package/src/components/DetailView.tsx +200 -58
  16. package/src/components/DeveloperCenter.tsx +403 -0
  17. package/src/components/EnhancedListView.tsx +890 -0
  18. package/src/components/GraphQLExplorer.tsx +675 -0
  19. package/src/components/GraphQLPlayground.tsx +627 -0
  20. package/src/components/ListView.tsx +192 -54
  21. package/src/components/MediaGallery.tsx +1569 -0
  22. package/src/components/Modal.tsx +206 -0
  23. package/src/components/RestPlayground.tsx +951 -0
  24. package/src/components/Sidebar.astro +237 -0
  25. package/src/components/ThemeProvider.tsx +8 -2
  26. package/src/components/UserManagement.tsx +204 -0
  27. package/src/components/VersionHistoryPanel.tsx +3 -3
  28. package/src/components/WebhookManager.tsx +608 -0
  29. package/src/components/blocks/AccordionBlock.tsx +65 -0
  30. package/src/components/blocks/ArrayBlock.tsx +84 -0
  31. package/src/components/blocks/BlockEditModal.tsx +363 -0
  32. package/src/components/blocks/ButtonBlock.tsx +64 -0
  33. package/src/components/blocks/ChildBlocksTree.tsx +551 -0
  34. package/src/components/blocks/CodeBlock.tsx +114 -0
  35. package/src/components/blocks/ColumnsBlock.tsx +93 -0
  36. package/src/components/blocks/DividerBlock.tsx +43 -0
  37. package/src/components/blocks/FileBlock.tsx +63 -0
  38. package/src/components/blocks/HeadingBlock.tsx +59 -0
  39. package/src/components/blocks/HeroBlock.tsx +99 -0
  40. package/src/components/blocks/ImageBlock.tsx +82 -0
  41. package/src/components/blocks/LinkBlock.tsx +65 -0
  42. package/src/components/blocks/ListBlock.tsx +60 -0
  43. package/src/components/blocks/ParagraphBlock.tsx +61 -0
  44. package/src/components/blocks/RelationshipBlock.tsx +72 -0
  45. package/src/components/blocks/RichTextBlock.tsx +66 -0
  46. package/src/components/blocks/VStackBlock.tsx +61 -0
  47. package/src/components/blocks/VideoBlock.tsx +65 -0
  48. package/src/components/blocks/index.ts +10 -0
  49. package/src/components/fields/AccordionField.tsx +213 -0
  50. package/src/components/fields/ArrayField.tsx +241 -0
  51. package/src/components/fields/BlocksField.tsx +323 -0
  52. package/src/components/fields/ButtonField.tsx +53 -0
  53. package/src/components/fields/CheckboxField.tsx +18 -8
  54. package/src/components/fields/ChildrenField.tsx +48 -0
  55. package/src/components/fields/CodeField.tsx +294 -0
  56. package/src/components/fields/ColumnsField.tsx +137 -0
  57. package/src/components/fields/DateField.tsx +24 -12
  58. package/src/components/fields/EditorClient.tsx +537 -0
  59. package/src/components/fields/HeadingField.tsx +31 -0
  60. package/src/components/fields/HeroField.tsx +101 -0
  61. package/src/components/fields/JSONField.tsx +341 -0
  62. package/src/components/fields/LinkField.tsx +81 -0
  63. package/src/components/fields/ListField.tsx +74 -0
  64. package/src/components/fields/MarkdownField.tsx +260 -0
  65. package/src/components/fields/NumberField.tsx +25 -13
  66. package/src/components/fields/PortableTextField.tsx +155 -0
  67. package/src/components/fields/PortableTextRenderer.tsx +68 -0
  68. package/src/components/fields/RelationshipBlockField.tsx +233 -0
  69. package/src/components/fields/RelationshipField.tsx +278 -60
  70. package/src/components/fields/SelectField.tsx +28 -16
  71. package/src/components/fields/TextField.tsx +31 -15
  72. package/src/components/fields/UploadField.tsx +613 -0
  73. package/src/components/fields/VideoField.tsx +73 -0
  74. package/src/components/fields/extensions/blockComponents.tsx +247 -0
  75. package/src/components/fields/extensions/blocksStore.ts +273 -0
  76. package/src/components/fields/index.ts +24 -0
  77. package/src/components/index.ts +1 -2
  78. package/src/components/layout/Header.tsx +2 -2
  79. package/src/components/layout/Layout.tsx +3 -3
  80. package/src/components/ui/Badge.tsx +9 -4
  81. package/src/components/ui/BlockDrawer.tsx +79 -0
  82. package/src/components/ui/Button.tsx +1 -1
  83. package/src/components/ui/CommandPalette.tsx +362 -0
  84. package/src/components/ui/CommandPaletteWrapper.tsx +97 -0
  85. package/src/components/ui/Dropdown.tsx +1 -1
  86. package/src/components/ui/Modal.tsx +37 -12
  87. package/src/components/ui/PromptModal.tsx +94 -0
  88. package/src/components/ui/SlidePanel.tsx +43 -16
  89. package/src/components/ui/Toast.tsx +80 -14
  90. package/src/env.d.ts +16 -0
  91. package/src/env.ts +20 -0
  92. package/src/index.ts +0 -1
  93. package/src/layouts/AdminLayout.astro +164 -170
  94. package/src/layouts/AuthLayout.astro +23 -6
  95. package/src/lib/MediaService.ts +541 -0
  96. package/src/lib/api.ts +163 -0
  97. package/src/lib/auth/sqlite-adapter.ts +319 -0
  98. package/src/lib/config.ts +23 -7
  99. package/src/lib/dataStore.ts +188 -73
  100. package/src/lib/date-utils.ts +69 -0
  101. package/src/lib/db/adapter.ts +54 -0
  102. package/src/lib/db/drizzle-mysql-adapter.ts +194 -0
  103. package/src/lib/db/drizzle-mysql-auth-adapter.ts +327 -0
  104. package/src/lib/db/drizzle-postgres-adapter.ts +202 -0
  105. package/src/lib/db/drizzle-postgres-auth-adapter.ts +304 -0
  106. package/src/lib/db/drizzle-sqlite-adapter.ts +227 -0
  107. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +548 -0
  108. package/src/lib/db/index.ts +449 -0
  109. package/src/lib/db/mongodb-adapter.ts +207 -0
  110. package/src/lib/db/mongodb-auth-adapter.ts +305 -0
  111. package/src/lib/db/schema/mysql-auth.ts +113 -0
  112. package/src/lib/db/schema/mysql-content.ts +20 -0
  113. package/src/lib/db/schema/postgres-auth.ts +116 -0
  114. package/src/lib/db/schema/postgres-content.ts +35 -0
  115. package/src/lib/db/schema/postgres-media.ts +52 -0
  116. package/src/lib/db/schema/postgres-settings.ts +11 -0
  117. package/src/lib/db/schema/sqlite-auth.ts +112 -0
  118. package/src/lib/db/schema/sqlite-content.ts +20 -0
  119. package/src/lib/db/version-adapter.ts +248 -0
  120. package/src/lib/graphql/index.ts +1 -0
  121. package/src/lib/graphql/schema.ts +443 -0
  122. package/src/lib/i18n.tsx +353 -0
  123. package/src/lib/rate-limit.ts +267 -0
  124. package/src/lib/slugify.ts +15 -0
  125. package/src/lib/storage.ts +374 -0
  126. package/src/lib/store.ts +85 -0
  127. package/src/lib/validation.ts +250 -0
  128. package/src/middleware.ts +70 -11
  129. package/src/pages/[collection]/[id].astro +178 -122
  130. package/src/pages/[collection]/index.astro +24 -156
  131. package/src/pages/admin/api-explorer.astro +98 -0
  132. package/src/pages/admin/graphql-explorer.astro +40 -0
  133. package/src/pages/admin/graphql.astro +97 -0
  134. package/src/pages/admin/index.astro +200 -139
  135. package/src/pages/admin/keys.astro +8 -0
  136. package/src/pages/admin/rest-playground.astro +44 -0
  137. package/src/pages/admin/webhooks.astro +8 -0
  138. package/src/pages/api/[collection]/[id]/publish.ts +52 -0
  139. package/src/pages/api/[collection]/[id]/unpublish.ts +42 -0
  140. package/src/pages/api/[collection]/[id]/versions.ts +66 -0
  141. package/src/pages/api/[collection]/[id].ts +114 -159
  142. package/src/pages/api/[collection]/index.ts +150 -230
  143. package/src/pages/api/auth/[id].ts +48 -69
  144. package/src/pages/api/auth/audit-logs.ts +20 -43
  145. package/src/pages/api/auth/login.ts +159 -45
  146. package/src/pages/api/auth/logout.ts +42 -24
  147. package/src/pages/api/auth/refresh.ts +119 -0
  148. package/src/pages/api/auth/register.ts +110 -40
  149. package/src/pages/api/auth/users.ts +22 -97
  150. package/src/pages/api/collections.ts +59 -0
  151. package/src/pages/api/globals/[slug]/test.ts +172 -0
  152. package/src/pages/api/globals/[slug].ts +42 -0
  153. package/src/pages/api/graphql.ts +90 -0
  154. package/src/pages/api/health.ts +417 -40
  155. package/src/pages/api/keys/[id].ts +26 -0
  156. package/src/pages/api/keys/index.ts +75 -0
  157. package/src/pages/api/media/[id].ts +309 -0
  158. package/src/pages/api/media/folders.ts +609 -0
  159. package/src/pages/api/media/index.ts +146 -0
  160. package/src/pages/api/media/resize.ts +267 -0
  161. package/src/pages/api/search.ts +82 -0
  162. package/src/pages/api/slug-availability.ts +70 -0
  163. package/src/pages/api/storage-config.ts +20 -0
  164. package/src/pages/api/storage-status.ts +206 -0
  165. package/src/pages/api/upload.ts +334 -0
  166. package/src/pages/api/webhooks/index.ts +71 -0
  167. package/src/pages/audit/index.astro +2 -104
  168. package/src/pages/login.astro +11 -11
  169. package/src/pages/media.astro +10 -0
  170. package/src/pages/preview/[collection]/[id].astro +178 -0
  171. package/src/pages/register.astro +13 -13
  172. package/src/pages/roles/index.astro +21 -21
  173. package/src/pages/settings/[slug].astro +162 -0
  174. package/src/pages/settings/index.astro +9 -0
  175. package/src/pages/users/[id].astro +29 -21
  176. package/src/pages/users/index.astro +22 -17
  177. package/src/pages/users/new.astro +18 -17
  178. package/src/styles/main.css +563 -128
  179. package/src/components/layout/Sidebar.tsx +0 -497
@@ -0,0 +1,112 @@
1
+ import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
2
+
3
+ export const users = sqliteTable("users", {
4
+ id: text("id").primaryKey(),
5
+ email: text("email").notNull().unique(),
6
+ passwordHash: text("password_hash").notNull(),
7
+ name: text("name"),
8
+ role: text("role").notNull().default("customer"),
9
+ tenantId: text("tenant_id"),
10
+ emailVerified: integer("email_verified", { mode: "boolean" }).default(false),
11
+ locked: integer("locked", { mode: "boolean" }).default(false),
12
+ lastLogin: text("last_login"),
13
+ failedLoginAttempts: integer("failed_login_attempts").default(0),
14
+ lockedUntil: text("locked_until"),
15
+ createdAt: text("created_at").notNull(),
16
+ updatedAt: text("updated_at").notNull(),
17
+ });
18
+
19
+ export const sessions = sqliteTable(
20
+ "sessions",
21
+ {
22
+ id: text("id").primaryKey(),
23
+ token: text("token").notNull().unique(),
24
+ refreshToken: text("refresh_token"),
25
+ userId: text("user_id")
26
+ .notNull()
27
+ .references(() => users.id, { onDelete: "cascade" }),
28
+ expiresAt: text("expires_at").notNull(),
29
+ createdAt: text("created_at").notNull(),
30
+ },
31
+ (table) => {
32
+ return {
33
+ userIdIdx: index("idx_sessions_user_id").on(table.userId),
34
+ tokenIdx: index("idx_sessions_token").on(table.token),
35
+ expiresIdx: index("idx_sessions_expires").on(table.expiresAt),
36
+ };
37
+ },
38
+ );
39
+
40
+ export const roles = sqliteTable("roles", {
41
+ id: text("id").primaryKey(),
42
+ name: text("name").notNull().unique(),
43
+ level: integer("level").notNull().default(0),
44
+ inherits: text("inherits").$type<string[]>(),
45
+ permissions: text("permissions").$type<string[]>(),
46
+ isSystem: integer("is_system", { mode: "boolean" }).default(false),
47
+ description: text("description"),
48
+ createdAt: text("created_at").notNull(),
49
+ });
50
+
51
+ export const auditLogs = sqliteTable(
52
+ "audit_logs",
53
+ {
54
+ id: text("id").primaryKey(),
55
+ action: text("action").notNull(),
56
+ userId: text("user_id").references(() => users.id, {
57
+ onDelete: "set null",
58
+ }),
59
+ userEmail: text("user_email"),
60
+ role: text("role"),
61
+ resource: text("resource").notNull(),
62
+ ipAddress: text("ip_address"),
63
+ userAgent: text("user_agent"),
64
+ success: integer("success", { mode: "boolean" }).notNull(),
65
+ error: text("error"),
66
+ metadata: text("metadata").$type<Record<string, unknown>>(),
67
+ createdAt: text("created_at").notNull(),
68
+ },
69
+ (table) => {
70
+ return {
71
+ userIdIdx: index("idx_audit_user_id").on(table.userId),
72
+ actionIdx: index("idx_audit_action").on(table.action),
73
+ createdAtIdx: index("idx_audit_created_at").on(table.createdAt),
74
+ };
75
+ },
76
+ );
77
+
78
+ export const passwordHistory = sqliteTable(
79
+ "password_history",
80
+ {
81
+ id: text("id").primaryKey(),
82
+ userId: text("user_id")
83
+ .notNull()
84
+ .references(() => users.id, { onDelete: "cascade" }),
85
+ passwordHash: text("password_hash").notNull(),
86
+ createdAt: text("created_at").notNull(),
87
+ },
88
+ (table) => {
89
+ return {
90
+ userIdIdx: index("idx_password_history_user_id").on(table.userId),
91
+ };
92
+ },
93
+ );
94
+
95
+ export const lockouts = sqliteTable("lockouts", {
96
+ id: text("id").primaryKey(),
97
+ userId: text("user_id")
98
+ .notNull()
99
+ .references(() => users.id, { onDelete: "cascade" }),
100
+ ipAddress: text("ip_address"),
101
+ lockedUntil: text("locked_until").notNull(),
102
+ createdAt: text("created_at").notNull(),
103
+ });
104
+
105
+ export type User = typeof users.$inferSelect;
106
+ export type NewUser = typeof users.$inferInsert;
107
+ export type Session = typeof sessions.$inferSelect;
108
+ export type NewSession = typeof sessions.$inferInsert;
109
+ export type Role = typeof roles.$inferSelect;
110
+ export type NewRole = typeof roles.$inferInsert;
111
+ export type AuditLog = typeof auditLogs.$inferSelect;
112
+ export type NewAuditLog = typeof auditLogs.$inferInsert;
@@ -0,0 +1,20 @@
1
+ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
2
+
3
+ export const documents = sqliteTable("documents", {
4
+ id: text("id").primaryKey(),
5
+ collection: text("collection").notNull(),
6
+ data: text("data").notNull().$type<Record<string, unknown>>(),
7
+ createdAt: text("created_at").notNull(),
8
+ updatedAt: text("updated_at").notNull(),
9
+ });
10
+
11
+ export const globals = sqliteTable("globals", {
12
+ slug: text("slug").primaryKey(),
13
+ data: text("data").notNull().$type<Record<string, unknown>>(),
14
+ updatedAt: text("updated_at").notNull(),
15
+ });
16
+
17
+ export type Document = typeof documents.$inferSelect;
18
+ export type NewDocument = typeof documents.$inferInsert;
19
+ export type Global = typeof globals.$inferSelect;
20
+ export type NewGlobal = typeof globals.$inferInsert;
@@ -0,0 +1,248 @@
1
+ import { randomUUID } from "crypto";
2
+ import { diff } from "jsondiffpatch";
3
+ import type {
4
+ Version,
5
+ VersionStatus,
6
+ VersionDiff,
7
+ CreateVersionOptions,
8
+ PublishVersionOptions,
9
+ CompareVersionsOptions,
10
+ VersionHistoryOptions,
11
+ VersionAdapter,
12
+ } from "@kyro-cms/core";
13
+ import type { DatabaseAdapter } from "./adapter.js";
14
+
15
+ const VERSIONS_COLLECTION = "_versions";
16
+
17
+ export class DataStoreVersionAdapter implements VersionAdapter {
18
+ constructor(private db: DatabaseAdapter) {}
19
+
20
+ async createVersion<T>(
21
+ options: CreateVersionOptions<T>,
22
+ ): Promise<Version<T>> {
23
+ const latest = await this.getLatestVersion(
24
+ options.collection,
25
+ options.documentId,
26
+ );
27
+ const nextVersion = (latest?.version ?? 0) + 1;
28
+
29
+ const versionDoc: Version<T> = {
30
+ id: randomUUID(),
31
+ collection: options.collection,
32
+ documentId: options.documentId,
33
+ version: nextVersion,
34
+ status: options.status || "draft",
35
+ data: options.data,
36
+ createdBy: options.createdBy,
37
+ createdAt: new Date(),
38
+ changeDescription: options.changeDescription,
39
+ };
40
+
41
+ await this.db.create(VERSIONS_COLLECTION, versionDoc);
42
+ return versionDoc;
43
+ }
44
+
45
+ async getVersion<T>(
46
+ collection: string,
47
+ versionId: string,
48
+ ): Promise<Version<T> | null> {
49
+ const version = await this.db.findById<Version<T>>(
50
+ VERSIONS_COLLECTION,
51
+ versionId,
52
+ );
53
+ if (!version || version.collection !== collection) return null;
54
+ return version;
55
+ }
56
+
57
+ async getVersions<T>(options: VersionHistoryOptions): Promise<Version<T>[]> {
58
+ const result = await this.db.find<Version<T>>(VERSIONS_COLLECTION, {
59
+ limit: 100,
60
+ });
61
+ const filtered = result.docs
62
+ .filter(
63
+ (v: Version<T>) =>
64
+ v.collection === options.collection &&
65
+ v.documentId === options.documentId,
66
+ )
67
+ .sort((a: Version<T>, b: Version<T>) => b.version - a.version)
68
+ .slice(
69
+ options.offset ?? 0,
70
+ (options.offset ?? 0) + (options.limit ?? 20),
71
+ );
72
+ return filtered;
73
+ }
74
+
75
+ async getLatestVersion<T>(
76
+ collection: string,
77
+ documentId: string,
78
+ ): Promise<Version<T> | null> {
79
+ const versions = await this.getVersions<T>({
80
+ collection,
81
+ documentId,
82
+ limit: 1,
83
+ });
84
+ return versions.length > 0 ? versions[0] : null;
85
+ }
86
+
87
+ async getPublishedVersion<T>(
88
+ collection: string,
89
+ documentId: string,
90
+ ): Promise<Version<T> | null> {
91
+ const versions = await this.getVersions<T>({
92
+ collection,
93
+ documentId,
94
+ limit: 100,
95
+ });
96
+ return versions.find((v: Version<T>) => v.status === "published") || null;
97
+ }
98
+
99
+ async publishVersion(options: PublishVersionOptions): Promise<void> {
100
+ const versions = await this.getVersions({
101
+ collection: options.collection,
102
+ documentId: options.documentId,
103
+ limit: 100,
104
+ });
105
+ for (const v of versions) {
106
+ if (v.status === "published") {
107
+ const updated = { ...v, status: "draft" as VersionStatus };
108
+ await this.db.update(VERSIONS_COLLECTION, v.id, updated);
109
+ }
110
+ }
111
+
112
+ const target = versions.find(
113
+ (v: Version<unknown>) => v.id === options.versionId,
114
+ );
115
+ if (target) {
116
+ const updated = {
117
+ ...target,
118
+ status: "published" as VersionStatus,
119
+ publishedAt: new Date(),
120
+ };
121
+ await this.db.update(VERSIONS_COLLECTION, target.id, updated);
122
+ }
123
+ }
124
+
125
+ async revertToVersion<T>(options: {
126
+ collection: string;
127
+ documentId: string;
128
+ versionId: string;
129
+ userId: string;
130
+ }): Promise<Version<T>> {
131
+ const target = await this.getVersion<T>(
132
+ options.collection,
133
+ options.versionId,
134
+ );
135
+ if (!target) {
136
+ throw new Error("Version not found");
137
+ }
138
+
139
+ return this.createVersion<T>({
140
+ collection: options.collection,
141
+ documentId: options.documentId,
142
+ data: target.data,
143
+ status: "draft",
144
+ createdBy: options.userId,
145
+ changeDescription: `Reverted to version ${target.version}`,
146
+ });
147
+ }
148
+
149
+ async compareVersions<T>(
150
+ options: CompareVersionsOptions,
151
+ ): Promise<VersionDiff[]> {
152
+ const versions = await this.getVersions<T>({
153
+ collection: options.collection,
154
+ documentId: options.documentId,
155
+ limit: 100,
156
+ });
157
+
158
+ const findVersion = (ref: string | number): Version<T> | undefined => {
159
+ if (typeof ref === "number") {
160
+ return versions.find((v) => v.version === ref);
161
+ }
162
+ return versions.find((v) => v.id === ref);
163
+ };
164
+
165
+ const versionA = findVersion(options.versionA);
166
+ const versionB = findVersion(options.versionB);
167
+
168
+ if (!versionA || !versionB) {
169
+ throw new Error("One or both versions not found");
170
+ }
171
+
172
+ const delta = diff(
173
+ versionA.data as Record<string, unknown>,
174
+ versionB.data as Record<string, unknown>,
175
+ );
176
+ const diffs: VersionDiff[] = [];
177
+
178
+ if (!delta) return diffs;
179
+
180
+ const walkDelta = (obj: any, path: string = "") => {
181
+ if (
182
+ Array.isArray(obj) &&
183
+ obj[0] !== undefined &&
184
+ obj[1] !== undefined &&
185
+ obj.length === 2 &&
186
+ !Array.isArray(obj[0])
187
+ ) {
188
+ diffs.push({
189
+ field: path || "root",
190
+ oldValue: obj[0],
191
+ newValue: obj[1],
192
+ });
193
+ return;
194
+ }
195
+ if (obj && typeof obj === "object" && obj._t === "a") {
196
+ for (const [key, value] of Object.entries(obj)) {
197
+ if (key === "_t") continue;
198
+ const arrayPath = `${path}[${key.replace(/^_/, "")}]`;
199
+ if (Array.isArray(value) && value.length >= 2) {
200
+ diffs.push({
201
+ field: arrayPath,
202
+ oldValue: value[0],
203
+ newValue: value[1],
204
+ });
205
+ } else {
206
+ walkDelta(value, arrayPath);
207
+ }
208
+ }
209
+ return;
210
+ }
211
+ for (const [key, value] of Object.entries(obj)) {
212
+ const fullPath = path ? `${path}.${key}` : key;
213
+ if (
214
+ Array.isArray(value) &&
215
+ value.length === 2 &&
216
+ !Array.isArray(value[0]) &&
217
+ !Array.isArray(value[1])
218
+ ) {
219
+ diffs.push({
220
+ field: fullPath,
221
+ oldValue: value[0],
222
+ newValue: value[1],
223
+ });
224
+ } else if (
225
+ value &&
226
+ typeof value === "object" &&
227
+ !Array.isArray(value)
228
+ ) {
229
+ walkDelta(value, fullPath);
230
+ }
231
+ }
232
+ };
233
+
234
+ walkDelta(delta);
235
+ return diffs;
236
+ }
237
+
238
+ async deleteVersions(collection: string, documentId: string): Promise<void> {
239
+ const versions = await this.getVersions({
240
+ collection,
241
+ documentId,
242
+ limit: 100,
243
+ });
244
+ for (const v of versions) {
245
+ await this.db.delete(VERSIONS_COLLECTION, v.id);
246
+ }
247
+ }
248
+ }
@@ -0,0 +1 @@
1
+ export { executeGraphQL, schemaString } from "./schema";