fastify-auto-sqlapi 0.0.5 → 0.1.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 (123) hide show
  1. package/AGENTS.md +15 -2
  2. package/AGENTS_BACKEND.md +144 -20
  3. package/AGENTS_FRONTEND.md +161 -67
  4. package/README.md +47 -26
  5. package/dist/bin/generate-schema.js +9 -26
  6. package/dist/bin/generate-tables.js +7 -30
  7. package/dist/bin/utils.d.ts +28 -0
  8. package/dist/bin/utils.d.ts.map +1 -1
  9. package/dist/bin/utils.js +69 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/lib/cli/load-dependency.d.ts +8 -0
  13. package/dist/lib/cli/load-dependency.d.ts.map +1 -0
  14. package/dist/lib/cli/load-dependency.js +21 -0
  15. package/dist/lib/cli/mysql-introspect.d.ts.map +1 -1
  16. package/dist/lib/cli/mysql-introspect.js +2 -18
  17. package/dist/lib/cli/pg-introspect.d.ts.map +1 -1
  18. package/dist/lib/cli/pg-introspect.js +2 -17
  19. package/dist/lib/db.d.ts +1 -0
  20. package/dist/lib/db.d.ts.map +1 -1
  21. package/dist/lib/db.js +33 -55
  22. package/dist/lib/dialect.d.ts +17 -0
  23. package/dist/lib/dialect.d.ts.map +1 -1
  24. package/dist/lib/dialect.js +32 -0
  25. package/dist/lib/engine/bulk/bulk-delete.d.ts.map +1 -1
  26. package/dist/lib/engine/bulk/bulk-delete.js +8 -2
  27. package/dist/lib/engine/bulk/bulk-upsert.d.ts.map +1 -1
  28. package/dist/lib/engine/bulk/bulk-upsert.js +15 -43
  29. package/dist/lib/engine/rest/delete.d.ts.map +1 -1
  30. package/dist/lib/engine/rest/delete.js +14 -12
  31. package/dist/lib/engine/rest/get.d.ts.map +1 -1
  32. package/dist/lib/engine/rest/get.js +3 -5
  33. package/dist/lib/engine/rest/insert.d.ts.map +1 -1
  34. package/dist/lib/engine/rest/insert.js +11 -31
  35. package/dist/lib/engine/rest/update.d.ts.map +1 -1
  36. package/dist/lib/engine/rest/update.js +13 -46
  37. package/dist/lib/engine/search/search.d.ts.map +1 -1
  38. package/dist/lib/engine/search/search.js +553 -229
  39. package/dist/lib/engine/write-helpers.d.ts +39 -2
  40. package/dist/lib/engine/write-helpers.d.ts.map +1 -1
  41. package/dist/lib/engine/write-helpers.js +55 -12
  42. package/dist/lib/errors.d.ts +11 -0
  43. package/dist/lib/errors.d.ts.map +1 -0
  44. package/dist/lib/errors.js +12 -0
  45. package/dist/lib/schema/bulk-upsert.d.ts.map +1 -1
  46. package/dist/lib/schema/bulk-upsert.js +9 -25
  47. package/dist/lib/schema/helpers.d.ts +28 -1
  48. package/dist/lib/schema/helpers.d.ts.map +1 -1
  49. package/dist/lib/schema/helpers.js +38 -0
  50. package/dist/lib/schema/insert.d.ts.map +1 -1
  51. package/dist/lib/schema/insert.js +9 -19
  52. package/dist/lib/schema/search.d.ts.map +1 -1
  53. package/dist/lib/schema/search.js +138 -50
  54. package/dist/lib/schema/update.d.ts.map +1 -1
  55. package/dist/lib/schema/update.js +9 -25
  56. package/dist/lib/sql-api.d.ts +6 -6
  57. package/dist/lib/sql-api.d.ts.map +1 -1
  58. package/dist/lib/sql-api.js +10 -10
  59. package/dist/lib/table-helpers.d.ts +6 -1
  60. package/dist/lib/table-helpers.d.ts.map +1 -1
  61. package/dist/lib/table-helpers.js +42 -2
  62. package/dist/lib/tenant.d.ts +30 -0
  63. package/dist/lib/tenant.d.ts.map +1 -1
  64. package/dist/lib/tenant.js +78 -9
  65. package/dist/routes/auto/bulk-delete.routes.d.ts.map +1 -1
  66. package/dist/routes/auto/bulk-delete.routes.js +17 -23
  67. package/dist/routes/auto/bulk-upsert.routes.d.ts.map +1 -1
  68. package/dist/routes/auto/bulk-upsert.routes.js +16 -22
  69. package/dist/routes/auto/delete.routes.d.ts.map +1 -1
  70. package/dist/routes/auto/delete.routes.js +18 -24
  71. package/dist/routes/auto/get.routes.d.ts.map +1 -1
  72. package/dist/routes/auto/get.routes.js +16 -23
  73. package/dist/routes/auto/insert.routes.d.ts.map +1 -1
  74. package/dist/routes/auto/insert.routes.js +19 -26
  75. package/dist/routes/auto/plugin.d.ts.map +1 -1
  76. package/dist/routes/auto/plugin.js +40 -5
  77. package/dist/routes/auto/route-helpers.d.ts +27 -2
  78. package/dist/routes/auto/route-helpers.d.ts.map +1 -1
  79. package/dist/routes/auto/route-helpers.js +39 -1
  80. package/dist/routes/auto/search.routes.d.ts.map +1 -1
  81. package/dist/routes/auto/search.routes.js +47 -47
  82. package/dist/routes/auto/update.routes.d.ts.map +1 -1
  83. package/dist/routes/auto/update.routes.js +20 -27
  84. package/dist/types/cli.d.ts +22 -0
  85. package/dist/types/cli.d.ts.map +1 -0
  86. package/dist/types/cli.js +1 -0
  87. package/dist/types/computed.d.ts +29 -0
  88. package/dist/types/computed.d.ts.map +1 -0
  89. package/dist/types/computed.js +1 -0
  90. package/dist/types/conditions.d.ts +16 -0
  91. package/dist/types/conditions.d.ts.map +1 -0
  92. package/dist/types/conditions.js +1 -0
  93. package/dist/types/db.d.ts +22 -0
  94. package/dist/types/db.d.ts.map +1 -0
  95. package/dist/types/db.js +1 -0
  96. package/dist/types/dml.d.ts +66 -0
  97. package/dist/types/dml.d.ts.map +1 -0
  98. package/dist/types/dml.js +1 -0
  99. package/dist/types/join.d.ts +44 -0
  100. package/dist/types/join.d.ts.map +1 -0
  101. package/dist/types/join.js +1 -0
  102. package/dist/types/plugin.d.ts +20 -0
  103. package/dist/types/plugin.d.ts.map +1 -0
  104. package/dist/types/plugin.js +1 -0
  105. package/dist/types/schema.d.ts +10 -0
  106. package/dist/types/schema.d.ts.map +1 -0
  107. package/dist/types/schema.js +1 -0
  108. package/dist/types/search.d.ts +45 -0
  109. package/dist/types/search.d.ts.map +1 -0
  110. package/dist/types/search.js +1 -0
  111. package/dist/types/table.d.ts +76 -0
  112. package/dist/types/table.d.ts.map +1 -0
  113. package/dist/types/table.js +7 -0
  114. package/dist/types/tenant.d.ts +19 -0
  115. package/dist/types/tenant.d.ts.map +1 -0
  116. package/dist/types/tenant.js +1 -0
  117. package/dist/types/validation.d.ts +21 -0
  118. package/dist/types/validation.d.ts.map +1 -0
  119. package/dist/types/validation.js +1 -0
  120. package/dist/types.d.ts +12 -277
  121. package/dist/types.d.ts.map +1 -1
  122. package/dist/types.js +12 -7
  123. package/package.json +3 -3
