@cosmicdrift/kumiko-bundled-features 0.371.0 → 0.372.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-mfa/db/queries.d.ts +3 -2
- package/dist/auth-mfa/db/queries.js +2 -5
- package/dist/auth-mfa/schema/user-mfa.d.ts +10 -10
- package/dist/billing-foundation/entities.d.ts +10 -10
- package/dist/billing-foundation/projection.d.ts +10 -10
- package/dist/cap-counter/entity.d.ts +4 -4
- package/dist/cap-overview/web/cap-cards-panel.js +2 -1
- package/dist/compliance-profiles/schema/profile-selection.d.ts +4 -4
- package/dist/config/table.d.ts +6 -6
- package/dist/config/web/index.d.ts +1 -0
- package/dist/config/web/index.js +1 -0
- package/dist/crypto-shredding/handlers/forget-subject.write.js +55 -30
- package/dist/crypto-shredding/resolve-file-bytes-deleter.d.ts +3 -0
- package/dist/crypto-shredding/resolve-file-bytes-deleter.js +24 -0
- package/dist/custom-fields/entity.d.ts +9 -7
- package/dist/custom-fields/executor.d.ts +19 -1
- package/dist/data-retention/index.d.ts +1 -0
- package/dist/data-retention/index.js +1 -0
- package/dist/data-retention/run-retention-cleanup.js +27 -159
- package/dist/data-retention/schema/tenant-retention-override.d.ts +6 -6
- package/dist/delivery/delivery-service.d.ts +2 -1
- package/dist/delivery/delivery-service.js +3 -2
- package/dist/delivery/handlers/unsubscribe-page-chrome.query.js +2 -2
- package/dist/delivery/index.d.ts +1 -1
- package/dist/delivery/index.js +1 -1
- package/dist/delivery/jobs.js +21 -7
- package/dist/delivery/recipient-address-seal.d.ts +13 -0
- package/dist/delivery/recipient-address-seal.js +53 -0
- package/dist/delivery/tables.d.ts +8 -7
- package/dist/delivery/testing.d.ts +2 -1
- package/dist/delivery/testing.js +1 -0
- package/dist/delivery/unsubscribe.d.ts +13 -2
- package/dist/delivery/unsubscribe.js +9 -1
- package/dist/document-ingest-foundation/entity.d.ts +8 -8
- package/dist/document-ingest-foundation/executor.d.ts +16 -1
- package/dist/file-foundation/read-file-ref-content.js +2 -2
- package/dist/folders/entity.d.ts +8 -8
- package/dist/folders/executor.d.ts +29 -2
- package/dist/form-draft/entity.d.ts +3 -3
- package/dist/form-draft/executor.d.ts +15 -1
- package/dist/inbound-mail-foundation/entities.d.ts +31 -31
- package/dist/inbound-mail-foundation/projection.d.ts +24 -24
- package/dist/jobs/job-run-table.d.ts +10 -10
- package/dist/ledger/entity.d.ts +21 -21
- package/dist/ledger/executor.d.ts +57 -3
- package/dist/managed-pages/table.d.ts +16 -14
- package/dist/notes-history/entity.d.ts +14 -14
- package/dist/notes-history/executor.d.ts +31 -2
- package/dist/personal-access-tokens/schema/api-token.d.ts +9 -9
- package/dist/personal-access-tokens/screens.js +14 -9
- package/dist/secrets/table.d.ts +2 -2
- package/dist/sessions/schema/user-session.d.ts +8 -8
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/purge-files-reaching-subject.d.ts +25 -0
- package/dist/shared/purge-files-reaching-subject.js +146 -0
- package/dist/shared/purge-row-file-refs.d.ts +33 -0
- package/dist/shared/purge-row-file-refs.js +152 -0
- package/dist/tags/entity.d.ts +9 -9
- package/dist/tags/executor.d.ts +30 -2
- package/dist/template-resolver/handlers/shared.d.ts +28 -1
- package/dist/template-resolver/table.d.ts +32 -32
- package/dist/template-resolver/template-preview.js +13 -4
- package/dist/template-resolver/user-content-table.d.ts +16 -16
- package/dist/template-resolver/web/client-plugin.js +2 -1
- package/dist/template-resolver/web/document-editor-extensions.d.ts +1 -1
- package/dist/template-resolver/web/document-editor-extensions.js +2 -5
- package/dist/template-resolver/web/placeholder-line-editor.js +2 -2
- package/dist/tenant/handlers/list.query.d.ts +21 -1
- package/dist/tenant/invitation-table.d.ts +14 -14
- package/dist/tenant/membership-table.d.ts +4 -4
- package/dist/tenant/schema/tenant.d.ts +20 -18
- package/dist/tier-engine/entity.d.ts +2 -2
- package/dist/tier-engine/tier-resolver.d.ts +2 -2
- package/dist/user/handlers/me.query.d.ts +21 -0
- package/dist/user/schema/user.d.ts +24 -22
- package/dist/user-data-rights/run-export-jobs.d.ts +20 -1
- package/dist/user-data-rights/run-forget-cleanup.js +35 -1
- package/dist/user-data-rights/schema/download-attempt.d.ts +16 -16
- package/dist/user-data-rights/schema/download-token.d.ts +16 -16
- package/dist/user-data-rights/schema/export-job.d.ts +20 -20
- package/dist/user-data-rights/web/privacy-center-screen.js +2 -1
- package/dist/waitlist/entity.d.ts +24 -24
- package/dist/waitlist/lib.d.ts +24 -1
- package/package.json +11 -11
- package/src/crypto-shredding/changes.json +6 -0
- package/src/data-retention/changes.json +6 -0
- package/src/delivery/changes.json +13 -0
- package/src/files/changes.json +7 -0
- package/src/personal-access-tokens/changes.json +12 -0
- package/src/template-resolver/changes.json +6 -0
- package/src/user-data-rights/changes.json +6 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { fileRefsTable } from "@cosmicdrift/kumiko-framework/files";
|
|
3
|
+
import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
4
|
+
const NO_FILE_FIELDS = { singleFields: [], multiFields: [] };
|
|
5
|
+
// Same set as boot-validator/entity-handler.ts FILE_FIELD_TYPES; the single/multi split
|
|
6
|
+
// matters here because only plural fields resolve through file_refs.
|
|
7
|
+
export function planFileFields(fields, fieldNames) {
|
|
8
|
+
const singleFields = [];
|
|
9
|
+
const multiFields = [];
|
|
10
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
11
|
+
if (fieldNames !== undefined && !fieldNames.includes(name))
|
|
12
|
+
continue;
|
|
13
|
+
if (field.type === "file" || field.type === "image")
|
|
14
|
+
singleFields.push(name);
|
|
15
|
+
else if (field.type === "files" || field.type === "images")
|
|
16
|
+
multiFields.push(name);
|
|
17
|
+
}
|
|
18
|
+
return singleFields.length > 0 || multiFields.length > 0
|
|
19
|
+
? { singleFields, multiFields }
|
|
20
|
+
: NO_FILE_FIELDS;
|
|
21
|
+
}
|
|
22
|
+
// Every implicit entity table with file/image fields: an unbound fileRef
|
|
23
|
+
// (POST /files without entityType/entityId) can sit in single fields of
|
|
24
|
+
// several entity types, so the sharing check must look beyond the purged
|
|
25
|
+
// row's own table.
|
|
26
|
+
export function collectSingleFileHolders(registry) {
|
|
27
|
+
const holders = [];
|
|
28
|
+
for (const proj of registry.getAllProjections().values()) {
|
|
29
|
+
if (proj.isImplicit !== true || typeof proj.source !== "string" || !proj.table)
|
|
30
|
+
continue;
|
|
31
|
+
const entity = registry.getEntity(proj.source);
|
|
32
|
+
if (!entity)
|
|
33
|
+
continue;
|
|
34
|
+
const { singleFields } = planFileFields(entity.fields);
|
|
35
|
+
if (singleFields.length === 0)
|
|
36
|
+
continue;
|
|
37
|
+
holders.push({
|
|
38
|
+
table: proj.table,
|
|
39
|
+
tableHasTenantId: propertyOf(proj.table, "tenantId") !== undefined,
|
|
40
|
+
singleFields,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return holders;
|
|
44
|
+
}
|
|
45
|
+
// Every fileRef-id a row's file-fields reference: single fields hold the id in
|
|
46
|
+
// their own column, plural fields resolve via file_refs (tenantId, entityType,
|
|
47
|
+
// entityId, fieldName).
|
|
48
|
+
async function collectFileRefIds(db, tenantId, entityName, row, plan) {
|
|
49
|
+
const ids = new Set();
|
|
50
|
+
for (const field of plan.singleFields) {
|
|
51
|
+
const value = row[field];
|
|
52
|
+
if (typeof value === "string" && value.length > 0)
|
|
53
|
+
ids.add(value);
|
|
54
|
+
}
|
|
55
|
+
if (plan.multiFields.length > 0) {
|
|
56
|
+
const refRows = await selectMany(db, fileRefsTable, {
|
|
57
|
+
tenantId,
|
|
58
|
+
entityType: entityName,
|
|
59
|
+
entityId: String(row["id"]),
|
|
60
|
+
fieldName: { in: plan.multiFields },
|
|
61
|
+
});
|
|
62
|
+
for (const r of refRows)
|
|
63
|
+
ids.add(r.id);
|
|
64
|
+
}
|
|
65
|
+
return Array.from(ids);
|
|
66
|
+
}
|
|
67
|
+
// A fileRef is bound to a DIFFERENT entity/record (or, when only some fields are
|
|
68
|
+
// purged, to another field of this row) than the one being purged — its binding,
|
|
69
|
+
// not the row's own single-field column, decides ownership.
|
|
70
|
+
function isFileRefBoundElsewhere(fileRef, entityName, rowId, fieldNames) {
|
|
71
|
+
const entityType = fileRef["entityType"];
|
|
72
|
+
const entityId = fileRef["entityId"];
|
|
73
|
+
const fieldName = fileRef["fieldName"];
|
|
74
|
+
if (typeof entityType === "string" && entityType !== entityName)
|
|
75
|
+
return true;
|
|
76
|
+
if (typeof entityId === "string" && entityId !== rowId)
|
|
77
|
+
return true;
|
|
78
|
+
if (fieldNames !== undefined &&
|
|
79
|
+
typeof fieldName === "string" &&
|
|
80
|
+
!fieldNames.includes(fieldName)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
// A single-field fileRef-id can be shared by more than one row, in the SAME
|
|
86
|
+
// table or in another entity's table; with a partial field set, also by another
|
|
87
|
+
// field of the same row. Purging its bytes would corrupt the holder that stays.
|
|
88
|
+
async function isFileRefSharedByOtherHolder(db, holders, ownTable, tenantId, rowId, fileRefId, fieldNames) {
|
|
89
|
+
for (const holder of holders) {
|
|
90
|
+
for (const field of holder.singleFields) {
|
|
91
|
+
const where = { [field]: fileRefId };
|
|
92
|
+
if (holder.table === ownTable) {
|
|
93
|
+
const fieldIsPurged = fieldNames === undefined || fieldNames.includes(field);
|
|
94
|
+
where["id"] = fieldIsPurged ? { ne: rowId } : rowId;
|
|
95
|
+
}
|
|
96
|
+
if (holder.tableHasTenantId)
|
|
97
|
+
where["tenantId"] = tenantId;
|
|
98
|
+
const others = await selectMany(db, holder.table, where, { limit: 1 });
|
|
99
|
+
if (others.length > 0)
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
// Deletes the un-shared fileRefs a row's file-fields reference: bytes (incl.
|
|
106
|
+
// derivatives) first, then the fileRef rows — bytes before rows so a crash
|
|
107
|
+
// mid-way leaves an orphaned fileRef pointing at already-gone bytes (a later
|
|
108
|
+
// run's delete is idempotent) rather than a gone fileRef whose bytes leak forever.
|
|
109
|
+
// The entity row itself is the caller's business. `fieldNames` narrows the purge
|
|
110
|
+
// to those fields; without it every file-field of the entity is purged.
|
|
111
|
+
export async function purgeRowFileRefs(args) {
|
|
112
|
+
const rowId = String(args.row["id"]);
|
|
113
|
+
const plan = planFileFields(args.entity.fields, args.fieldNames);
|
|
114
|
+
const fileRefIds = await collectFileRefIds(args.db, args.tenantId, args.entityName, args.row, plan);
|
|
115
|
+
if (fileRefIds.length === 0)
|
|
116
|
+
return "purged";
|
|
117
|
+
const fileRefRows = await selectMany(args.db, fileRefsTable, {
|
|
118
|
+
id: { in: fileRefIds },
|
|
119
|
+
tenantId: args.tenantId,
|
|
120
|
+
});
|
|
121
|
+
const toDelete = [];
|
|
122
|
+
for (const fileRef of fileRefRows) {
|
|
123
|
+
if (isFileRefBoundElsewhere(fileRef, args.entityName, rowId, args.fieldNames))
|
|
124
|
+
continue;
|
|
125
|
+
const shared = await isFileRefSharedByOtherHolder(args.db, args.fileHolders, args.table, args.tenantId, rowId, String(fileRef["id"]), args.fieldNames);
|
|
126
|
+
if (shared)
|
|
127
|
+
continue;
|
|
128
|
+
toDelete.push(fileRef);
|
|
129
|
+
}
|
|
130
|
+
if (toDelete.length === 0)
|
|
131
|
+
return "purged";
|
|
132
|
+
if (!args.deleteBytes)
|
|
133
|
+
return "missing_file_storage";
|
|
134
|
+
let anyFailed = false;
|
|
135
|
+
for (const fileRef of toDelete) {
|
|
136
|
+
const key = fileRef["storageKey"];
|
|
137
|
+
if (typeof key !== "string" || key.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
const failedKeys = await args.deleteBytes(key);
|
|
140
|
+
if (failedKeys.length > 0) {
|
|
141
|
+
anyFailed = true;
|
|
142
|
+
args.onBytesDeleteFailed?.(failedKeys);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (anyFailed)
|
|
146
|
+
return "file_delete_failed";
|
|
147
|
+
for (const fileRef of toDelete) {
|
|
148
|
+
if (!(await args.fileRefForget(String(fileRef["id"]))))
|
|
149
|
+
return "file_delete_failed";
|
|
150
|
+
}
|
|
151
|
+
return "purged";
|
|
152
|
+
}
|
package/dist/tags/entity.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type EntityDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
export declare const tagEntity: EntityDefinition<{
|
|
3
|
-
name: {
|
|
3
|
+
readonly name: {
|
|
4
4
|
readonly type: "text";
|
|
5
5
|
readonly description?: string;
|
|
6
6
|
readonly maxLength?: number;
|
|
@@ -23,7 +23,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
23
23
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
24
24
|
required: true;
|
|
25
25
|
};
|
|
26
|
-
color: {
|
|
26
|
+
readonly color: {
|
|
27
27
|
readonly type: "text";
|
|
28
28
|
readonly description?: string;
|
|
29
29
|
readonly maxLength?: number;
|
|
@@ -46,7 +46,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
46
46
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
47
47
|
required: false;
|
|
48
48
|
};
|
|
49
|
-
scope: {
|
|
49
|
+
readonly scope: {
|
|
50
50
|
readonly type: "text";
|
|
51
51
|
readonly description?: string;
|
|
52
52
|
readonly maxLength?: number;
|
|
@@ -71,7 +71,7 @@ export declare const tagEntity: EntityDefinition<{
|
|
|
71
71
|
};
|
|
72
72
|
}, "tenant">;
|
|
73
73
|
export declare function createTagAssignmentEntity(access?: EntityDefinition["access"], parents?: readonly string[]): EntityDefinition<{
|
|
74
|
-
tagId: {
|
|
74
|
+
readonly tagId: {
|
|
75
75
|
readonly type: "text";
|
|
76
76
|
readonly description?: string;
|
|
77
77
|
readonly maxLength?: number;
|
|
@@ -94,7 +94,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
94
94
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
95
95
|
required: true;
|
|
96
96
|
};
|
|
97
|
-
entityType: {
|
|
97
|
+
readonly entityType: {
|
|
98
98
|
readonly type: "text";
|
|
99
99
|
readonly description?: string;
|
|
100
100
|
readonly maxLength?: number;
|
|
@@ -117,7 +117,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
117
117
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
118
118
|
required: true;
|
|
119
119
|
};
|
|
120
|
-
entityId: {
|
|
120
|
+
readonly entityId: {
|
|
121
121
|
readonly type: "text";
|
|
122
122
|
readonly description?: string;
|
|
123
123
|
readonly maxLength?: number;
|
|
@@ -142,7 +142,7 @@ export declare function createTagAssignmentEntity(access?: EntityDefinition["acc
|
|
|
142
142
|
};
|
|
143
143
|
}, "tenant">;
|
|
144
144
|
export declare const tagAssignmentEntity: EntityDefinition<{
|
|
145
|
-
tagId: {
|
|
145
|
+
readonly tagId: {
|
|
146
146
|
readonly type: "text";
|
|
147
147
|
readonly description?: string;
|
|
148
148
|
readonly maxLength?: number;
|
|
@@ -165,7 +165,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
165
165
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
166
166
|
required: true;
|
|
167
167
|
};
|
|
168
|
-
entityType: {
|
|
168
|
+
readonly entityType: {
|
|
169
169
|
readonly type: "text";
|
|
170
170
|
readonly description?: string;
|
|
171
171
|
readonly maxLength?: number;
|
|
@@ -188,7 +188,7 @@ export declare const tagAssignmentEntity: EntityDefinition<{
|
|
|
188
188
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
189
189
|
required: true;
|
|
190
190
|
};
|
|
191
|
-
entityId: {
|
|
191
|
+
readonly entityId: {
|
|
192
192
|
readonly type: "text";
|
|
193
193
|
readonly description?: string;
|
|
194
194
|
readonly maxLength?: number;
|
package/dist/tags/executor.d.ts
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
import { type EntityDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
-
export declare const tagExecutor: import("@cosmicdrift/kumiko-types/event-store-executor-types").EventStoreExecutor
|
|
3
|
-
|
|
2
|
+
export declare const tagExecutor: import("@cosmicdrift/kumiko-types/event-store-executor-types").EventStoreExecutor<{
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly tenantId: string;
|
|
5
|
+
readonly version: number;
|
|
6
|
+
readonly insertedAt: Temporal.Instant;
|
|
7
|
+
readonly modifiedAt: Temporal.Instant | null;
|
|
8
|
+
readonly insertedById: string | null;
|
|
9
|
+
readonly modifiedById: string | null;
|
|
10
|
+
readonly isDeleted?: boolean | undefined;
|
|
11
|
+
readonly deletedAt?: Temporal.Instant | null | undefined;
|
|
12
|
+
readonly deletedById?: string | null | undefined;
|
|
13
|
+
readonly color: string | null;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly scope: string | null;
|
|
16
|
+
}>;
|
|
17
|
+
export declare const tagAssignmentExecutor: import("@cosmicdrift/kumiko-types/event-store-executor-types").EventStoreExecutor<{
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly tenantId: string;
|
|
20
|
+
readonly version: number;
|
|
21
|
+
readonly insertedAt: Temporal.Instant;
|
|
22
|
+
readonly modifiedAt: Temporal.Instant | null;
|
|
23
|
+
readonly insertedById: string | null;
|
|
24
|
+
readonly modifiedById: string | null;
|
|
25
|
+
readonly isDeleted?: boolean | undefined;
|
|
26
|
+
readonly deletedAt?: Temporal.Instant | null | undefined;
|
|
27
|
+
readonly deletedById?: string | null | undefined;
|
|
28
|
+
readonly entityId: string;
|
|
29
|
+
readonly entityType: string;
|
|
30
|
+
readonly tagId: string;
|
|
31
|
+
}>;
|
|
4
32
|
export declare function createTagAssignmentExecutor(ownership: EntityDefinition["access"] | undefined): typeof tagAssignmentExecutor;
|
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { templateSlugSchema } from "../template-meta.ts";
|
|
3
|
-
export declare const executor: import("@cosmicdrift/kumiko-types/event-store-executor-types").EventStoreExecutor
|
|
3
|
+
export declare const executor: import("@cosmicdrift/kumiko-types/event-store-executor-types").EventStoreExecutor<{
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly tenantId: string;
|
|
6
|
+
readonly version: number;
|
|
7
|
+
readonly insertedAt: Temporal.Instant;
|
|
8
|
+
readonly modifiedAt: Temporal.Instant | null;
|
|
9
|
+
readonly insertedById: string | null;
|
|
10
|
+
readonly modifiedById: string | null;
|
|
11
|
+
readonly isDeleted?: boolean | undefined;
|
|
12
|
+
readonly deletedAt?: Temporal.Instant | null | undefined;
|
|
13
|
+
readonly deletedById?: string | null | undefined;
|
|
14
|
+
readonly activeSince: Temporal.Instant | null;
|
|
15
|
+
readonly activeVersion: number | null;
|
|
16
|
+
readonly content: string | null;
|
|
17
|
+
readonly contentFormat: string;
|
|
18
|
+
readonly draft: string | null;
|
|
19
|
+
readonly folder: string | null;
|
|
20
|
+
readonly kind: string;
|
|
21
|
+
readonly linkedResources: string | null;
|
|
22
|
+
readonly locale: string;
|
|
23
|
+
readonly meta: string | null;
|
|
24
|
+
readonly parentTemplateId: string | null;
|
|
25
|
+
readonly scope: string;
|
|
26
|
+
readonly slug: string;
|
|
27
|
+
readonly status: string;
|
|
28
|
+
readonly title: string | null;
|
|
29
|
+
readonly variableSchema: string | null;
|
|
30
|
+
}>;
|
|
4
31
|
export declare const slugSchema: z.ZodString;
|
|
5
32
|
export { templateSlugSchema };
|
|
6
33
|
export declare const localeSchema: z.ZodString;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
2
2
|
export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/fields").EntityDefinition<{
|
|
3
|
-
slug: {
|
|
3
|
+
readonly slug: {
|
|
4
4
|
readonly type: "text";
|
|
5
5
|
readonly description?: string;
|
|
6
6
|
readonly maxLength?: number;
|
|
@@ -23,7 +23,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
23
23
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
24
24
|
required: true;
|
|
25
25
|
};
|
|
26
|
-
kind: {
|
|
26
|
+
readonly kind: {
|
|
27
27
|
readonly type: "select";
|
|
28
28
|
readonly description?: string;
|
|
29
29
|
readonly options: readonly ["mail", "document-pdf", "image-snapshot", "notification", "text-block", "snippet", "ai-prompt", "mail-layout", "pdf-layout"];
|
|
@@ -52,7 +52,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
52
52
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
53
53
|
required: true;
|
|
54
54
|
};
|
|
55
|
-
locale: {
|
|
55
|
+
readonly locale: {
|
|
56
56
|
readonly type: "text";
|
|
57
57
|
readonly description?: string;
|
|
58
58
|
readonly maxLength?: number;
|
|
@@ -75,7 +75,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
75
75
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
76
76
|
required: true;
|
|
77
77
|
};
|
|
78
|
-
title: {
|
|
78
|
+
readonly title: {
|
|
79
79
|
readonly type: "text";
|
|
80
80
|
readonly description?: string;
|
|
81
81
|
readonly maxLength?: number;
|
|
@@ -98,7 +98,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
98
98
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
99
99
|
required: false;
|
|
100
100
|
};
|
|
101
|
-
folder: {
|
|
101
|
+
readonly folder: {
|
|
102
102
|
readonly type: "text";
|
|
103
103
|
readonly description?: string;
|
|
104
104
|
readonly maxLength?: number;
|
|
@@ -121,7 +121,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
121
121
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
122
122
|
required: false;
|
|
123
123
|
};
|
|
124
|
-
content: {
|
|
124
|
+
readonly content: {
|
|
125
125
|
readonly type: "longText";
|
|
126
126
|
readonly description?: string;
|
|
127
127
|
readonly maxLength?: number;
|
|
@@ -137,7 +137,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
137
137
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
138
138
|
required: false;
|
|
139
139
|
};
|
|
140
|
-
contentFormat: {
|
|
140
|
+
readonly contentFormat: {
|
|
141
141
|
readonly type: "select";
|
|
142
142
|
readonly description?: string;
|
|
143
143
|
readonly options: readonly ["markdown", "mjml", "html", "plain"];
|
|
@@ -161,7 +161,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
161
161
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
162
162
|
required: true;
|
|
163
163
|
};
|
|
164
|
-
variableSchema: {
|
|
164
|
+
readonly variableSchema: {
|
|
165
165
|
readonly type: "longText";
|
|
166
166
|
readonly description?: string;
|
|
167
167
|
readonly maxLength?: number;
|
|
@@ -177,7 +177,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
177
177
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
178
178
|
required: false;
|
|
179
179
|
};
|
|
180
|
-
linkedResources: {
|
|
180
|
+
readonly linkedResources: {
|
|
181
181
|
readonly type: "longText";
|
|
182
182
|
readonly description?: string;
|
|
183
183
|
readonly maxLength?: number;
|
|
@@ -193,7 +193,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
193
193
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
194
194
|
required: false;
|
|
195
195
|
};
|
|
196
|
-
meta: {
|
|
196
|
+
readonly meta: {
|
|
197
197
|
readonly type: "longText";
|
|
198
198
|
readonly description?: string;
|
|
199
199
|
readonly maxLength?: number;
|
|
@@ -209,7 +209,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
209
209
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
210
210
|
required: false;
|
|
211
211
|
};
|
|
212
|
-
scope: {
|
|
212
|
+
readonly scope: {
|
|
213
213
|
readonly type: "select";
|
|
214
214
|
readonly description?: string;
|
|
215
215
|
readonly options: readonly ["system", "tenant"];
|
|
@@ -231,7 +231,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
231
231
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
232
232
|
required: true;
|
|
233
233
|
};
|
|
234
|
-
parentTemplateId: {
|
|
234
|
+
readonly parentTemplateId: {
|
|
235
235
|
readonly type: "text";
|
|
236
236
|
readonly description?: string;
|
|
237
237
|
readonly maxLength?: number;
|
|
@@ -254,7 +254,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
254
254
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
255
255
|
required: false;
|
|
256
256
|
};
|
|
257
|
-
status: {
|
|
257
|
+
readonly status: {
|
|
258
258
|
readonly type: "select";
|
|
259
259
|
readonly description?: string;
|
|
260
260
|
readonly options: readonly ["draft", "active", "archived"];
|
|
@@ -277,7 +277,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
277
277
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
278
278
|
required: true;
|
|
279
279
|
};
|
|
280
|
-
draft: {
|
|
280
|
+
readonly draft: {
|
|
281
281
|
readonly type: "longText";
|
|
282
282
|
readonly description?: string;
|
|
283
283
|
readonly maxLength?: number;
|
|
@@ -293,7 +293,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
293
293
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
294
294
|
required: false;
|
|
295
295
|
};
|
|
296
|
-
activeVersion: {
|
|
296
|
+
readonly activeVersion: {
|
|
297
297
|
readonly type: "number";
|
|
298
298
|
readonly description?: string;
|
|
299
299
|
readonly required?: boolean;
|
|
@@ -312,7 +312,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
312
312
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
313
313
|
required: false;
|
|
314
314
|
};
|
|
315
|
-
activeSince: {
|
|
315
|
+
readonly activeSince: {
|
|
316
316
|
readonly type: "timestamp";
|
|
317
317
|
readonly description?: string;
|
|
318
318
|
readonly required?: boolean;
|
|
@@ -329,7 +329,7 @@ export declare const templateResourceEntity: import("@cosmicdrift/kumiko-types/f
|
|
|
329
329
|
};
|
|
330
330
|
}, "tenant">;
|
|
331
331
|
export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framework/db").EntityTable<import("@cosmicdrift/kumiko-types/fields").EntityDefinition<{
|
|
332
|
-
slug: {
|
|
332
|
+
readonly slug: {
|
|
333
333
|
readonly type: "text";
|
|
334
334
|
readonly description?: string;
|
|
335
335
|
readonly maxLength?: number;
|
|
@@ -352,7 +352,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
352
352
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
353
353
|
required: true;
|
|
354
354
|
};
|
|
355
|
-
kind: {
|
|
355
|
+
readonly kind: {
|
|
356
356
|
readonly type: "select";
|
|
357
357
|
readonly description?: string;
|
|
358
358
|
readonly options: readonly ["mail", "document-pdf", "image-snapshot", "notification", "text-block", "snippet", "ai-prompt", "mail-layout", "pdf-layout"];
|
|
@@ -381,7 +381,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
381
381
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
382
382
|
required: true;
|
|
383
383
|
};
|
|
384
|
-
locale: {
|
|
384
|
+
readonly locale: {
|
|
385
385
|
readonly type: "text";
|
|
386
386
|
readonly description?: string;
|
|
387
387
|
readonly maxLength?: number;
|
|
@@ -404,7 +404,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
404
404
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
405
405
|
required: true;
|
|
406
406
|
};
|
|
407
|
-
title: {
|
|
407
|
+
readonly title: {
|
|
408
408
|
readonly type: "text";
|
|
409
409
|
readonly description?: string;
|
|
410
410
|
readonly maxLength?: number;
|
|
@@ -427,7 +427,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
427
427
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
428
428
|
required: false;
|
|
429
429
|
};
|
|
430
|
-
folder: {
|
|
430
|
+
readonly folder: {
|
|
431
431
|
readonly type: "text";
|
|
432
432
|
readonly description?: string;
|
|
433
433
|
readonly maxLength?: number;
|
|
@@ -450,7 +450,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
450
450
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
451
451
|
required: false;
|
|
452
452
|
};
|
|
453
|
-
content: {
|
|
453
|
+
readonly content: {
|
|
454
454
|
readonly type: "longText";
|
|
455
455
|
readonly description?: string;
|
|
456
456
|
readonly maxLength?: number;
|
|
@@ -466,7 +466,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
466
466
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
467
467
|
required: false;
|
|
468
468
|
};
|
|
469
|
-
contentFormat: {
|
|
469
|
+
readonly contentFormat: {
|
|
470
470
|
readonly type: "select";
|
|
471
471
|
readonly description?: string;
|
|
472
472
|
readonly options: readonly ["markdown", "mjml", "html", "plain"];
|
|
@@ -490,7 +490,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
490
490
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
491
491
|
required: true;
|
|
492
492
|
};
|
|
493
|
-
variableSchema: {
|
|
493
|
+
readonly variableSchema: {
|
|
494
494
|
readonly type: "longText";
|
|
495
495
|
readonly description?: string;
|
|
496
496
|
readonly maxLength?: number;
|
|
@@ -506,7 +506,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
506
506
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
507
507
|
required: false;
|
|
508
508
|
};
|
|
509
|
-
linkedResources: {
|
|
509
|
+
readonly linkedResources: {
|
|
510
510
|
readonly type: "longText";
|
|
511
511
|
readonly description?: string;
|
|
512
512
|
readonly maxLength?: number;
|
|
@@ -522,7 +522,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
522
522
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
523
523
|
required: false;
|
|
524
524
|
};
|
|
525
|
-
meta: {
|
|
525
|
+
readonly meta: {
|
|
526
526
|
readonly type: "longText";
|
|
527
527
|
readonly description?: string;
|
|
528
528
|
readonly maxLength?: number;
|
|
@@ -538,7 +538,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
538
538
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
539
539
|
required: false;
|
|
540
540
|
};
|
|
541
|
-
scope: {
|
|
541
|
+
readonly scope: {
|
|
542
542
|
readonly type: "select";
|
|
543
543
|
readonly description?: string;
|
|
544
544
|
readonly options: readonly ["system", "tenant"];
|
|
@@ -560,7 +560,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
560
560
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
561
561
|
required: true;
|
|
562
562
|
};
|
|
563
|
-
parentTemplateId: {
|
|
563
|
+
readonly parentTemplateId: {
|
|
564
564
|
readonly type: "text";
|
|
565
565
|
readonly description?: string;
|
|
566
566
|
readonly maxLength?: number;
|
|
@@ -583,7 +583,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
583
583
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
584
584
|
required: false;
|
|
585
585
|
};
|
|
586
|
-
status: {
|
|
586
|
+
readonly status: {
|
|
587
587
|
readonly type: "select";
|
|
588
588
|
readonly description?: string;
|
|
589
589
|
readonly options: readonly ["draft", "active", "archived"];
|
|
@@ -606,7 +606,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
606
606
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
607
607
|
required: true;
|
|
608
608
|
};
|
|
609
|
-
draft: {
|
|
609
|
+
readonly draft: {
|
|
610
610
|
readonly type: "longText";
|
|
611
611
|
readonly description?: string;
|
|
612
612
|
readonly maxLength?: number;
|
|
@@ -622,7 +622,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
622
622
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
623
623
|
required: false;
|
|
624
624
|
};
|
|
625
|
-
activeVersion: {
|
|
625
|
+
readonly activeVersion: {
|
|
626
626
|
readonly type: "number";
|
|
627
627
|
readonly description?: string;
|
|
628
628
|
readonly required?: boolean;
|
|
@@ -641,7 +641,7 @@ export declare const templateResourcesTable: import("@cosmicdrift/kumiko-framewo
|
|
|
641
641
|
} & import("@cosmicdrift/kumiko-types/fields").ResolvedPiiFlags & {
|
|
642
642
|
required: false;
|
|
643
643
|
};
|
|
644
|
-
activeSince: {
|
|
644
|
+
readonly activeSince: {
|
|
645
645
|
readonly type: "timestamp";
|
|
646
646
|
readonly description?: string;
|
|
647
647
|
readonly required?: boolean;
|
|
@@ -20,9 +20,15 @@ import { isMailTemplateMeta, parseTemplateMeta, readPdfTemplateMeta, } from "./t
|
|
|
20
20
|
const SAMPLE_LINK = "https://example.com";
|
|
21
21
|
const SAMPLE_DATE = "2026-01-15";
|
|
22
22
|
const SAMPLE_LIST_ROWS = 3;
|
|
23
|
+
function isTranslatableExample(def, example) {
|
|
24
|
+
return (def.type === "text" || def.type === "richText") && typeof example === "string";
|
|
25
|
+
}
|
|
26
|
+
function exampleFor(def, example, labelText) {
|
|
27
|
+
return isTranslatableExample(def, example) ? labelText(example) : example;
|
|
28
|
+
}
|
|
23
29
|
function sampleScalar(name, def, labelText) {
|
|
24
30
|
if (def.example !== undefined)
|
|
25
|
-
return def.example;
|
|
31
|
+
return exampleFor(def, def.example, labelText);
|
|
26
32
|
const label = def.label === undefined ? name : labelText(def.label);
|
|
27
33
|
switch (def.type) {
|
|
28
34
|
case "text":
|
|
@@ -41,9 +47,12 @@ function sampleScalar(name, def, labelText) {
|
|
|
41
47
|
}
|
|
42
48
|
function sampleRow(item, labelText, given = {}) {
|
|
43
49
|
const row = {};
|
|
44
|
-
for (const [name, def] of Object.entries(item))
|
|
45
|
-
row[name] =
|
|
46
|
-
|
|
50
|
+
for (const [name, def] of Object.entries(item)) {
|
|
51
|
+
row[name] = Object.hasOwn(given, name)
|
|
52
|
+
? exampleFor(def, given[name], labelText)
|
|
53
|
+
: sampleScalar(name, def, labelText);
|
|
54
|
+
}
|
|
55
|
+
return { ...given, ...row };
|
|
47
56
|
}
|
|
48
57
|
function sampleList(def, labelText) {
|
|
49
58
|
const rows = (def.example ?? [])
|