@apiagex/database 0.6.2

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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/admin-permission-repository.d.ts +7 -0
  4. package/dist/admin-permission-repository.d.ts.map +1 -0
  5. package/dist/admin-permission-repository.js +74 -0
  6. package/dist/admin-permission-repository.type.d.ts +10 -0
  7. package/dist/admin-permission-repository.type.d.ts.map +1 -0
  8. package/dist/admin-permission-repository.type.js +1 -0
  9. package/dist/api-token-repository.d.ts +7 -0
  10. package/dist/api-token-repository.d.ts.map +1 -0
  11. package/dist/api-token-repository.js +80 -0
  12. package/dist/api-token-repository.type.d.ts +19 -0
  13. package/dist/api-token-repository.type.d.ts.map +1 -0
  14. package/dist/api-token-repository.type.js +1 -0
  15. package/dist/entry-query.d.ts +4 -0
  16. package/dist/entry-query.d.ts.map +1 -0
  17. package/dist/entry-query.js +61 -0
  18. package/dist/entry-repository.d.ts +8 -0
  19. package/dist/entry-repository.d.ts.map +1 -0
  20. package/dist/entry-repository.js +181 -0
  21. package/dist/entry-repository.type.d.ts +32 -0
  22. package/dist/entry-repository.type.d.ts.map +1 -0
  23. package/dist/entry-repository.type.js +1 -0
  24. package/dist/index.d.ts +29 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +15 -0
  27. package/dist/migrations-additive.d.ts +2 -0
  28. package/dist/migrations-additive.d.ts.map +1 -0
  29. package/dist/migrations-additive.js +71 -0
  30. package/dist/migrations.d.ts +5 -0
  31. package/dist/migrations.d.ts.map +1 -0
  32. package/dist/migrations.js +175 -0
  33. package/dist/permission-repository.d.ts +6 -0
  34. package/dist/permission-repository.d.ts.map +1 -0
  35. package/dist/permission-repository.js +80 -0
  36. package/dist/permission-repository.type.d.ts +11 -0
  37. package/dist/permission-repository.type.d.ts.map +1 -0
  38. package/dist/permission-repository.type.js +1 -0
  39. package/dist/realtime-repository.d.ts +12 -0
  40. package/dist/realtime-repository.d.ts.map +1 -0
  41. package/dist/realtime-repository.js +98 -0
  42. package/dist/realtime-repository.type.d.ts +33 -0
  43. package/dist/realtime-repository.type.d.ts.map +1 -0
  44. package/dist/realtime-repository.type.js +1 -0
  45. package/dist/realtime-session-repository.d.ts +5 -0
  46. package/dist/realtime-session-repository.d.ts.map +1 -0
  47. package/dist/realtime-session-repository.js +38 -0
  48. package/dist/realtime-session-repository.type.d.ts +21 -0
  49. package/dist/realtime-session-repository.type.d.ts.map +1 -0
  50. package/dist/realtime-session-repository.type.js +1 -0
  51. package/dist/relation-errors.d.ts +20 -0
  52. package/dist/relation-errors.d.ts.map +1 -0
  53. package/dist/relation-errors.js +30 -0
  54. package/dist/relation-helpers.d.ts +14 -0
  55. package/dist/relation-helpers.d.ts.map +1 -0
  56. package/dist/relation-helpers.js +21 -0
  57. package/dist/role-repository.d.ts +8 -0
  58. package/dist/role-repository.d.ts.map +1 -0
  59. package/dist/role-repository.js +66 -0
  60. package/dist/role-repository.type.d.ts +15 -0
  61. package/dist/role-repository.type.d.ts.map +1 -0
  62. package/dist/role-repository.type.js +1 -0
  63. package/dist/schema-repository.d.ts +9 -0
  64. package/dist/schema-repository.d.ts.map +1 -0
  65. package/dist/schema-repository.js +155 -0
  66. package/dist/schema-repository.type.d.ts +45 -0
  67. package/dist/schema-repository.type.d.ts.map +1 -0
  68. package/dist/schema-repository.type.js +1 -0
  69. package/dist/schema.type.d.ts +9 -0
  70. package/dist/schema.type.d.ts.map +1 -0
  71. package/dist/schema.type.js +1 -0
  72. package/dist/sqlite.d.ts +6 -0
  73. package/dist/sqlite.d.ts.map +1 -0
  74. package/dist/sqlite.js +40 -0
  75. package/dist/user-repository.d.ts +11 -0
  76. package/dist/user-repository.d.ts.map +1 -0
  77. package/dist/user-repository.js +56 -0
  78. package/dist/user-repository.type.d.ts +15 -0
  79. package/dist/user-repository.type.d.ts.map +1 -0
  80. package/dist/user-repository.type.js +1 -0
  81. package/dist/webhook-repository.d.ts +15 -0
  82. package/dist/webhook-repository.d.ts.map +1 -0
  83. package/dist/webhook-repository.js +136 -0
  84. package/dist/webhook-repository.type.d.ts +79 -0
  85. package/dist/webhook-repository.type.d.ts.map +1 -0
  86. package/dist/webhook-repository.type.js +1 -0
  87. package/package.json +43 -0