@@ -4,102 +4,190 @@ const JoinGroupResultItem = Type.Object({
4
4
  sum: Type.Optional(Type.Record(Type.String(), Type.Any())),
5
5
  min: Type.Optional(Type.Record(Type.String(), Type.Any())),
6
6
  max: Type.Optional(Type.Record(Type.String(), Type.Any())),
7
+ avg: Type.Optional(Type.Record(Type.String(), Type.Any())),
8
+ count: Type.Optional(Type.Record(Type.String(), Type.Any())),
7
9
  distinctCount: Type.Optional(Type.Record(Type.String(), Type.Any())),
8
10
  rows: Type.Optional(Type.Array(Type.Any())),
9
11
  });
10
12
  export function SearchTableBodyPost(dbTables, tableName) {
11
13
  const tableConf = dbTables[tableName];
12
14
  const schema = tableConf.Schema;
13
- // Condition item shape (shared across main, joinFilters, joins, joinGroups)
14
15
  const methodEnum = Type.Union(ALLOWED_METHODS.map((m) => Type.Literal(m)));
15
16
  const conditionItemSchema = Type.Object({
16
17
  field: Type.String(),
17
18
  method: methodEnum,
18
19
  params: Type.Optional(Type.Array(Type.Any())),
19
20
  });
20
- // Filters: schema fields + extraFilters
21
+ // Build a TypeBox map of computed-field types (declared in defineTable).
22
+ const computedTypes = {};
23
+ if (tableConf.computedFields) {
24
+ for (const [name, fn] of Object.entries(tableConf.computedFields)) {
25
+ // Cheap dry-run with a stub context to extract the declared `type`.
26
+ // The fn is pure-functional w.r.t. ctx — only `type` is read here.
27
+ try {
28
+ const stub = fn({
29
+ db: { qi: (s) => s, dialectName: 'postgres' },
30
+ qiCol: () => '""',
31
+ });
32
+ if (stub.type)
33
+ computedTypes[name] = stub.type;
34
+ }
35
+ catch {
36
+ // ignore — Swagger schema generation is best-effort; runtime validates.
37
+ }
38
+ }
39
+ }
21
40
  const filterFields = {
22
41
  ...schema.fields,
23
42
  ...tableConf.extraFilters,
43
+ ...computedTypes,
24
44
  };
25
45
  const filtersSchema = Type.Optional(Type.Partial(Type.Object(filterFields)));
26
- // Joins & JoinGroups from allowedReadJoins
27
- const joinProperties = {};
28
- const joinFilterProperties = {};
29
- const joinGroupProperties = {};
46
+ // Per-alias entries split by unique flag:
47
+ // - unique:false → joinMustExist / joinMultiple / joinGroup
48
+ // - unique:true → joinLeft
49
+ const joinMustExistProps = {};
50
+ const joinMultipleProps = {};
51
+ const joinGroupProps = {};
52
+ const joinLeftProps = {};
30
53
  if (tableConf.allowedReadJoins) {
31
- for (const [joinSchema] of tableConf.allowedReadJoins) {
32
- const joinTableName = joinSchema.tableName;
33
- const joinTableConf = dbTables[joinTableName];
54
+ for (const joinDef of tableConf.allowedReadJoins) {
55
+ const { joinSchema, alias, unique } = joinDef;
56
+ const joinTableConf = dbTables[joinSchema.tableName];
57
+ const joinComputedTypes = {};
58
+ if (joinTableConf?.computedFields) {
59
+ for (const [name, fn] of Object.entries(joinTableConf.computedFields)) {
60
+ try {
61
+ const stub = fn({
62
+ db: { qi: (s) => s, dialectName: 'postgres' },
63
+ qiCol: () => '""',
64
+ });
65
+ if (stub.type)
66
+ joinComputedTypes[name] = stub.type;
67
+ }
68
+ catch {
69
+ // best-effort
70
+ }
71
+ }
72
+ }
34
73
  const joinFilterFields = joinTableConf
35
- ? { ...joinSchema.fields, ...joinTableConf.extraFilters }
74
+ ? { ...joinSchema.fields, ...joinTableConf.extraFilters, ...joinComputedTypes }
36
75
  : { ...joinSchema.fields };
37
- // Shared { filters?, conditions? } shape for joinFilters, joins, and joinGroups inner filters
38
76
  const joinRefShape = {
39
77
  filters: Type.Optional(Type.Partial(Type.Object(joinFilterFields))),
40
78
  conditions: Type.Optional(Type.Array(conditionItemSchema)),
41
79
  };
42
- joinProperties[joinTableName] = Type.Object(joinRefShape);
43
- joinFilterProperties[joinTableName] = Type.Object(joinRefShape);
44
- joinGroupProperties[joinTableName] = Type.Object({
45
- aggregations: Type.Object({
46
- by: Type.Optional(Type.String()),
47
- distinctCount: Type.Optional(Type.Array(Type.String())),
48
- min: Type.Optional(Type.Array(Type.String())),
49
- max: Type.Optional(Type.Array(Type.String())),
50
- sum: Type.Optional(Type.Array(Type.String())),
51
- avg: Type.Optional(Type.Array(Type.String())),
52
- count: Type.Optional(Type.Array(Type.String())),
53
- }),
54
- filters: Type.Optional(Type.Partial(Type.Object(joinFilterFields))),
55
- conditions: Type.Optional(Type.Array(conditionItemSchema)),
56
- });
80
+ const joinFetchShape = {
81
+ ...joinRefShape,
82
+ selection: Type.Optional(Type.String()),
83
+ };
84
+ if (unique) {
85
+ joinLeftProps[alias] = Type.Object(joinFetchShape);
86
+ }
87
+ else {
88
+ joinMustExistProps[alias] = Type.Object(joinRefShape);
89
+ joinMultipleProps[alias] = Type.Object(joinFetchShape);
90
+ joinGroupProps[alias] = Type.Object({
91
+ aggregations: Type.Object({
92
+ by: Type.Optional(Type.String()),
93
+ distinctCount: Type.Optional(Type.Array(Type.String())),
94
+ min: Type.Optional(Type.Array(Type.String())),
95
+ max: Type.Optional(Type.Array(Type.String())),
96
+ sum: Type.Optional(Type.Array(Type.String())),
97
+ avg: Type.Optional(Type.Array(Type.String())),
98
+ count: Type.Optional(Type.Array(Type.String())),
99
+ }),
100
+ filters: Type.Optional(Type.Partial(Type.Object(joinFilterFields))),
101
+ conditions: Type.Optional(Type.Array(conditionItemSchema)),
102
+ });
103
+ }
57
104
  }
58
105
  }
59
106
  const bodyProperties = {
60
107
  filters: filtersSchema,
61
108
  conditions: Type.Optional(Type.Array(conditionItemSchema)),
62
109
  };
63
- if (Object.keys(joinProperties).length > 0) {
64
- bodyProperties.joinFilters = Type.Optional(Type.Partial(Type.Object(joinFilterProperties)));
65
- bodyProperties.joins = Type.Optional(Type.Partial(Type.Object(joinProperties)));
66
- bodyProperties.joinGroups = Type.Optional(Type.Partial(Type.Object(joinGroupProperties)));
110
+ if (Object.keys(computedTypes).length > 0) {
111
+ bodyProperties.selectComputed = Type.Optional(Type.Array(Type.String()));
112
+ }
113
+ if (Object.keys(joinMustExistProps).length > 0) {
114
+ bodyProperties.joinMustExist = Type.Optional(Type.Partial(Type.Object(joinMustExistProps)));
115
+ bodyProperties.joinMultiple = Type.Optional(Type.Partial(Type.Object(joinMultipleProps)));
116
+ bodyProperties.joinGroup = Type.Optional(Type.Partial(Type.Object(joinGroupProps)));
117
+ }
118
+ if (Object.keys(joinLeftProps).length > 0) {
119
+ bodyProperties.joinLeft = Type.Optional(Type.Partial(Type.Object(joinLeftProps)));
67
120
  }
68
121
  return Type.Object(bodyProperties);
69
122
  }
70
123
  export const SearchTableQueryString = Type.Object({
71
124
  orderBy: Type.Optional(Type.String()),
72
125
  page: Type.Optional(Type.Integer({ minimum: 1 })),
73
- itemsPerPage: Type.Optional(Type.Integer({ minimum: 1, maximum: 5000, default: 500 })),
126
+ itemsPerPage: Type.Optional(Type.Integer({ minimum: 1, default: 500 })),
74
127
  computeMin: Type.Optional(Type.String()),
75
128
  computeMax: Type.Optional(Type.String()),
76
129
  computeSum: Type.Optional(Type.String()),
77
130
  computeAvg: Type.Optional(Type.String()),
131
+ // selectComputed list goes in the body (POST), not querystring — see SearchTableBodyPost.
78
132
  });
79
133
  export function SearchTableResponse(dbTables, tableName) {
80
134
  const tableConf = dbTables[tableName];
81
- const mainItem = Type.Partial(Type.Object(tableConf.Schema.fields));
82
- const joinResponseProperties = {};
83
- const joinGroupResponseProperties = {};
135
+ // Computed fields are present in main rows only when explicitly listed in
136
+ // request body's selectComputed, so they're optional/Partial here.
137
+ const computedTypes = {};
138
+ if (tableConf.computedFields) {
139
+ for (const [name, fn] of Object.entries(tableConf.computedFields)) {
140
+ try {
141
+ const stub = fn({
142
+ db: { qi: (s) => s, dialectName: 'postgres' },
143
+ qiCol: () => '""',
144
+ });
145
+ if (stub.type)
146
+ computedTypes[name] = stub.type;
147
+ }
148
+ catch {
149
+ // best-effort
150
+ }
151
+ }
152
+ }
153
+ const mainItem = Object.keys(computedTypes).length > 0
154
+ ? Type.Partial(Type.Object({ ...tableConf.Schema.fields, ...computedTypes }))
155
+ : Type.Partial(Type.Object(tableConf.Schema.fields));
156
+ const joinMultipleProps = {};
157
+ const joinLeftProps = {};
158
+ const joinGroupProps = {};
84
159
  if (tableConf.allowedReadJoins) {
85
- for (const [joinSchema] of tableConf.allowedReadJoins) {
86
- joinResponseProperties[joinSchema.tableName] = Type.Array(Type.Partial(Type.Object(joinSchema.fields)));
87
- joinGroupResponseProperties[joinSchema.tableName] = JoinGroupResultItem;
160
+ for (const joinDef of tableConf.allowedReadJoins) {
161
+ const { joinSchema, alias, unique } = joinDef;
162
+ const itemArray = Type.Array(Type.Partial(Type.Object(joinSchema.fields)));
163
+ if (unique) {
164
+ joinLeftProps[alias] = itemArray;
165
+ }
166
+ else {
167
+ joinMultipleProps[alias] = itemArray;
168
+ joinGroupProps[alias] = JoinGroupResultItem;
169
+ }
88
170
  }
89
171
  }
90
- return Type.Object({
172
+ const responseProps = {
91
173
  table: Type.String(),
92
174
  main: Type.Array(mainItem),
93
- joins: Type.Optional(Type.Partial(Type.Object(joinResponseProperties))),
94
- joinGroups: Type.Optional(Type.Partial(Type.Object(joinGroupResponseProperties))),
95
- pagination: Type.Optional(Type.Object({
96
- total: Type.Integer(),
97
- pages: Type.Integer(),
98
- computed: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
99
- paginator: Type.Object({
100
- page: Type.Integer(),
101
- itemsPerPage: Type.Integer(),
102
- }),
103
- })),
104
- });
175
+ };
176
+ if (Object.keys(joinMultipleProps).length > 0) {
177
+ responseProps.joinMultiple = Type.Optional(Type.Partial(Type.Object(joinMultipleProps)));
178
+ responseProps.joinGroup = Type.Optional(Type.Partial(Type.Object(joinGroupProps)));
179
+ }
180
+ if (Object.keys(joinLeftProps).length > 0) {
181
+ responseProps.joinLeft = Type.Optional(Type.Partial(Type.Object(joinLeftProps)));
182
+ }
183
+ responseProps.pagination = Type.Optional(Type.Object({
184
+ total: Type.Integer(),
185
+ pages: Type.Integer(),
186
+ computed: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
187
+ paginator: Type.Object({
188
+ page: Type.Integer(),
189
+ itemsPerPage: Type.Integer(),
190
+ }),
191
+ }));
192
+ return Type.Object(responseProps);
105
193
  }
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAsC9E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA8BlF"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/lib/schema/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAgB,MAAM,mBAAmB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAS/C,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAqB9E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAalF"}
@@ -1,7 +1,6 @@
1
1
  import { Type } from '@sinclair/typebox';
2
2
  import { primaryAsString } from '../../types.js';
3
- import { findSecondaryTableConf } from '../engine/write-helpers.js';
4
- import { pkSchema, buildSecondaryFields, applySchemaOverrides } from './helpers.js';
3
+ import { pkSchema, applySchemaOverrides, attachWriteJoinSections, writeJoinBodyFields, writeJoinResponseFields, } from './helpers.js';
5
4
  export function UpdateTableBody(dbTables, tableName) {
6
5
  const tableConf = dbTables[tableName];
7
6
  const schema = tableConf.Schema;
@@ -14,18 +13,10 @@ export function UpdateTableBody(dbTables, tableName) {
14
13
  const bodyProperties = {
15
14
  main: Type.Object(mainFields),
16
15
  };
17
- if (tableConf.allowedWriteJoins?.length) {
18
- const secondaryProperties = {};
19
- const deletionProperties = {};
20
- for (const [joinSchema, joinField] of tableConf.allowedWriteJoins) {
21
- const secondaryTableConf = findSecondaryTableConf(dbTables, joinSchema.tableName);
22
- const joinFields = buildSecondaryFields(joinSchema, joinField, secondaryTableConf);
23
- secondaryProperties[joinSchema.tableName] = Type.Array(Type.Object(joinFields));
24
- deletionProperties[joinSchema.tableName] = Type.Array(Type.Partial(Type.Object(joinSchema.fields)));
25
- }
26
- bodyProperties.secondaries = Type.Optional(Type.Partial(Type.Object(secondaryProperties)));
27
- bodyProperties.deletions = Type.Optional(Type.Partial(Type.Object(deletionProperties)));
28
- }
16
+ attachWriteJoinSections(bodyProperties, tableConf, dbTables, {
17
+ withDeletions: true,
18
+ secondaryFields: writeJoinBodyFields,
19
+ });
29
20
  return Type.Object(bodyProperties);
30
21
  }
31
22
  export function UpdateTableResponse(dbTables, tableName) {
@@ -33,16 +24,9 @@ export function UpdateTableResponse(dbTables, tableName) {
33
24
  const responseProperties = {
34
25
  main: Type.Object(pkSchema(tableConf, tableConf.Schema, primaryAsString(tableConf.primary))),
35
26
  };
36
- if (tableConf.allowedWriteJoins?.length) {
37
- const secondaryProperties = {};
38
- const deletionProperties = {};
39
- for (const [joinSchema, joinField] of tableConf.allowedWriteJoins) {
40
- const secondaryTableConf = findSecondaryTableConf(dbTables, joinSchema.tableName);
41
- secondaryProperties[joinSchema.tableName] = Type.Array(Type.Object(pkSchema(secondaryTableConf, joinSchema, joinField)));
42
- deletionProperties[joinSchema.tableName] = Type.Array(Type.Partial(Type.Object(joinSchema.fields)));
43
- }
44
- responseProperties.secondaries = Type.Optional(Type.Partial(Type.Object(secondaryProperties)));
45
- responseProperties.deletions = Type.Optional(Type.Partial(Type.Object(deletionProperties)));
46
- }
27
+ attachWriteJoinSections(responseProperties, tableConf, dbTables, {
28
+ withDeletions: true,
29
+ secondaryFields: writeJoinResponseFields,
30
+ });
47
31
  return Type.Object(responseProperties);
48
32
  }
@@ -1,21 +1,21 @@
1
1
  import type { FastifyRequest } from 'fastify';
2
2
  import { QueryClient } from './db.js';
3
3
  import { type DialectName } from './dialect.js';
4
- import type { DbTables, Queryable, FilterRecord, SearchCondition, Paginator, JoinGroupRequest, SearchResult, GetResult, InsertResult, UpdateResult, DeleteResult, BulkUpsertItem, BulkUpsertResult, BulkDeleteResult, TenantId } from '../types.js';
4
+ import type { DbTables, Queryable, FilterRecord, SearchCondition, Paginator, JoinGroupRequest, JoinRefFilter, JoinFetchRequest, SearchResult, GetResult, InsertResult, UpdateResult, DeleteResult, BulkUpsertItem, BulkUpsertResult, BulkDeleteResult, TenantId } from '../types.js';
5
5
  export interface SqlApiSearchParams {
6
6
  filters?: FilterRecord;
7
7
  conditions?: SearchCondition[];
8
- joinFilters?: Record<string, FilterRecord>;
9
- joins?: Record<string, {
10
- filters?: FilterRecord;
11
- }>;
12
- joinGroups?: Record<string, JoinGroupRequest>;
8
+ joinMustExist?: Record<string, JoinRefFilter>;
9
+ joinMultiple?: Record<string, JoinFetchRequest>;
10
+ joinGroup?: Record<string, JoinGroupRequest>;
11
+ joinLeft?: Record<string, JoinFetchRequest>;
13
12
  orderBy?: string;
14
13
  paginator?: Paginator;
15
14
  computeMin?: string;
16
15
  computeMax?: string;
17
16
  computeSum?: string;
18
17
  computeAvg?: string;
18
+ selectComputed?: string[];
19
19
  }
20
20
  export interface SqlApiInsertParams {
21
21
  record: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"sql-api.d.ts","sourceRoot":"","sources":["../../src/lib/sql-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sql-api.d.ts","sourceRoot":"","sources":["../../src/lib/sql-api.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import { ConditionBuilder } from 'node-condition-builder';
2
2
  import { createQueryClient, QueryClient } from './db.js';
3
3
  import { getDialect } from './dialect.js';
4
4
  import { resolveTenant } from './tenant.js';
5
+ import { httpError } from './errors.js';
5
6
  import { searchEngine } from './engine/search/search.js';
6
7
  import { getEngine } from './engine/rest/get.js';
7
8
  import { insertEngine } from './engine/rest/insert.js';
@@ -31,11 +32,8 @@ export class SqlApi {
31
32
  }
32
33
  getTableConf(tableName) {
33
34
  const conf = this.dbTables[tableName];
34
- if (!conf) {
35
- const err = new Error(`Table "${tableName}" not found in DbTables`);
36
- err.statusCode = 400;
37
- throw err;
38
- }
35
+ if (!conf)
36
+ throw httpError(400, `Table "${tableName}" not found in DbTables`);
39
37
  return conf;
40
38
  }
41
39
  async getTenant(tableName, request) {
@@ -52,15 +50,17 @@ export class SqlApi {
52
50
  tableConf,
53
51
  filters: params.filters,
54
52
  conditions: params.conditions,
55
- joinFilters: params.joinFilters,
56
- joins: params.joins,
57
- joinGroups: params.joinGroups,
53
+ joinMustExist: params.joinMustExist,
54
+ joinMultiple: params.joinMultiple,
55
+ joinGroup: params.joinGroup,
56
+ joinLeft: params.joinLeft,
58
57
  orderBy: params.orderBy,
59
58
  paginator: params.paginator,
60
59
  computeMin: params.computeMin,
61
60
  computeMax: params.computeMax,
62
61
  computeSum: params.computeSum,
63
62
  computeAvg: params.computeAvg,
63
+ selectComputed: params.selectComputed,
64
64
  tenant,
65
65
  });
66
66
  }
@@ -99,7 +99,7 @@ export class SqlApi {
99
99
  async delete(tableName, id, request) {
100
100
  const tableConf = this.getTableConf(tableName);
101
101
  const tenant = await this.getTenant(tableName, request);
102
- return deleteEngine({ db: this.db, tableConf, id, tenant });
102
+ return deleteEngine({ db: this.db, tableConf, id, tenant, request });
103
103
  }
104
104
  async bulkUpsert(tableName, items, request) {
105
105
  const tableConf = this.getTableConf(tableName);
@@ -116,7 +116,7 @@ export class SqlApi {
116
116
  async bulkDelete(tableName, ids, request) {
117
117
  const tableConf = this.getTableConf(tableName);
118
118
  const tenant = await this.getTenant(tableName, request);
119
- return bulkDeleteEngine({ db: this.db, tableConf, ids, tenant });
119
+ return bulkDeleteEngine({ db: this.db, tableConf, ids, tenant, request });
120
120
  }
121
121
  }
122
122
  export function createSqlApi(dbOrPool, dbTables, options) {
@@ -10,7 +10,12 @@ export declare function exportTableInfo<F extends Record<string, TSchema>, EF ex
10
10
  extraFilters: Record<string, TSchema>;
11
11
  };
12
12
  export declare function defineTable<F extends Record<string, TSchema>>(config: ITable<F>): ITable<F>;
13
- export declare function buildRelation<M extends Record<string, TSchema>, J extends Record<string, TSchema>>(mainSchema: SchemaDefinition<M>, mainField: string & keyof M | (string & keyof M)[], joinSchema: SchemaDefinition<J>, joinField: string & keyof J, selection?: string): JoinDefinition;
13
+ export interface BuildRelationOptions {
14
+ alias?: string;
15
+ selection?: string;
16
+ unique?: boolean;
17
+ }
18
+ export declare function buildRelation<M extends Record<string, TSchema>, J extends Record<string, TSchema>>(mainSchema: SchemaDefinition<M>, mainField: string & keyof M | (string & keyof M)[], joinSchema: SchemaDefinition<J>, joinField: string & keyof J, options?: BuildRelationOptions): JoinDefinition;
14
19
  export declare function buildUpsertRule<F extends Record<string, TSchema>>(schema: SchemaDefinition<F>, columns: (string & keyof F)[]): [SchemaDefinition, string[]];
15
20
  export declare function buildUpsertRules(...rules: [SchemaDefinition, string[]][]): Map<SchemaDefinition, string[]>;
16
21
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"table-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/table-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,MAAM,EACP,MAAM,aAAa,CAAC;AAYrB,KAAK,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAExH,wBAAgB,eAAe,CAC7B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,EAAE,SAAS,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAErE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,YAAY,GAAE,EAAa,EAC3B,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,yBAAyB;CAAE,KAC3E,IAAI,GACR;IAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CA0BhG;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAChB,MAAM,CAAC,CAAC,CAAC,CAEX;AAED,wBAAgB,aAAa,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,EAClD,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,EAC3B,SAAS,GAAE,MAAY,GACtB,cAAc,CAEhB;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,GAC5B,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAE9B;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,KAAK,EAAE,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,EAAE,GACvC,GAAG,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAEjC"}
1
+ {"version":3,"file":"table-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/table-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,MAAM,EACP,MAAM,aAAa,CAAC;AAYrB,KAAK,UAAU,CAAC,EAAE,IAAI,EAAE,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAExH,wBAAgB,eAAe,CAC7B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,EAAE,SAAS,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAErE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,YAAY,GAAE,EAAa,EAC3B,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,yBAAyB;CAAE,KAC3E,IAAI,GACR;IAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CA0BhG;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAChB,MAAM,CAAC,CAAC,CAAC,CAKX;AA2CD,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,aAAa,CAC3B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEjC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,EAClD,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC/B,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,EAC3B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,cAAc,CAShB;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,OAAO,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,GAC5B,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAE9B;AAED,wBAAgB,gBAAgB,CAC9B,GAAG,KAAK,EAAE,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,EAAE,GACvC,GAAG,CAAC,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAEjC"}
@@ -29,10 +29,50 @@ export function exportTableInfo(Schema, extraFilters = {}, extendedCondition) {
29
29
  return { Schema, filters, extraFilters: efRecord };
30
30
  }
31
31
  export function defineTable(config) {
32
+ validateAliasUniqueness(config.allowedReadJoins, 'allowedReadJoins');
33
+ validateAliasUniqueness(config.allowedWriteJoins, 'allowedWriteJoins');
34
+ validateComputedFields(config);
32
35
  return config;
33
36
  }
34
- export function buildRelation(mainSchema, mainField, joinSchema, joinField, selection = '*') {
35
- return [joinSchema, joinField, mainField, selection];
37
+ function validateComputedFields(config) {
38
+ const computed = config.computedFields;
39
+ if (!computed)
40
+ return;
41
+ const schemaFields = Object.keys(config.Schema.fields);
42
+ const extraKeys = Object.keys(config.extraFilters || {});
43
+ for (const name of Object.keys(computed)) {
44
+ if (schemaFields.includes(name)) {
45
+ throw new Error(`defineTable: computedFields name '${name}' collides with a schema field on ` +
46
+ `table '${config.Schema.tableName}'. Choose a different name.`);
47
+ }
48
+ if (extraKeys.includes(name)) {
49
+ throw new Error(`defineTable: computedFields name '${name}' collides with an extraFilters key on ` +
50
+ `table '${config.Schema.tableName}'. Choose a different name.`);
51
+ }
52
+ }
53
+ }
54
+ function validateAliasUniqueness(joins, label) {
55
+ if (!joins?.length)
56
+ return;
57
+ const seen = new Set();
58
+ for (const j of joins) {
59
+ if (seen.has(j.alias)) {
60
+ throw new Error(`defineTable: duplicate alias '${j.alias}' in ${label}. ` +
61
+ `When omitted, alias defaults to joinSchema.tableName — ` +
62
+ `declare an explicit alias to disambiguate.`);
63
+ }
64
+ seen.add(j.alias);
65
+ }
66
+ }
67
+ export function buildRelation(mainSchema, mainField, joinSchema, joinField, options) {
68
+ return {
69
+ joinSchema,
70
+ joinField,
71
+ mainField,
72
+ alias: options?.alias ?? joinSchema.tableName,
73
+ selection: options?.selection ?? '*',
74
+ unique: options?.unique ?? false,
75
+ };
36
76
  }
37
77
  export function buildUpsertRule(schema, columns) {
38
78
  return [schema, columns];
@@ -15,5 +15,35 @@ export declare function buildTenantDeleteWhere(db: QueryClient, tableName: strin
15
15
  where: string;
16
16
  values: unknown[];
17
17
  };
18
+ /**
19
+ * Mutates `fields` in-place: removes the tenant column from a SET payload (direct scopes only).
20
+ * No-op for indirect or no scope.
21
+ */
18
22
  export declare function stripTenantColumn(fields: Record<string, unknown>, scope: TenantScope): void;
23
+ /**
24
+ * For insert/bulk-upsert: enforce tenant on records that are about to be written.
25
+ * For direct scopes, mutates each record in-place to inject/validate the tenant column.
26
+ * For indirect scopes, batch-validates the FK values against the tenant scope.
27
+ * Throws 400 (ambiguous tenant) or 403 (mismatch) on violation. No-op when `tenant` is undefined.
28
+ */
29
+ export declare function enforceTenantOnWrites(db: QueryClient, tenant: TenantContext | undefined, records: Record<string, unknown>[]): Promise<void>;
30
+ /**
31
+ * For update: builds the optional `extraCondition` ConditionBuilder for direct tenants.
32
+ * Returns undefined for indirect or no-tenant scenarios — those are handled separately
33
+ * (indirect uses pre-check via `assertTenantOwnership`).
34
+ */
35
+ export declare function buildTenantUpdateExtra(tenant: TenantContext | undefined): ConditionBuilder | undefined;
36
+ /**
37
+ * For update with indirect tenant: pre-check ownership via `SELECT 1 ... INNER JOIN ... LIMIT 1`.
38
+ * Throws 404 if the record either does not exist or does not belong to the tenant.
39
+ * No-op for direct or no-tenant — those are enforced via `extraCondition` in the UPDATE itself.
40
+ */
41
+ export declare function assertTenantOwnership(db: QueryClient, tenant: TenantContext | undefined, tableName: string, pkCol: string, pkValue: ConditionValue): Promise<void>;
42
+ /**
43
+ * Assert that EVERY pk in `ids` exists and belongs to the tenant, for both direct and indirect
44
+ * scopes. Throws 404 if any id is missing or not owned. No-op when `tenant` is undefined or `ids`
45
+ * is empty. Used by the delete engines to enforce tenant ownership BEFORE running a `beforeDelete`/
46
+ * `beforeBulkDelete` hook, so user code never runs against rows the caller cannot access.
47
+ */
48
+ export declare function assertTenantOwnsAll(db: QueryClient, tenant: TenantContext | undefined, tableName: string, pkCol: string, ids: ConditionValue[]): Promise<void>;
19
49
  //# sourceMappingURL=tenant.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/lib/tenant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAC/G,OAAO,KAAK,EACV,MAAM,EACN,mBAAmB,EACnB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,aAAa,EACd,MAAM,aAAa,CAAC;AAMrB,wBAAsB,aAAa,CACjC,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CASpC;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,QAAQ,EAAE,GACpB,gBAAgB,CAclB;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,mBAAmB,EAC1B,aAAa,EAAE,MAAM,GACpB,MAAM,CAMR;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,QAAQ,EAAE,GACpB,IAAI,CAqBN;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,mBAAmB,EAC1B,SAAS,EAAE,QAAQ,EAAE,EACrB,QAAQ,EAAE,OAAO,EAAE,GAClB,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,WAAW,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GAAG,cAAc,EAAE,EACrD,MAAM,EAAE,aAAa,GACpB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAsBtC;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,WAAW,GACjB,IAAI,CAGN"}
1
+ {"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../src/lib/tenant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,KAAK,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAE/G,OAAO,KAAK,EACV,MAAM,EACN,mBAAmB,EACnB,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,aAAa,EACd,MAAM,aAAa,CAAC;AAMrB,wBAAsB,aAAa,CACjC,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CASpC;AAED,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,QAAQ,EAAE,GACpB,gBAAgB,CAclB;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,mBAAmB,EAC1B,aAAa,EAAE,MAAM,GACpB,MAAM,CAMR;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,QAAQ,EAAE,GACpB,IAAI,CAiBN;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,WAAW,EACf,KAAK,EAAE,mBAAmB,EAC1B,SAAS,EAAE,QAAQ,EAAE,EACrB,QAAQ,EAAE,OAAO,EAAE,GAClB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,WAAW,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,GAAG,cAAc,EAAE,EACrD,MAAM,EAAE,aAAa,GACpB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAsBtC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,WAAW,GACjB,IAAI,CAGN;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,WAAW,EACf,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,gBAAgB,GAAG,SAAS,CAM9B;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,WAAW,EACf,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,WAAW,EACf,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,cAAc,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC,CAgBf"}