@orthacms/content-server 0.4.2 → 0.4.3

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 (135) hide show
  1. package/dist/index.d.ts +15 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +38 -1
  4. package/dist/lib/content.module.d.ts.map +1 -1
  5. package/dist/lib/content.module.js +23 -1
  6. package/dist/lib/entries/http/controllers/create-entry.controller.d.ts.map +1 -1
  7. package/dist/lib/entries/http/controllers/create-entry.controller.js +1 -1
  8. package/dist/lib/entries/http/controllers/update-entry.controller.d.ts.map +1 -1
  9. package/dist/lib/entries/http/controllers/update-entry.controller.js +1 -1
  10. package/dist/lib/entries/http/dto/extension-bag.validator.d.ts +14 -0
  11. package/dist/lib/entries/http/dto/extension-bag.validator.d.ts.map +1 -0
  12. package/dist/lib/entries/http/dto/extension-bag.validator.js +42 -0
  13. package/dist/lib/entries/http/dto/save-entry.dto.d.ts +16 -0
  14. package/dist/lib/entries/http/dto/save-entry.dto.d.ts.map +1 -1
  15. package/dist/lib/entries/http/dto/save-entry.dto.js +30 -0
  16. package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts +28 -2
  17. package/dist/lib/entries/infrastructure/persistence/entry-writer.service.d.ts.map +1 -1
  18. package/dist/lib/entries/infrastructure/persistence/entry-writer.service.js +112 -15
  19. package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts +38 -5
  20. package/dist/lib/entries/infrastructure/persistence/relation-link.service.d.ts.map +1 -1
  21. package/dist/lib/entries/infrastructure/persistence/relation-link.service.js +38 -6
  22. package/dist/lib/entries/infrastructure/queries/entries.service.d.ts +3 -1
  23. package/dist/lib/entries/infrastructure/queries/entries.service.d.ts.map +1 -1
  24. package/dist/lib/entries/infrastructure/queries/entries.service.js +17 -5
  25. package/dist/lib/entries/infrastructure/queries/entry-match.query.d.ts +82 -0
  26. package/dist/lib/entries/infrastructure/queries/entry-match.query.d.ts.map +1 -0
  27. package/dist/lib/entries/infrastructure/queries/entry-match.query.js +152 -0
  28. package/dist/lib/extension/entry-access-source.d.ts +112 -0
  29. package/dist/lib/extension/entry-access-source.d.ts.map +1 -0
  30. package/dist/lib/extension/entry-filter-provider.d.ts +79 -0
  31. package/dist/lib/extension/entry-filter-provider.d.ts.map +1 -0
  32. package/dist/lib/extension/entry-filter-provider.js +149 -0
  33. package/dist/lib/extension/entry-write-extension.d.ts +185 -0
  34. package/dist/lib/extension/entry-write-extension.d.ts.map +1 -0
  35. package/dist/lib/extension/entry-write-extension.js +182 -0
  36. package/dist/lib/extension/entry-write-hook.d.ts +98 -0
  37. package/dist/lib/extension/entry-write-hook.d.ts.map +1 -0
  38. package/dist/lib/extension/read-scope.d.ts +107 -0
  39. package/dist/lib/extension/read-scope.d.ts.map +1 -0
  40. package/dist/lib/extension/read-scope.js +131 -0
  41. package/dist/lib/public-api/infrastructure/public-entries.query.d.ts +14 -1
  42. package/dist/lib/public-api/infrastructure/public-entries.query.d.ts.map +1 -1
  43. package/dist/lib/public-api/infrastructure/public-entries.query.js +20 -3
  44. package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts +16 -4
  45. package/dist/lib/public-api/infrastructure/public-expansion.query.d.ts.map +1 -1
  46. package/dist/lib/public-api/infrastructure/public-expansion.query.js +29 -9
  47. package/dist/lib/revisions/application/use-cases/restore-revision.use-case.d.ts.map +1 -1
  48. package/dist/lib/revisions/application/use-cases/restore-revision.use-case.js +12 -1
  49. package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts +7 -1
  50. package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.d.ts.map +1 -1
  51. package/dist/lib/revisions/infrastructure/persistence/revision-snapshot.js +9 -2
  52. package/dist/lib/revisions/types/revision-view.d.ts +11 -0
  53. package/dist/lib/revisions/types/revision-view.d.ts.map +1 -1
  54. package/dist/lib/utils/content-views-plugin.d.ts +28 -0
  55. package/dist/lib/utils/content-views-plugin.d.ts.map +1 -0
  56. package/dist/lib/utils/content-views-plugin.js +29 -0
  57. package/dist/lib/views/application/queries/saved-views.query.d.ts +16 -0
  58. package/dist/lib/views/application/queries/saved-views.query.d.ts.map +1 -0
  59. package/dist/lib/views/application/queries/saved-views.query.js +46 -0
  60. package/dist/lib/views/application/saved-view-access.service.d.ts +30 -0
  61. package/dist/lib/views/application/saved-view-access.service.d.ts.map +1 -0
  62. package/dist/lib/views/application/saved-view-access.service.js +55 -0
  63. package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts +27 -0
  64. package/dist/lib/views/application/use-cases/create-saved-view.use-case.d.ts.map +1 -0
  65. package/dist/lib/views/application/use-cases/create-saved-view.use-case.js +57 -0
  66. package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts +15 -0
  67. package/dist/lib/views/application/use-cases/delete-saved-view.use-case.d.ts.map +1 -0
  68. package/dist/lib/views/application/use-cases/delete-saved-view.use-case.js +35 -0
  69. package/dist/lib/views/application/use-cases/set-default-view.use-case.d.ts +23 -0
  70. package/dist/lib/views/application/use-cases/set-default-view.use-case.d.ts.map +1 -0
  71. package/dist/lib/views/application/use-cases/set-default-view.use-case.js +51 -0
  72. package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts +21 -0
  73. package/dist/lib/views/application/use-cases/update-saved-view.use-case.d.ts.map +1 -0
  74. package/dist/lib/views/application/use-cases/update-saved-view.use-case.js +61 -0
  75. package/dist/lib/views/application/view-scope.service.d.ts +25 -0
  76. package/dist/lib/views/application/view-scope.service.d.ts.map +1 -0
  77. package/dist/lib/views/application/view-scope.service.js +45 -0
  78. package/dist/lib/views/content-views.module.d.ts +22 -0
  79. package/dist/lib/views/content-views.module.d.ts.map +1 -0
  80. package/dist/lib/views/content-views.module.js +63 -0
  81. package/dist/lib/views/domain/errors/index.d.ts +6 -0
  82. package/dist/lib/views/domain/errors/index.d.ts.map +1 -0
  83. package/dist/lib/views/domain/errors/index.js +12 -0
  84. package/dist/lib/views/domain/errors/saved-view-forbidden.error.d.ts +10 -0
  85. package/dist/lib/views/domain/errors/saved-view-forbidden.error.d.ts.map +1 -0
  86. package/dist/lib/views/domain/errors/saved-view-forbidden.error.js +16 -0
  87. package/dist/lib/views/domain/errors/saved-view-limit-error.d.ts +9 -0
  88. package/dist/lib/views/domain/errors/saved-view-limit-error.d.ts.map +1 -0
  89. package/dist/lib/views/domain/errors/saved-view-limit-error.js +16 -0
  90. package/dist/lib/views/domain/errors/saved-view-name-taken.error.d.ts +5 -0
  91. package/dist/lib/views/domain/errors/saved-view-name-taken.error.d.ts.map +1 -0
  92. package/dist/lib/views/domain/errors/saved-view-name-taken.error.js +11 -0
  93. package/dist/lib/views/domain/errors/saved-view-not-found.error.d.ts +9 -0
  94. package/dist/lib/views/domain/errors/saved-view-not-found.error.d.ts.map +1 -0
  95. package/dist/lib/views/domain/errors/saved-view-not-found.error.js +15 -0
  96. package/dist/lib/views/domain/saved-view.d.ts +66 -0
  97. package/dist/lib/views/domain/saved-view.d.ts.map +1 -0
  98. package/dist/lib/views/domain/saved-view.js +18 -0
  99. package/dist/lib/views/domain/saved-view.repository.d.ts +77 -0
  100. package/dist/lib/views/domain/saved-view.repository.d.ts.map +1 -0
  101. package/dist/lib/views/domain/saved-view.repository.js +5 -0
  102. package/dist/lib/views/http/controllers/saved-views.controller.d.ts +45 -0
  103. package/dist/lib/views/http/controllers/saved-views.controller.d.ts.map +1 -0
  104. package/dist/lib/views/http/controllers/saved-views.controller.js +216 -0
  105. package/dist/lib/views/http/controllers/to-http-error.d.ts +10 -0
  106. package/dist/lib/views/http/controllers/to-http-error.d.ts.map +1 -0
  107. package/dist/lib/views/http/controllers/to-http-error.js +26 -0
  108. package/dist/lib/views/http/dto/save-view.dto.d.ts +54 -0
  109. package/dist/lib/views/http/dto/save-view.dto.d.ts.map +1 -0
  110. package/dist/lib/views/http/dto/save-view.dto.js +230 -0
  111. package/dist/lib/views/http/dto/view-payload.validator.d.ts +20 -0
  112. package/dist/lib/views/http/dto/view-payload.validator.d.ts.map +1 -0
  113. package/dist/lib/views/http/dto/view-payload.validator.js +52 -0
  114. package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.d.ts +17 -0
  115. package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.d.ts.map +1 -0
  116. package/dist/lib/views/infrastructure/persistence/drizzle-saved-view.repository.js +125 -0
  117. package/dist/lib/views/infrastructure/schema/external-refs.d.ts +68 -0
  118. package/dist/lib/views/infrastructure/schema/external-refs.d.ts.map +1 -0
  119. package/dist/lib/views/infrastructure/schema/external-refs.js +29 -0
  120. package/dist/lib/views/infrastructure/schema/index.d.ts +8 -0
  121. package/dist/lib/views/infrastructure/schema/index.d.ts.map +1 -0
  122. package/dist/lib/views/infrastructure/schema/index.js +13 -0
  123. package/dist/lib/views/infrastructure/schema/saved-views.d.ts +275 -0
  124. package/dist/lib/views/infrastructure/schema/saved-views.d.ts.map +1 -0
  125. package/dist/lib/views/infrastructure/schema/saved-views.js +87 -0
  126. package/dist/lib/views/views.constants.d.ts +26 -0
  127. package/dist/lib/views/views.constants.d.ts.map +1 -0
  128. package/dist/lib/views/views.constants.js +28 -0
  129. package/dist/lib/views/views.tokens.d.ts +17 -0
  130. package/dist/lib/views/views.tokens.d.ts.map +1 -0
  131. package/dist/lib/views/views.tokens.js +21 -0
  132. package/migrations/0000_saved_views.sql +29 -0
  133. package/migrations/meta/0000_snapshot.json +256 -0
  134. package/migrations/meta/_journal.json +13 -0
  135. package/package.json +13 -12
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ListViewsQueryDto = exports.UpdateViewDto = exports.CreateViewDto = exports.ViewPayloadDto = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const swagger_1 = require("@nestjs/swagger");
6
+ const class_transformer_1 = require("class-transformer");
7
+ const class_validator_1 = require("class-validator");
8
+ const class_transformer_2 = require("class-transformer");
9
+ const entries_constants_1 = require("../../../entries/entries.constants");
10
+ const saved_view_1 = require("../../domain/saved-view");
11
+ const views_constants_1 = require("../../views.constants");
12
+ const view_payload_validator_1 = require("./view-payload.validator");
13
+ /**
14
+ * The slice a view stores. Mirrors the records page's URL params rather than
15
+ * re-modelling them, so a saved view and a hand-edited link travel the same
16
+ * code path. `search` and `page` are absent by design — see
17
+ * {@link SavedViewPayload}.
18
+ */
19
+ class ViewPayloadDto {
20
+ /** The `?filter=` JSON string. */
21
+ filter;
22
+ /** The `?sort=` spec. */
23
+ sort;
24
+ /** Rows per page. */
25
+ pageSize;
26
+ /** Visible column ids, in display order. */
27
+ columns;
28
+ /** Slot-owned list params (e.g. the i18n plugin's `locale`). */
29
+ extra;
30
+ }
31
+ exports.ViewPayloadDto = ViewPayloadDto;
32
+ tslib_1.__decorate([
33
+ (0, swagger_1.ApiPropertyOptional)({
34
+ type: String,
35
+ maxLength: entries_constants_1.FILTER_MAX_LENGTH,
36
+ example: '{"op":"and","rules":[{"field":"status","op":"eq","value":"review"}]}',
37
+ description: 'The filter tree as a JSON string, byte-identical to the `?filter=` query param. Stored opaquely and re-validated against the type when replayed.'
38
+ }),
39
+ (0, class_validator_1.IsOptional)(),
40
+ (0, class_validator_1.IsString)(),
41
+ (0, class_validator_1.MaxLength)(entries_constants_1.FILTER_MAX_LENGTH),
42
+ tslib_1.__metadata("design:type", String)
43
+ ], ViewPayloadDto.prototype, "filter", void 0);
44
+ tslib_1.__decorate([
45
+ (0, swagger_1.ApiPropertyOptional)({
46
+ type: String,
47
+ maxLength: 255,
48
+ example: '-updatedAt',
49
+ description: 'Sort spec: a column id (ascending) or `-`-prefixed (descending).'
50
+ }),
51
+ (0, class_validator_1.IsOptional)(),
52
+ (0, class_validator_1.IsString)(),
53
+ (0, class_validator_1.MaxLength)(255),
54
+ tslib_1.__metadata("design:type", String)
55
+ ], ViewPayloadDto.prototype, "sort", void 0);
56
+ tslib_1.__decorate([
57
+ (0, swagger_1.ApiPropertyOptional)({
58
+ type: 'integer',
59
+ minimum: 1,
60
+ maximum: entries_constants_1.MAX_PAGE_SIZE,
61
+ example: 25,
62
+ description: 'Rows per page this view opens with.'
63
+ }),
64
+ (0, class_validator_1.IsOptional)(),
65
+ (0, class_validator_1.IsInt)(),
66
+ (0, class_validator_1.Min)(1),
67
+ (0, class_validator_1.Max)(entries_constants_1.MAX_PAGE_SIZE),
68
+ tslib_1.__metadata("design:type", Number)
69
+ ], ViewPayloadDto.prototype, "pageSize", void 0);
70
+ tslib_1.__decorate([
71
+ (0, swagger_1.ApiPropertyOptional)({
72
+ type: [String],
73
+ maxItems: views_constants_1.VIEW_MAX_COLUMNS,
74
+ example: ['title', 'status', 'updatedAt'],
75
+ description: 'Visible column ids **in display order**. Ids the type no longer has are dropped when the view is applied.'
76
+ }),
77
+ (0, class_validator_1.IsOptional)(),
78
+ (0, class_validator_1.IsArray)(),
79
+ (0, class_validator_1.ArrayMaxSize)(views_constants_1.VIEW_MAX_COLUMNS),
80
+ (0, class_validator_1.IsString)({ each: true }),
81
+ (0, class_validator_1.MaxLength)(views_constants_1.VIEW_COLUMN_MAX_LENGTH, { each: true }),
82
+ tslib_1.__metadata("design:type", Array)
83
+ ], ViewPayloadDto.prototype, "columns", void 0);
84
+ tslib_1.__decorate([
85
+ (0, swagger_1.ApiPropertyOptional)({
86
+ type: 'object',
87
+ additionalProperties: { type: 'string' },
88
+ example: { locale: 'en' },
89
+ description: 'Slot-contributed list params, as a flat string map. Opaque on purpose — the keys come from plugin-registered toolbar items at runtime.'
90
+ }),
91
+ (0, class_validator_1.IsOptional)(),
92
+ (0, class_validator_1.IsObject)(),
93
+ (0, view_payload_validator_1.IsViewExtraParams)(),
94
+ tslib_1.__metadata("design:type", Object)
95
+ ], ViewPayloadDto.prototype, "extra", void 0);
96
+ /** Body of `POST /api/views`. */
97
+ class CreateViewDto {
98
+ /**
99
+ * The list this view is over. v1 accepts content collections only, so a
100
+ * typo cannot quietly create a view nothing will ever list.
101
+ */
102
+ scope;
103
+ /** Display name. */
104
+ name;
105
+ /** Who may see it. Defaults to private. */
106
+ visibility;
107
+ /** The slice it restores. */
108
+ payload;
109
+ /** Whether to land on this view when opening the list. */
110
+ makeDefault;
111
+ }
112
+ exports.CreateViewDto = CreateViewDto;
113
+ tslib_1.__decorate([
114
+ (0, swagger_1.ApiProperty)({
115
+ type: String,
116
+ maxLength: views_constants_1.VIEW_SCOPE_MAX_LENGTH,
117
+ example: 'content:article',
118
+ description: 'The list this view belongs to. Must be `content:<typeName>`.'
119
+ }),
120
+ (0, class_validator_1.IsString)(),
121
+ (0, class_validator_1.MaxLength)(views_constants_1.VIEW_SCOPE_MAX_LENGTH),
122
+ (0, class_validator_1.Matches)(new RegExp(`^${views_constants_1.CONTENT_SCOPE_PREFIX}[A-Za-z0-9_-]+$`), {
123
+ message: `scope must be "${views_constants_1.CONTENT_SCOPE_PREFIX}<typeName>"`
124
+ }),
125
+ tslib_1.__metadata("design:type", String)
126
+ ], CreateViewDto.prototype, "scope", void 0);
127
+ tslib_1.__decorate([
128
+ (0, swagger_1.ApiProperty)({
129
+ type: String,
130
+ minLength: 1,
131
+ maxLength: views_constants_1.VIEW_NAME_MAX_LENGTH,
132
+ example: 'Needs review',
133
+ description: 'Display name, unique per person within one list.'
134
+ }),
135
+ (0, class_validator_1.IsString)(),
136
+ (0, class_transformer_2.Transform)(({ value }) => typeof value === 'string' ? value.trim() : value),
137
+ (0, class_validator_1.MinLength)(1),
138
+ (0, class_validator_1.MaxLength)(views_constants_1.VIEW_NAME_MAX_LENGTH),
139
+ tslib_1.__metadata("design:type", String)
140
+ ], CreateViewDto.prototype, "name", void 0);
141
+ tslib_1.__decorate([
142
+ (0, swagger_1.ApiPropertyOptional)({
143
+ enum: saved_view_1.VIEW_VISIBILITY_VALUES,
144
+ default: saved_view_1.VIEW_VISIBILITY.Private,
145
+ description: '`private` (only you) or `workspace` (every member). Sharing requires the `views:share` permission.'
146
+ }),
147
+ (0, class_validator_1.IsOptional)(),
148
+ (0, class_validator_1.IsIn)(saved_view_1.VIEW_VISIBILITY_VALUES),
149
+ tslib_1.__metadata("design:type", String)
150
+ ], CreateViewDto.prototype, "visibility", void 0);
151
+ tslib_1.__decorate([
152
+ (0, swagger_1.ApiProperty)({
153
+ type: () => ViewPayloadDto,
154
+ description: 'The filter/sort/columns/page-size slice this view stores.'
155
+ }),
156
+ (0, class_validator_1.ValidateNested)(),
157
+ (0, class_transformer_1.Type)(() => ViewPayloadDto),
158
+ tslib_1.__metadata("design:type", ViewPayloadDto)
159
+ ], CreateViewDto.prototype, "payload", void 0);
160
+ tslib_1.__decorate([
161
+ (0, swagger_1.ApiPropertyOptional)({
162
+ type: Boolean,
163
+ default: false,
164
+ description: 'Make this the caller’s default view for the list.'
165
+ }),
166
+ (0, class_validator_1.IsOptional)(),
167
+ (0, class_validator_1.IsBoolean)(),
168
+ tslib_1.__metadata("design:type", Boolean)
169
+ ], CreateViewDto.prototype, "makeDefault", void 0);
170
+ /**
171
+ * Body of `PATCH /api/views/:id`. Every field optional — the switcher sends
172
+ * only `payload` on “Save”, only `name` on a rename, and only `visibility` on
173
+ * share/unshare.
174
+ */
175
+ class UpdateViewDto {
176
+ /** New display name. */
177
+ name;
178
+ /** New visibility. */
179
+ visibility;
180
+ /** New slice. */
181
+ payload;
182
+ }
183
+ exports.UpdateViewDto = UpdateViewDto;
184
+ tslib_1.__decorate([
185
+ (0, swagger_1.ApiPropertyOptional)({
186
+ type: String,
187
+ minLength: 1,
188
+ maxLength: views_constants_1.VIEW_NAME_MAX_LENGTH,
189
+ example: 'Needs review'
190
+ }),
191
+ (0, class_validator_1.IsOptional)(),
192
+ (0, class_validator_1.IsString)(),
193
+ (0, class_transformer_2.Transform)(({ value }) => typeof value === 'string' ? value.trim() : value),
194
+ (0, class_validator_1.MinLength)(1),
195
+ (0, class_validator_1.MaxLength)(views_constants_1.VIEW_NAME_MAX_LENGTH),
196
+ tslib_1.__metadata("design:type", String)
197
+ ], UpdateViewDto.prototype, "name", void 0);
198
+ tslib_1.__decorate([
199
+ (0, swagger_1.ApiPropertyOptional)({ enum: saved_view_1.VIEW_VISIBILITY_VALUES }),
200
+ (0, class_validator_1.IsOptional)(),
201
+ (0, class_validator_1.IsIn)(saved_view_1.VIEW_VISIBILITY_VALUES),
202
+ tslib_1.__metadata("design:type", String)
203
+ ], UpdateViewDto.prototype, "visibility", void 0);
204
+ tslib_1.__decorate([
205
+ (0, swagger_1.ApiPropertyOptional)({ type: () => ViewPayloadDto }),
206
+ (0, class_validator_1.IsOptional)(),
207
+ (0, class_validator_1.ValidateNested)(),
208
+ (0, class_transformer_1.Type)(() => ViewPayloadDto),
209
+ tslib_1.__metadata("design:type", ViewPayloadDto)
210
+ ], UpdateViewDto.prototype, "payload", void 0);
211
+ /** Query of `GET /api/views`. */
212
+ class ListViewsQueryDto {
213
+ /** The list to return views for. */
214
+ scope;
215
+ }
216
+ exports.ListViewsQueryDto = ListViewsQueryDto;
217
+ tslib_1.__decorate([
218
+ (0, swagger_1.ApiProperty)({
219
+ type: String,
220
+ maxLength: views_constants_1.VIEW_SCOPE_MAX_LENGTH,
221
+ example: 'content:article',
222
+ description: 'The list to return saved views for (`content:<typeName>`).'
223
+ }),
224
+ (0, class_validator_1.IsString)(),
225
+ (0, class_validator_1.MaxLength)(views_constants_1.VIEW_SCOPE_MAX_LENGTH),
226
+ (0, class_validator_1.Matches)(new RegExp(`^${views_constants_1.CONTENT_SCOPE_PREFIX}[A-Za-z0-9_-]+$`), {
227
+ message: `scope must be "${views_constants_1.CONTENT_SCOPE_PREFIX}<typeName>"`
228
+ }),
229
+ tslib_1.__metadata("design:type", String)
230
+ ], ListViewsQueryDto.prototype, "scope", void 0);
@@ -0,0 +1,20 @@
1
+ import { type ValidationOptions, type ValidatorConstraintInterface } from 'class-validator';
2
+ /**
3
+ * Validates a payload's `extra` bag: the slot-owned list params (the i18n
4
+ * plugin's `?locale=`, and whatever a later plugin contributes) as a flat
5
+ * string map.
6
+ *
7
+ * class-validator cannot decorate a `Record`'s dynamic keys, and the keys here
8
+ * are genuinely dynamic — they come from `RECORDS_TOOLBAR_SLOT.listParamKeys`
9
+ * at runtime, so naming them in a DTO would silently break the next plugin's
10
+ * params. What we can still enforce is the shape and a bound: flat, strings on
11
+ * both sides, and small. That turns a malformed bag into a clean 400 instead of
12
+ * a jsonb column quietly swallowing a nested object nothing can replay.
13
+ */
14
+ export declare class ViewExtraParamsConstraint implements ValidatorConstraintInterface {
15
+ validate(value: unknown): boolean;
16
+ defaultMessage(): string;
17
+ }
18
+ /** Applies {@link ViewExtraParamsConstraint} to a property. */
19
+ export declare function IsViewExtraParams(options?: ValidationOptions): (object: object, propertyName: string) => void;
20
+ //# sourceMappingURL=view-payload.validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-payload.validator.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/http/dto/view-payload.validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,iBAAiB,EACtB,KAAK,4BAA4B,EACpC,MAAM,iBAAiB,CAAC;AAMzB;;;;;;;;;;;GAWG;AACH,qBACa,yBAA0B,YAAW,4BAA4B;IAC1E,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAcjC,cAAc,IAAI,MAAM;CAG3B;AAED,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,iBAAiB,IACxC,QAAQ,MAAM,EAAE,cAAc,MAAM,KAAG,IAAI,CAQ/D"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ViewExtraParamsConstraint = void 0;
4
+ exports.IsViewExtraParams = IsViewExtraParams;
5
+ const tslib_1 = require("tslib");
6
+ const class_validator_1 = require("class-validator");
7
+ const views_constants_1 = require("../../views.constants");
8
+ /**
9
+ * Validates a payload's `extra` bag: the slot-owned list params (the i18n
10
+ * plugin's `?locale=`, and whatever a later plugin contributes) as a flat
11
+ * string map.
12
+ *
13
+ * class-validator cannot decorate a `Record`'s dynamic keys, and the keys here
14
+ * are genuinely dynamic — they come from `RECORDS_TOOLBAR_SLOT.listParamKeys`
15
+ * at runtime, so naming them in a DTO would silently break the next plugin's
16
+ * params. What we can still enforce is the shape and a bound: flat, strings on
17
+ * both sides, and small. That turns a malformed bag into a clean 400 instead of
18
+ * a jsonb column quietly swallowing a nested object nothing can replay.
19
+ */
20
+ let ViewExtraParamsConstraint = class ViewExtraParamsConstraint {
21
+ validate(value) {
22
+ if (value === null || typeof value !== 'object')
23
+ return false;
24
+ if (Array.isArray(value))
25
+ return false;
26
+ const entries = Object.entries(value);
27
+ if (entries.length > views_constants_1.VIEW_MAX_EXTRA_KEYS)
28
+ return false;
29
+ return entries.every(([key, param]) => key.length > 0 &&
30
+ key.length <= views_constants_1.VIEW_EXTRA_MAX_LENGTH &&
31
+ typeof param === 'string' &&
32
+ param.length <= views_constants_1.VIEW_EXTRA_MAX_LENGTH);
33
+ }
34
+ defaultMessage() {
35
+ return `extra must be a flat map of at most ${views_constants_1.VIEW_MAX_EXTRA_KEYS} string values, each at most ${views_constants_1.VIEW_EXTRA_MAX_LENGTH} characters`;
36
+ }
37
+ };
38
+ exports.ViewExtraParamsConstraint = ViewExtraParamsConstraint;
39
+ exports.ViewExtraParamsConstraint = ViewExtraParamsConstraint = tslib_1.__decorate([
40
+ (0, class_validator_1.ValidatorConstraint)({ name: 'viewExtraParams' })
41
+ ], ViewExtraParamsConstraint);
42
+ /** Applies {@link ViewExtraParamsConstraint} to a property. */
43
+ function IsViewExtraParams(options) {
44
+ return function (object, propertyName) {
45
+ (0, class_validator_1.registerDecorator)({
46
+ target: object.constructor,
47
+ propertyName,
48
+ options,
49
+ validator: ViewExtraParamsConstraint
50
+ });
51
+ };
52
+ }
@@ -0,0 +1,17 @@
1
+ import { type Database } from '@orthacms/database';
2
+ import type { NewSavedView, SavedViewPatch, SavedViewRecord, SavedViewRepository } from '../../domain/saved-view.repository';
3
+ /** Drizzle adapter for {@link SavedViewRepository}. */
4
+ export declare class DrizzleSavedViewRepository implements SavedViewRepository {
5
+ private readonly db;
6
+ constructor(db: Database);
7
+ listVisible(workspaceId: string, scope: string, readerId: string): Promise<SavedViewRecord[]>;
8
+ findById(id: string): Promise<SavedViewRecord | null>;
9
+ countForOwner(workspaceId: string, scope: string, ownerId: string): Promise<number>;
10
+ create(view: NewSavedView): Promise<SavedViewRecord>;
11
+ update(id: string, patch: SavedViewPatch): Promise<SavedViewRecord>;
12
+ delete(id: string): Promise<void>;
13
+ findDefaultId(userId: string, scope: string): Promise<string | null>;
14
+ setDefault(userId: string, scope: string, viewId: string): Promise<void>;
15
+ clearDefault(userId: string, scope: string): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=drizzle-saved-view.repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drizzle-saved-view.repository.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/infrastructure/persistence/drizzle-saved-view.repository.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGnE,OAAO,KAAK,EACR,YAAY,EACZ,cAAc,EACd,eAAe,EACf,mBAAmB,EACtB,MAAM,oCAAoC,CAAC;AAiC5C,uDAAuD;AACvD,qBACa,0BAA2B,YAAW,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAErD,WAAW,CACb,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,eAAe,EAAE,CAAC;IAuBvB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IASrD,aAAa,CACf,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC;IAcZ,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAepD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAoBnE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcpE,UAAU,CACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAYV,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUnE"}
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DrizzleSavedViewRepository = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const database_1 = require("@orthacms/database");
7
+ const drizzle_orm_1 = require("drizzle-orm");
8
+ const saved_views_1 = require("../schema/saved-views");
9
+ const saved_view_1 = require("../../domain/saved-view");
10
+ const errors_1 = require("../../domain/errors");
11
+ /** The columns every read of this table projects. */
12
+ const COLUMNS = {
13
+ id: saved_views_1.savedViews.id,
14
+ workspaceId: saved_views_1.savedViews.workspaceId,
15
+ scope: saved_views_1.savedViews.scope,
16
+ ownerId: saved_views_1.savedViews.ownerId,
17
+ visibility: saved_views_1.savedViews.visibility,
18
+ name: saved_views_1.savedViews.name,
19
+ payload: saved_views_1.savedViews.payload,
20
+ position: saved_views_1.savedViews.position,
21
+ updatedAt: saved_views_1.savedViews.updatedAt
22
+ };
23
+ /** Narrows the enum column's `string` back to the domain union. */
24
+ function toRecord(row) {
25
+ return { ...row, visibility: row.visibility };
26
+ }
27
+ /** Drizzle adapter for {@link SavedViewRepository}. */
28
+ let DrizzleSavedViewRepository = class DrizzleSavedViewRepository {
29
+ db;
30
+ constructor(db) {
31
+ this.db = db;
32
+ }
33
+ async listVisible(workspaceId, scope, readerId) {
34
+ const rows = await this.db
35
+ .select(COLUMNS)
36
+ .from(saved_views_1.savedViews)
37
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(saved_views_1.savedViews.workspaceId, workspaceId), (0, drizzle_orm_1.eq)(saved_views_1.savedViews.scope, scope),
38
+ // Own views at any visibility, plus everyone's shared ones.
39
+ (0, drizzle_orm_1.or)((0, drizzle_orm_1.eq)(saved_views_1.savedViews.ownerId, readerId), (0, drizzle_orm_1.eq)(saved_views_1.savedViews.visibility, saved_view_1.VIEW_VISIBILITY.Workspace))))
40
+ .orderBy((0, drizzle_orm_1.asc)(saved_views_1.savedViews.position), (0, drizzle_orm_1.asc)(saved_views_1.savedViews.createdAt), (0, drizzle_orm_1.asc)(saved_views_1.savedViews.id));
41
+ return rows.map(toRecord);
42
+ }
43
+ async findById(id) {
44
+ const [row] = await this.db
45
+ .select(COLUMNS)
46
+ .from(saved_views_1.savedViews)
47
+ .where((0, drizzle_orm_1.eq)(saved_views_1.savedViews.id, id))
48
+ .limit(1);
49
+ return row ? toRecord(row) : null;
50
+ }
51
+ async countForOwner(workspaceId, scope, ownerId) {
52
+ const [row] = await this.db
53
+ .select({ count: (0, drizzle_orm_1.sql) `count(*)::int` })
54
+ .from(saved_views_1.savedViews)
55
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(saved_views_1.savedViews.workspaceId, workspaceId), (0, drizzle_orm_1.eq)(saved_views_1.savedViews.scope, scope), (0, drizzle_orm_1.eq)(saved_views_1.savedViews.ownerId, ownerId)));
56
+ return row?.count ?? 0;
57
+ }
58
+ async create(view) {
59
+ const [row] = await this.db
60
+ .insert(saved_views_1.savedViews)
61
+ .values({
62
+ workspaceId: view.workspaceId,
63
+ scope: view.scope,
64
+ ownerId: view.ownerId,
65
+ visibility: view.visibility,
66
+ name: view.name,
67
+ payload: view.payload
68
+ })
69
+ .returning(COLUMNS);
70
+ return toRecord(row);
71
+ }
72
+ async update(id, patch) {
73
+ const [row] = await this.db
74
+ .update(saved_views_1.savedViews)
75
+ .set({
76
+ ...(patch.name !== undefined ? { name: patch.name } : {}),
77
+ ...(patch.visibility !== undefined
78
+ ? { visibility: patch.visibility }
79
+ : {}),
80
+ ...(patch.payload !== undefined
81
+ ? { payload: patch.payload }
82
+ : {})
83
+ })
84
+ .where((0, drizzle_orm_1.eq)(saved_views_1.savedViews.id, id))
85
+ .returning(COLUMNS);
86
+ // The use-case reads the row before patching, so a miss here means it
87
+ // was deleted in between — surface the same not-found the read would.
88
+ if (!row)
89
+ throw new errors_1.SavedViewNotFoundError(id);
90
+ return toRecord(row);
91
+ }
92
+ async delete(id) {
93
+ await this.db.delete(saved_views_1.savedViews).where((0, drizzle_orm_1.eq)(saved_views_1.savedViews.id, id));
94
+ }
95
+ async findDefaultId(userId, scope) {
96
+ const [row] = await this.db
97
+ .select({ viewId: saved_views_1.savedViewDefaults.viewId })
98
+ .from(saved_views_1.savedViewDefaults)
99
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(saved_views_1.savedViewDefaults.userId, userId), (0, drizzle_orm_1.eq)(saved_views_1.savedViewDefaults.scope, scope)))
100
+ .limit(1);
101
+ return row?.viewId ?? null;
102
+ }
103
+ async setDefault(userId, scope, viewId) {
104
+ await this.db
105
+ .insert(saved_views_1.savedViewDefaults)
106
+ .values({ userId, scope, viewId })
107
+ // One row per (user, scope) — re-pointing the default is an upsert,
108
+ // not a delete-then-insert that could lose the row on a crash.
109
+ .onConflictDoUpdate({
110
+ target: [saved_views_1.savedViewDefaults.userId, saved_views_1.savedViewDefaults.scope],
111
+ set: { viewId }
112
+ });
113
+ }
114
+ async clearDefault(userId, scope) {
115
+ await this.db
116
+ .delete(saved_views_1.savedViewDefaults)
117
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(saved_views_1.savedViewDefaults.userId, userId), (0, drizzle_orm_1.eq)(saved_views_1.savedViewDefaults.scope, scope)));
118
+ }
119
+ };
120
+ exports.DrizzleSavedViewRepository = DrizzleSavedViewRepository;
121
+ exports.DrizzleSavedViewRepository = DrizzleSavedViewRepository = tslib_1.__decorate([
122
+ (0, common_1.Injectable)(),
123
+ tslib_1.__param(0, (0, database_1.InjectDatabase)()),
124
+ tslib_1.__metadata("design:paramtypes", [Object])
125
+ ], DrizzleSavedViewRepository);
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Reference-only projections of the tables this feature's foreign keys target.
3
+ * Identity owns `users` and workspaces owns `workspaces` (with all their
4
+ * columns and their own migrations); these stubs exist so that:
5
+ *
6
+ * - the generated migration can emit the cross-context FKs
7
+ * `saved_views.owner_id → users(id)` and
8
+ * `saved_views.workspace_id → workspaces(id)` — the host applies identity's
9
+ * and workspaces' migrations before this plugin's, so both physical tables
10
+ * already exist when this one runs; and
11
+ * - `drizzle-kit generate` bundles a **pure-Drizzle** schema graph. Importing
12
+ * either runtime barrel here would pull NestJS providers into drizzle-kit's
13
+ * esbuild pass, which has no `experimentalDecorators` and fails on their
14
+ * parameter decorators.
15
+ *
16
+ * Deliberately **not** re-exported from `schema/index.ts`, so drizzle-kit
17
+ * references them only in the FKs and never emits a duplicate `CREATE TABLE`.
18
+ */
19
+ export declare const users: import("drizzle-orm/pg-core").PgTableWithColumns<{
20
+ name: "users";
21
+ schema: undefined;
22
+ columns: {
23
+ id: import("drizzle-orm/pg-core").PgColumn<{
24
+ name: "id";
25
+ tableName: "users";
26
+ dataType: "string";
27
+ columnType: "PgUUID";
28
+ data: string;
29
+ driverParam: string;
30
+ notNull: true;
31
+ hasDefault: false;
32
+ isPrimaryKey: true;
33
+ isAutoincrement: false;
34
+ hasRuntimeDefault: false;
35
+ enumValues: undefined;
36
+ baseColumn: never;
37
+ identity: undefined;
38
+ generated: undefined;
39
+ }, {}, {}>;
40
+ };
41
+ dialect: "pg";
42
+ }>;
43
+ /** @see users — same reference-only rationale, for the workspaces table. */
44
+ export declare const workspaces: import("drizzle-orm/pg-core").PgTableWithColumns<{
45
+ name: "workspaces";
46
+ schema: undefined;
47
+ columns: {
48
+ id: import("drizzle-orm/pg-core").PgColumn<{
49
+ name: "id";
50
+ tableName: "workspaces";
51
+ dataType: "string";
52
+ columnType: "PgUUID";
53
+ data: string;
54
+ driverParam: string;
55
+ notNull: true;
56
+ hasDefault: false;
57
+ isPrimaryKey: true;
58
+ isAutoincrement: false;
59
+ hasRuntimeDefault: false;
60
+ enumValues: undefined;
61
+ baseColumn: never;
62
+ identity: undefined;
63
+ generated: undefined;
64
+ }, {}, {}>;
65
+ };
66
+ dialect: "pg";
67
+ }>;
68
+ //# sourceMappingURL=external-refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-refs.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/infrastructure/schema/external-refs.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;EAEhB,CAAC;AAEH,4EAA4E;AAC5E,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;EAErB,CAAC"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.workspaces = exports.users = void 0;
4
+ const pg_core_1 = require("drizzle-orm/pg-core");
5
+ /**
6
+ * Reference-only projections of the tables this feature's foreign keys target.
7
+ * Identity owns `users` and workspaces owns `workspaces` (with all their
8
+ * columns and their own migrations); these stubs exist so that:
9
+ *
10
+ * - the generated migration can emit the cross-context FKs
11
+ * `saved_views.owner_id → users(id)` and
12
+ * `saved_views.workspace_id → workspaces(id)` — the host applies identity's
13
+ * and workspaces' migrations before this plugin's, so both physical tables
14
+ * already exist when this one runs; and
15
+ * - `drizzle-kit generate` bundles a **pure-Drizzle** schema graph. Importing
16
+ * either runtime barrel here would pull NestJS providers into drizzle-kit's
17
+ * esbuild pass, which has no `experimentalDecorators` and fails on their
18
+ * parameter decorators.
19
+ *
20
+ * Deliberately **not** re-exported from `schema/index.ts`, so drizzle-kit
21
+ * references them only in the FKs and never emits a duplicate `CREATE TABLE`.
22
+ */
23
+ exports.users = (0, pg_core_1.pgTable)('users', {
24
+ id: (0, pg_core_1.uuid)('id').primaryKey()
25
+ });
26
+ /** @see users — same reference-only rationale, for the workspaces table. */
27
+ exports.workspaces = (0, pg_core_1.pgTable)('workspaces', {
28
+ id: (0, pg_core_1.uuid)('id').primaryKey()
29
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The saved-views schema barrel — also the entry point `drizzle.config.ts`
3
+ * diffs. `external-refs.ts` is deliberately absent: those stubs exist only so
4
+ * the foreign keys resolve, and re-exporting them here would make drizzle-kit
5
+ * emit a duplicate `CREATE TABLE users`.
6
+ */
7
+ export { savedViews, savedViewDefaults, viewVisibility } from './saved-views';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/views/infrastructure/schema/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.viewVisibility = exports.savedViewDefaults = exports.savedViews = void 0;
4
+ /**
5
+ * The saved-views schema barrel — also the entry point `drizzle.config.ts`
6
+ * diffs. `external-refs.ts` is deliberately absent: those stubs exist only so
7
+ * the foreign keys resolve, and re-exporting them here would make drizzle-kit
8
+ * emit a duplicate `CREATE TABLE users`.
9
+ */
10
+ var saved_views_1 = require("./saved-views");
11
+ Object.defineProperty(exports, "savedViews", { enumerable: true, get: function () { return saved_views_1.savedViews; } });
12
+ Object.defineProperty(exports, "savedViewDefaults", { enumerable: true, get: function () { return saved_views_1.savedViewDefaults; } });
13
+ Object.defineProperty(exports, "viewVisibility", { enumerable: true, get: function () { return saved_views_1.viewVisibility; } });