@@ -0,0 +1,20 @@
1
+ export declare const relationErrors: {
2
+ readonly metadataForNonRelationField: "RELATION_METADATA_FOR_NON_RELATION_FIELD";
3
+ readonly entryReferenced: "RELATION_ENTRY_REFERENCED";
4
+ readonly fieldUpdateUnsafe: "RELATION_FIELD_UPDATE_UNSAFE";
5
+ readonly oneToOneConflict: "RELATION_ONE_TO_ONE_CONFLICT";
6
+ readonly schemaReferenced: "RELATION_SCHEMA_REFERENCED";
7
+ readonly targetEntryInvalid: "RELATION_TARGET_ENTRY_INVALID";
8
+ readonly targetMissing: "RELATION_TARGET_MISSING";
9
+ readonly targetRequired: "RELATION_TARGET_REQUIRED";
10
+ readonly typeInvalid: "RELATION_TYPE_INVALID";
11
+ readonly valueShapeInvalid: "RELATION_VALUE_SHAPE_INVALID";
12
+ };
13
+ export type RelationErrorCode = (typeof relationErrors)[keyof typeof relationErrors];
14
+ export declare function relationEntryReferenced(entryId: string): string;
15
+ export declare function relationFieldUpdateUnsafe(fieldSlug: string): string;
16
+ export declare function relationSchemaReferenced(schemaId: string): string;
17
+ export declare function relationTargetEntryInvalid(fieldSlug: string): string;
18
+ export declare function relationOneToOneConflict(fieldSlug: string): string;
19
+ export declare function relationValueShapeInvalid(fieldSlug: string): string;
20
+ //# sourceMappingURL=relation-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-errors.d.ts","sourceRoot":"","sources":["../src/relation-errors.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;;;;;;;;;;;CAWjB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAErF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,30 @@
1
+ export const relationErrors = {
2
+ metadataForNonRelationField: "RELATION_METADATA_FOR_NON_RELATION_FIELD",
3
+ entryReferenced: "RELATION_ENTRY_REFERENCED",
4
+ fieldUpdateUnsafe: "RELATION_FIELD_UPDATE_UNSAFE",
5
+ oneToOneConflict: "RELATION_ONE_TO_ONE_CONFLICT",
6
+ schemaReferenced: "RELATION_SCHEMA_REFERENCED",
7
+ targetEntryInvalid: "RELATION_TARGET_ENTRY_INVALID",
8
+ targetMissing: "RELATION_TARGET_MISSING",
9
+ targetRequired: "RELATION_TARGET_REQUIRED",
10
+ typeInvalid: "RELATION_TYPE_INVALID",
11
+ valueShapeInvalid: "RELATION_VALUE_SHAPE_INVALID",
12
+ };
13
+ export function relationEntryReferenced(entryId) {
14
+ return `${relationErrors.entryReferenced}:${entryId}`;
15
+ }
16
+ export function relationFieldUpdateUnsafe(fieldSlug) {
17
+ return `${relationErrors.fieldUpdateUnsafe}:${fieldSlug}`;
18
+ }
19
+ export function relationSchemaReferenced(schemaId) {
20
+ return `${relationErrors.schemaReferenced}:${schemaId}`;
21
+ }
22
+ export function relationTargetEntryInvalid(fieldSlug) {
23
+ return `${relationErrors.targetEntryInvalid}:${fieldSlug}`;
24
+ }
25
+ export function relationOneToOneConflict(fieldSlug) {
26
+ return `${relationErrors.oneToOneConflict}:${fieldSlug}`;
27
+ }
28
+ export function relationValueShapeInvalid(fieldSlug) {
29
+ return `${relationErrors.valueShapeInvalid}:${fieldSlug}`;
30
+ }
@@ -0,0 +1,14 @@
1
+ import type { EntryData } from "./entry-repository.type.js";
2
+ import type { FieldRecord, RelationType } from "./schema-repository.type.js";
3
+ import type { SqliteDatabase } from "./sqlite.js";
4
+ type EntryDataRow = {
5
+ dataJson: string;
6
+ id: string;
7
+ };
8
+ export declare function relationTypeOf(field: FieldRecord): RelationType;
9
+ export declare function entryDataReferences(data: EntryData, entryId: string): boolean;
10
+ export declare function listEntryDataRows(db: SqliteDatabase, where?: string, params?: unknown[]): EntryDataRow[];
11
+ export declare function parseEntryData(dataJson: string): EntryData;
12
+ export declare function schemaEntriesUseField(db: SqliteDatabase, schemaId: string, fieldSlug: string): boolean;
13
+ export {};
14
+ //# sourceMappingURL=relation-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-helpers.d.ts","sourceRoot":"","sources":["../src/relation-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,KAAK,YAAY,GAAG;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAE/D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAI7E;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,SAAK,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,YAAY,EAAE,CAIxG;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAE1D;AAED,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,cAAc,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAMT"}
@@ -0,0 +1,21 @@
1
+ export function relationTypeOf(field) {
2
+ return field.relationType ?? "manyToOne";
3
+ }
4
+ export function entryDataReferences(data, entryId) {
5
+ return Object.values(data).some((value) => value === entryId || (Array.isArray(value) && value.includes(entryId)));
6
+ }
7
+ export function listEntryDataRows(db, where = "", params = []) {
8
+ return db
9
+ .prepare(`SELECT id, data_json as dataJson FROM entries ${where}`)
10
+ .all(...params);
11
+ }
12
+ export function parseEntryData(dataJson) {
13
+ return JSON.parse(dataJson);
14
+ }
15
+ export function schemaEntriesUseField(db, schemaId, fieldSlug) {
16
+ const rows = listEntryDataRows(db, "WHERE schema_id = ?", [schemaId]);
17
+ return rows.some((row) => {
18
+ const value = parseEntryData(row.dataJson)[fieldSlug];
19
+ return value !== undefined && value !== null && value !== "";
20
+ });
21
+ }
@@ -0,0 +1,8 @@
1
+ import type { SqliteDatabase } from "./sqlite.js";
2
+ import type { CreateRoleInput, RoleRecord } from "./role-repository.type.js";
3
+ export declare function createRole(db: SqliteDatabase, input: CreateRoleInput): RoleRecord;
4
+ export declare function createAdminRole(db: SqliteDatabase, input: CreateRoleInput): RoleRecord;
5
+ export declare function listRoles(db: SqliteDatabase): RoleRecord[];
6
+ export declare function listAdminRoles(db: SqliteDatabase): RoleRecord[];
7
+ export declare function getRoleById(db: SqliteDatabase, id: string): RoleRecord | undefined;
8
+ //# sourceMappingURL=role-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-repository.d.ts","sourceRoot":"","sources":["../src/role-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAO7E,wBAAgB,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CAQjF;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CAQtF;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,GAAG,UAAU,EAAE,CAK1D;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,GAAG,UAAU,EAAE,CAK/D;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAGlF"}
@@ -0,0 +1,66 @@
1
+ import { randomUUID } from "node:crypto";
2
+ const roleNamePattern = /^[a-z][a-z0-9-]*$/;
3
+ const adminRoleNames = new Set(["owner", "admin", "schema-manager", "user-manager"]);
4
+ export function createRole(db, input) {
5
+ validateApiRole(input);
6
+ const id = randomUUID();
7
+ const now = new Date().toISOString();
8
+ db.prepare("INSERT INTO roles (id, name, description, is_owner, role_kind, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)").run(id, input.name, input.description ?? "", 0, "api", now, now);
9
+ return requireRole(db, id);
10
+ }
11
+ export function createAdminRole(db, input) {
12
+ validateAdminRole(input);
13
+ const id = randomUUID();
14
+ const now = new Date().toISOString();
15
+ db.prepare("INSERT INTO roles (id, name, description, is_owner, role_kind, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)").run(id, input.name, input.description ?? "", 0, "admin", now, now);
16
+ return requireRole(db, id);
17
+ }
18
+ export function listRoles(db) {
19
+ const rows = db
20
+ .prepare(roleSelectSql("WHERE role_kind = 'api' ORDER BY created_at ASC"))
21
+ .all();
22
+ return rows.map(rowToRole);
23
+ }
24
+ export function listAdminRoles(db) {
25
+ const rows = db
26
+ .prepare(roleSelectSql("WHERE role_kind = 'admin' ORDER BY is_owner DESC, created_at ASC"))
27
+ .all();
28
+ return rows.map(rowToRole);
29
+ }
30
+ export function getRoleById(db, id) {
31
+ const row = db.prepare(roleSelectSql("WHERE id = ?")).get(id);
32
+ return row ? rowToRole(row) : undefined;
33
+ }
34
+ function validateApiRole(input) {
35
+ validateRoleName(input);
36
+ if (input.name === "owner") {
37
+ throw new Error("ROLE_OWNER_RESERVED");
38
+ }
39
+ if (adminRoleNames.has(input.name)) {
40
+ throw new Error("ROLE_ADMIN_RESERVED");
41
+ }
42
+ }
43
+ function validateAdminRole(input) {
44
+ validateRoleName(input);
45
+ if (input.name === "owner") {
46
+ throw new Error("ROLE_OWNER_RESERVED");
47
+ }
48
+ }
49
+ function validateRoleName(input) {
50
+ if (!roleNamePattern.test(input.name)) {
51
+ throw new Error("ROLE_NAME_INVALID");
52
+ }
53
+ }
54
+ function requireRole(db, id) {
55
+ const role = getRoleById(db, id);
56
+ if (!role) {
57
+ throw new Error("ROLE_NOT_FOUND");
58
+ }
59
+ return role;
60
+ }
61
+ function rowToRole(row) {
62
+ return { ...row, isOwner: Boolean(row.isOwner) };
63
+ }
64
+ function roleSelectSql(suffix) {
65
+ return `SELECT id, name, description, is_owner as isOwner, role_kind as roleKind, created_at as createdAt, updated_at as updatedAt FROM roles ${suffix}`;
66
+ }
@@ -0,0 +1,15 @@
1
+ export type RoleKind = "admin" | "api";
2
+ export type CreateRoleInput = {
3
+ name: string;
4
+ description?: string;
5
+ };
6
+ export type RoleRecord = {
7
+ id: string;
8
+ name: string;
9
+ description: string;
10
+ isOwner: boolean;
11
+ roleKind: RoleKind;
12
+ createdAt: string;
13
+ updatedAt: string;
14
+ };
15
+ //# sourceMappingURL=role-repository.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role-repository.type.d.ts","sourceRoot":"","sources":["../src/role-repository.type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;AAEvC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { SqliteDatabase } from "./sqlite.js";
2
+ import type { CreateSchemaInput, SchemaRecord, UpdateSchemaInput } from "./schema-repository.type.js";
3
+ export declare function createSchema(db: SqliteDatabase, input: CreateSchemaInput): SchemaRecord;
4
+ export declare function listSchemas(db: SqliteDatabase): SchemaRecord[];
5
+ export declare function getSchemaById(db: SqliteDatabase, id: string): SchemaRecord | undefined;
6
+ export declare function getSchemaBySlug(db: SqliteDatabase, slug: string): SchemaRecord | undefined;
7
+ export declare function updateSchema(db: SqliteDatabase, id: string, input: UpdateSchemaInput): SchemaRecord;
8
+ export declare function deleteSchema(db: SqliteDatabase, id: string): void;
9
+ //# sourceMappingURL=schema-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-repository.d.ts","sourceRoot":"","sources":["../src/schema-repository.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAEV,iBAAiB,EAIjB,YAAY,EACZ,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAoBrC,wBAAgB,YAAY,CAC1B,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,iBAAiB,GACvB,YAAY,CAmBd;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,GAAG,YAAY,EAAE,CAK9D;AAED,wBAAgB,aAAa,CAC3B,EAAE,EAAE,cAAc,EAClB,EAAE,EAAE,MAAM,GACT,YAAY,GAAG,SAAS,CAQ1B;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,cAAc,EAClB,IAAI,EAAE,MAAM,GACX,YAAY,GAAG,SAAS,CAK1B;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,cAAc,EAClB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,iBAAiB,GACvB,YAAY,CAsBd;AA6BD,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAMjE"}
@@ -0,0 +1,155 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { relationErrors, relationFieldUpdateUnsafe, relationSchemaReferenced, } from "./relation-errors.js";
3
+ import { schemaEntriesUseField } from "./relation-helpers.js";
4
+ const slugPattern = /^[a-z][a-z0-9-]*$/;
5
+ const fieldTypes = [
6
+ "text",
7
+ "longText",
8
+ "number",
9
+ "boolean",
10
+ "date",
11
+ "json",
12
+ "media",
13
+ "relation",
14
+ ];
15
+ const relationTypes = [
16
+ "oneToOne",
17
+ "oneToMany",
18
+ "manyToOne",
19
+ "manyToMany",
20
+ ];
21
+ export function createSchema(db, input) {
22
+ validateSchemaInput(db, input);
23
+ const schemaId = randomUUID();
24
+ const now = new Date().toISOString();
25
+ const description = input.description ?? "";
26
+ const create = db.transaction(() => {
27
+ db.prepare("INSERT INTO schemas (id, name, slug, description, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(schemaId, input.name, input.slug, description, now, now);
28
+ input.fields.forEach((field, index) => insertField(db, schemaId, field, index));
29
+ });
30
+ create();
31
+ const created = getSchemaById(db, schemaId);
32
+ if (!created) {
33
+ throw new Error("SCHEMA_CREATE_FAILED");
34
+ }
35
+ return created;
36
+ }
37
+ export function listSchemas(db) {
38
+ const rows = db.prepare("SELECT id FROM schemas ORDER BY created_at ASC").all();
39
+ return rows.map((row) => getSchemaById(db, row.id)).filter(isSchemaRecord);
40
+ }
41
+ export function getSchemaById(db, id) {
42
+ const schema = db.prepare("SELECT id, name, slug, description FROM schemas WHERE id = ?").get(id);
43
+ if (!schema) {
44
+ return undefined;
45
+ }
46
+ return { ...schema, fields: listFields(db, schema.id) };
47
+ }
48
+ export function getSchemaBySlug(db, slug) {
49
+ const row = db.prepare("SELECT id FROM schemas WHERE slug = ?").get(slug);
50
+ return row ? getSchemaById(db, row.id) : undefined;
51
+ }
52
+ export function updateSchema(db, id, input) {
53
+ if (!getSchemaById(db, id)) {
54
+ throw new Error("SCHEMA_NOT_FOUND");
55
+ }
56
+ assertSafeRelationUpdate(db, id, input);
57
+ validateSchemaInput(db, input);
58
+ const now = new Date().toISOString();
59
+ const description = input.description ?? "";
60
+ const update = db.transaction(() => {
61
+ db.prepare("UPDATE schemas SET name = ?, slug = ?, description = ?, updated_at = ? WHERE id = ?").run(input.name, input.slug, description, now, id);
62
+ db.prepare("DELETE FROM fields WHERE schema_id = ?").run(id);
63
+ input.fields.forEach((field, index) => insertField(db, id, field, index));
64
+ });
65
+ update();
66
+ const updated = getSchemaById(db, id);
67
+ if (!updated) {
68
+ throw new Error("SCHEMA_UPDATE_FAILED");
69
+ }
70
+ return updated;
71
+ }
72
+ function assertSafeRelationUpdate(db, schemaId, input) {
73
+ const current = getSchemaById(db, schemaId);
74
+ if (!current)
75
+ return;
76
+ const nextBySlug = new Map(input.fields.map((field) => [field.slug, field]));
77
+ for (const currentField of current.fields) {
78
+ if (currentField.type !== "relation")
79
+ continue;
80
+ if (!schemaEntriesUseField(db, schemaId, currentField.slug))
81
+ continue;
82
+ const nextField = nextBySlug.get(currentField.slug);
83
+ if (!nextField || nextField.type !== "relation") {
84
+ throw new Error(relationFieldUpdateUnsafe(currentField.slug));
85
+ }
86
+ const currentRelationType = currentField.relationType ?? "manyToOne";
87
+ const nextRelationType = nextField.relationType ?? "manyToOne";
88
+ if (currentField.relationSchemaId !== nextField.relationSchemaId ||
89
+ currentRelationType !== nextRelationType) {
90
+ throw new Error(relationFieldUpdateUnsafe(currentField.slug));
91
+ }
92
+ }
93
+ }
94
+ export function deleteSchema(db, id) {
95
+ assertSchemaNotReferenced(db, id);
96
+ const result = db.prepare("DELETE FROM schemas WHERE id = ?").run(id);
97
+ if (result.changes === 0) {
98
+ throw new Error("SCHEMA_NOT_FOUND");
99
+ }
100
+ }
101
+ function assertSchemaNotReferenced(db, id) {
102
+ const dependent = db
103
+ .prepare("SELECT schema_id as schemaId FROM fields WHERE relation_schema_id = ? AND schema_id != ? LIMIT 1")
104
+ .get(id, id);
105
+ if (dependent) {
106
+ throw new Error(relationSchemaReferenced(id));
107
+ }
108
+ }
109
+ function insertField(db, schemaId, field, position) {
110
+ db.prepare("INSERT INTO fields (id, schema_id, name, slug, type, relation_schema_id, relation_type, required, position) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)").run(randomUUID(), schemaId, field.name, field.slug, field.type, field.relationSchemaId ?? null, field.relationType ?? null, field.required ? 1 : 0, position);
111
+ }
112
+ function listFields(db, schemaId) {
113
+ const rows = db
114
+ .prepare("SELECT id, schema_id as schemaId, name, slug, type, relation_schema_id as relationSchemaId, relation_type as relationType, required, position FROM fields WHERE schema_id = ? ORDER BY position ASC")
115
+ .all(schemaId);
116
+ return rows.map((row) => ({ ...row, required: Boolean(row.required) }));
117
+ }
118
+ function validateSchemaInput(db, input) {
119
+ validateSlug(input.slug, "SCHEMA_SLUG_INVALID");
120
+ if (input.fields.length === 0) {
121
+ throw new Error("SCHEMA_FIELDS_REQUIRED");
122
+ }
123
+ for (const field of input.fields) {
124
+ validateSlug(field.slug, "FIELD_SLUG_INVALID");
125
+ if (!fieldTypes.includes(field.type)) {
126
+ throw new Error("FIELD_TYPE_INVALID");
127
+ }
128
+ validateRelationMetadata(db, field);
129
+ }
130
+ }
131
+ function validateRelationMetadata(db, field) {
132
+ if (field.type !== "relation") {
133
+ if (field.relationSchemaId || field.relationType) {
134
+ throw new Error(relationErrors.metadataForNonRelationField);
135
+ }
136
+ return;
137
+ }
138
+ if (!field.relationSchemaId) {
139
+ throw new Error(relationErrors.targetRequired);
140
+ }
141
+ if (!getSchemaById(db, field.relationSchemaId)) {
142
+ throw new Error(relationErrors.targetMissing);
143
+ }
144
+ if (field.relationType && !relationTypes.includes(field.relationType)) {
145
+ throw new Error(relationErrors.typeInvalid);
146
+ }
147
+ }
148
+ function validateSlug(slug, error) {
149
+ if (!slugPattern.test(slug)) {
150
+ throw new Error(error);
151
+ }
152
+ }
153
+ function isSchemaRecord(value) {
154
+ return Boolean(value);
155
+ }
@@ -0,0 +1,45 @@
1
+ export type FieldType = "text" | "longText" | "number" | "boolean" | "date" | "json" | "media" | "relation";
2
+ export type RelationType = "oneToOne" | "oneToMany" | "manyToOne" | "manyToMany";
3
+ export type SingleRelationValue = string | null;
4
+ export type MultiRelationValue = string[];
5
+ export type RelationValue = SingleRelationValue | MultiRelationValue;
6
+ export type RelationFieldContract = {
7
+ relationSchemaId: string;
8
+ relationType: RelationType;
9
+ required: boolean;
10
+ valueShape: "single" | "multi";
11
+ };
12
+ export type CreateFieldInput = {
13
+ name: string;
14
+ slug: string;
15
+ type: FieldType;
16
+ required?: boolean;
17
+ relationSchemaId?: string;
18
+ relationType?: RelationType;
19
+ };
20
+ export type CreateSchemaInput = {
21
+ name: string;
22
+ slug: string;
23
+ description?: string;
24
+ fields: CreateFieldInput[];
25
+ };
26
+ export type UpdateSchemaInput = CreateSchemaInput;
27
+ export type FieldRecord = {
28
+ id: string;
29
+ schemaId: string;
30
+ name: string;
31
+ slug: string;
32
+ type: FieldType;
33
+ required: boolean;
34
+ relationSchemaId?: string | null;
35
+ relationType?: RelationType | null;
36
+ position: number;
37
+ };
38
+ export type SchemaRecord = {
39
+ id: string;
40
+ name: string;
41
+ slug: string;
42
+ description: string;
43
+ fields: FieldRecord[];
44
+ };
45
+ //# sourceMappingURL=schema-repository.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-repository.type.d.ts","sourceRoot":"","sources":["../src/schema-repository.type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,GACN,OAAO,GACP,UAAU,CAAC;AAEf,MAAM,MAAM,YAAY,GACpB,UAAU,GACV,WAAW,GACX,WAAW,GACX,YAAY,CAAC;AAEjB,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAAC;AAEhD,MAAM,MAAM,kBAAkB,GAAG,MAAM,EAAE,CAAC;AAE1C,MAAM,MAAM,aAAa,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAAG;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,QAAQ,GAAG,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,gBAAgB,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAElD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ export type MvpTableName = "roles" | "users" | "schemas" | "fields" | "entries" | "permissions" | "admin_permissions" | "api_tokens" | "webhooks" | "webhook_events" | "webhook_deliveries" | "realtime_configs" | "realtime_events" | "realtime_sessions" | "migrations";
2
+ export type MigrationRecord = {
3
+ id: string;
4
+ applied_at: string;
5
+ };
6
+ export type TableInfoRow = {
7
+ name: string;
8
+ };
9
+ //# sourceMappingURL=schema.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.type.d.ts","sourceRoot":"","sources":["../src/schema.type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,OAAO,GACP,SAAS,GACT,QAAQ,GACR,SAAS,GACT,aAAa,GACb,mBAAmB,GACnB,YAAY,GACZ,UAAU,GACV,gBAAgB,GAChB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,mBAAmB,GACnB,YAAY,CAAC;AAEjB,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import Database from "better-sqlite3";
2
+ export type SqliteDatabase = Database.Database;
3
+ export declare function openSqliteDatabase(path?: string): SqliteDatabase;
4
+ export declare function migrateMvpDatabase(db: SqliteDatabase): void;
5
+ export declare function listMvpTables(db: SqliteDatabase): string[];
6
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAStC,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,QAAQ,CAAC;AAE/C,wBAAgB,kBAAkB,CAAC,IAAI,SAAa,GAAG,cAAc,CAIpE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,GAAG,IAAI,CAa3D;AAkBD,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,EAAE,CAQ1D"}
package/dist/sqlite.js ADDED
@@ -0,0 +1,40 @@
1
+ import Database from "better-sqlite3";
2
+ import { MVP_ADDITIVE_MIGRATIONS_SQL, MVP_FOUNDATION_SQL, MVP_MIGRATION_ID, MVP_TABLES, } from "./migrations.js";
3
+ export function openSqliteDatabase(path = ":memory:") {
4
+ const db = new Database(path);
5
+ db.pragma("foreign_keys = ON");
6
+ return db;
7
+ }
8
+ export function migrateMvpDatabase(db) {
9
+ db.exec(MVP_FOUNDATION_SQL);
10
+ applyAdditiveMigrations(db);
11
+ const existing = db
12
+ .prepare("SELECT id, applied_at FROM migrations WHERE id = ?")
13
+ .get(MVP_MIGRATION_ID);
14
+ if (!existing) {
15
+ db.prepare("INSERT INTO migrations (id, applied_at) VALUES (?, ?)").run(MVP_MIGRATION_ID, new Date().toISOString());
16
+ }
17
+ }
18
+ function applyAdditiveMigrations(db) {
19
+ for (const sql of MVP_ADDITIVE_MIGRATIONS_SQL) {
20
+ try {
21
+ db.exec(sql);
22
+ }
23
+ catch (error) {
24
+ if (!isDuplicateColumnError(error)) {
25
+ throw error;
26
+ }
27
+ }
28
+ }
29
+ }
30
+ function isDuplicateColumnError(error) {
31
+ return error instanceof Error && error.message.includes("duplicate column name");
32
+ }
33
+ export function listMvpTables(db) {
34
+ const rows = db
35
+ .prepare("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
36
+ .all();
37
+ return rows
38
+ .map((row) => row.name)
39
+ .filter((name) => MVP_TABLES.includes(name));
40
+ }
@@ -0,0 +1,11 @@
1
+ import type { SqliteDatabase } from "./sqlite.js";
2
+ import type { CreateUserInput, UserRecord } from "./user-repository.type.js";
3
+ export declare function createUser(db: SqliteDatabase, input: CreateUserInput): UserRecord;
4
+ export declare function listUsers(db: SqliteDatabase): UserRecord[];
5
+ export declare function getUserById(db: SqliteDatabase, id: string): UserRecord | undefined;
6
+ export declare function getUserPasswordHashByEmail(db: SqliteDatabase, email: string): {
7
+ id: string;
8
+ passwordHash: string;
9
+ roleId: string;
10
+ } | undefined;
11
+ //# sourceMappingURL=user-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-repository.d.ts","sourceRoot":"","sources":["../src/user-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAI7E,wBAAgB,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CASjF;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,GAAG,UAAU,EAAE,CAK1D;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAIlF;AAED,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,MAAM,GACZ;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQlE"}
@@ -0,0 +1,56 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { getRoleById } from "./role-repository.js";
3
+ export function createUser(db, input) {
4
+ const email = normalizeEmail(input.email);
5
+ validateUser(db, { ...input, email });
6
+ const id = randomUUID();
7
+ const now = new Date().toISOString();
8
+ db.prepare("INSERT INTO users (id, email, password_hash, role_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(id, email, input.passwordHash, input.roleId, now, now);
9
+ return requireUser(db, id);
10
+ }
11
+ export function listUsers(db) {
12
+ const rows = db
13
+ .prepare(userSelectSql("WHERE roles.role_kind = 'api' ORDER BY users.created_at ASC"))
14
+ .all();
15
+ return rows;
16
+ }
17
+ export function getUserById(db, id) {
18
+ return db
19
+ .prepare(userSelectSql("WHERE users.id = ? AND roles.role_kind = 'api'"))
20
+ .get(id);
21
+ }
22
+ export function getUserPasswordHashByEmail(db, email) {
23
+ return db
24
+ .prepare(`SELECT users.id, users.password_hash as passwordHash, users.role_id as roleId
25
+ FROM users JOIN roles ON roles.id = users.role_id
26
+ WHERE users.email = ? AND roles.role_kind = 'api'`)
27
+ .get(normalizeEmail(email));
28
+ }
29
+ function validateUser(db, input) {
30
+ if (!input.email.includes("@")) {
31
+ throw new Error("USER_EMAIL_INVALID");
32
+ }
33
+ if (!input.passwordHash) {
34
+ throw new Error("USER_PASSWORD_HASH_REQUIRED");
35
+ }
36
+ const role = getRoleById(db, input.roleId);
37
+ if (!role) {
38
+ throw new Error("ROLE_NOT_FOUND");
39
+ }
40
+ if (role.roleKind !== "api") {
41
+ throw new Error("ROLE_API_REQUIRED");
42
+ }
43
+ }
44
+ function requireUser(db, id) {
45
+ const user = getUserById(db, id);
46
+ if (!user) {
47
+ throw new Error("USER_NOT_FOUND");
48
+ }
49
+ return user;
50
+ }
51
+ function normalizeEmail(email) {
52
+ return email.trim().toLowerCase();
53
+ }
54
+ function userSelectSql(suffix) {
55
+ return `SELECT users.id, users.email, users.role_id as roleId, roles.name as roleName, roles.role_kind as roleKind, users.created_at as createdAt, users.updated_at as updatedAt FROM users JOIN roles ON roles.id = users.role_id ${suffix}`;
56
+ }
@@ -0,0 +1,15 @@
1
+ export type CreateUserInput = {
2
+ email: string;
3
+ passwordHash: string;
4
+ roleId: string;
5
+ };
6
+ export type UserRecord = {
7
+ id: string;
8
+ email: string;
9
+ roleId: string;
10
+ roleName: string;
11
+ roleKind: "api";
12
+ createdAt: string;
13
+ updatedAt: string;
14
+ };
15
+ //# sourceMappingURL=user-repository.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-repository.type.d.ts","sourceRoot":"","sources":["../src/user-repository.type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { SqliteDatabase } from "./sqlite.js";
2
+ import type { EnqueueWebhookEventInput, RecordWebhookDeliveryInput, WebhookDeliveryRecord, WebhookDraft, WebhookEventRecord, WebhookEventStatus, WebhookRecord, WebhookSecretRecord } from "./webhook-repository.type.js";
3
+ export declare function createWebhook(db: SqliteDatabase, input: WebhookDraft): WebhookRecord;
4
+ export declare function updateWebhook(db: SqliteDatabase, webhookId: string, input: WebhookDraft): WebhookRecord;
5
+ export declare function listWebhooks(db: SqliteDatabase): WebhookRecord[];
6
+ export declare function deleteWebhook(db: SqliteDatabase, webhookId: string): boolean;
7
+ export declare function enqueueWebhookEvent(db: SqliteDatabase, input: EnqueueWebhookEventInput): WebhookEventRecord;
8
+ export declare function listPendingWebhookEvents(db: SqliteDatabase, now?: string, limit?: number): WebhookEventRecord[];
9
+ export declare function listMatchingWebhooks(db: SqliteDatabase, event: WebhookEventRecord): WebhookSecretRecord[];
10
+ export declare function listWebhookDeliveries(db: SqliteDatabase, webhookId?: string): WebhookDeliveryRecord[];
11
+ export declare function countWebhookDeliveryAttempts(db: SqliteDatabase, eventId: string, webhookId: string): number;
12
+ export declare function hasSuccessfulWebhookDelivery(db: SqliteDatabase, eventId: string, webhookId: string): boolean;
13
+ export declare function recordWebhookDelivery(db: SqliteDatabase, input: RecordWebhookDeliveryInput): WebhookDeliveryRecord;
14
+ export declare function updateWebhookEventStatus(db: SqliteDatabase, eventId: string, status: WebhookEventStatus, attempts: number, nextRetryAt: string | null): void;
15
+ //# sourceMappingURL=webhook-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-repository.d.ts","sourceRoot":"","sources":["../src/webhook-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAElB,aAAa,EACb,mBAAmB,EAEpB,MAAM,8BAA8B,CAAC;AAgBtC,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,CASpF;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,CASvG;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,aAAa,EAAE,CAGhE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAG5E;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,wBAAwB,GAAG,kBAAkB,CAe3G;AAED,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,SAA2B,EAAE,KAAK,SAAK,GAAG,kBAAkB,EAAE,CAI7H;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,GAAG,mBAAmB,EAAE,CAIzG;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAMrG;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAI3G;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAI5G;AAED,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,0BAA0B,GAAG,qBAAqB,CASlH;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,cAAc,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,IAAI,CAGN"}