@tekki-forge/runtime-api 0.1.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.
Files changed (74) hide show
  1. package/README.md +5 -0
  2. package/dist/access.d.ts +15 -0
  3. package/dist/access.js +51 -0
  4. package/dist/access.js.map +1 -0
  5. package/dist/api-keys.d.ts +140 -0
  6. package/dist/api-keys.js +361 -0
  7. package/dist/api-keys.js.map +1 -0
  8. package/dist/backup.d.ts +142 -0
  9. package/dist/backup.js +280 -0
  10. package/dist/backup.js.map +1 -0
  11. package/dist/bootstrap.d.ts +57 -0
  12. package/dist/bootstrap.js +191 -0
  13. package/dist/bootstrap.js.map +1 -0
  14. package/dist/comments.d.ts +139 -0
  15. package/dist/comments.js +370 -0
  16. package/dist/comments.js.map +1 -0
  17. package/dist/computed.d.ts +27 -0
  18. package/dist/computed.js +174 -0
  19. package/dist/computed.js.map +1 -0
  20. package/dist/controllers.d.ts +309 -0
  21. package/dist/controllers.js +634 -0
  22. package/dist/controllers.js.map +1 -0
  23. package/dist/dashboard.d.ts +58 -0
  24. package/dist/dashboard.js +258 -0
  25. package/dist/dashboard.js.map +1 -0
  26. package/dist/errors.d.ts +6 -0
  27. package/dist/errors.js +55 -0
  28. package/dist/errors.js.map +1 -0
  29. package/dist/events.d.ts +39 -0
  30. package/dist/events.js +36 -0
  31. package/dist/events.js.map +1 -0
  32. package/dist/extension.d.ts +103 -0
  33. package/dist/extension.js +8 -0
  34. package/dist/extension.js.map +1 -0
  35. package/dist/import.d.ts +69 -0
  36. package/dist/import.js +347 -0
  37. package/dist/import.js.map +1 -0
  38. package/dist/index.d.ts +22 -0
  39. package/dist/index.js +23 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/jobs.d.ts +41 -0
  42. package/dist/jobs.js +10 -0
  43. package/dist/jobs.js.map +1 -0
  44. package/dist/notifications.d.ts +61 -0
  45. package/dist/notifications.js +195 -0
  46. package/dist/notifications.js.map +1 -0
  47. package/dist/plugin.d.ts +43 -0
  48. package/dist/plugin.js +4 -0
  49. package/dist/plugin.js.map +1 -0
  50. package/dist/query.d.ts +29 -0
  51. package/dist/query.js +192 -0
  52. package/dist/query.js.map +1 -0
  53. package/dist/saved-views.d.ts +42 -0
  54. package/dist/saved-views.js +130 -0
  55. package/dist/saved-views.js.map +1 -0
  56. package/dist/search.d.ts +17 -0
  57. package/dist/search.js +42 -0
  58. package/dist/search.js.map +1 -0
  59. package/dist/seed.d.ts +12 -0
  60. package/dist/seed.js +132 -0
  61. package/dist/seed.js.map +1 -0
  62. package/dist/serialize.d.ts +37 -0
  63. package/dist/serialize.js +165 -0
  64. package/dist/serialize.js.map +1 -0
  65. package/dist/service.d.ts +168 -0
  66. package/dist/service.js +757 -0
  67. package/dist/service.js.map +1 -0
  68. package/dist/teams.d.ts +63 -0
  69. package/dist/teams.js +131 -0
  70. package/dist/teams.js.map +1 -0
  71. package/dist/testing.d.ts +63 -0
  72. package/dist/testing.js +420 -0
  73. package/dist/testing.js.map +1 -0
  74. package/package.json +61 -0
@@ -0,0 +1,130 @@
1
+ import { can } from "@tekki-forge/runtime-permissions";
2
+ import { BadRequestException, ForbiddenException, NotFoundException, UnauthorizedException } from "@nestjs/common";
3
+ import { QueryError, parseListQuery } from "./query.js";
4
+ const MAX_VIEWS_PER_RESOURCE = 100;
5
+ const MAX_KEYS = 30;
6
+ const DROP = new Set(["page", "pageSize"]);
7
+ /** Saved list filters: personal, or shared with the organization by an admin. */
8
+ export class SavedViewService {
9
+ service;
10
+ constructor(service) {
11
+ this.service = service;
12
+ }
13
+ get prisma() {
14
+ return this.service.prisma;
15
+ }
16
+ principal(ctx) {
17
+ if (!ctx.principal)
18
+ throw new UnauthorizedException("Sign in to continue");
19
+ return ctx.principal;
20
+ }
21
+ orgWhere(ctx) {
22
+ return this.service.app.tenancy.enabled ? { organizationId: ctx.principal?.organizationId ?? null } : {};
23
+ }
24
+ readableEntity(resource, ctx) {
25
+ if (typeof resource !== "string" || !resource)
26
+ throw new BadRequestException("resource is required");
27
+ const entity = this.service.entity(resource);
28
+ if (!can(entity, "read", ctx.principal))
29
+ throw new ForbiddenException(`You cannot view ${entity.pluralLabel.toLowerCase()}`);
30
+ return entity;
31
+ }
32
+ /** Keep only list parameters, and check they are valid for this caller (unknown or unreadable fields are rejected). */
33
+ sanitize(entity, query, ctx) {
34
+ if (!query || typeof query !== "object" || Array.isArray(query))
35
+ throw new BadRequestException("query must be an object");
36
+ const entries = Object.entries(query).filter(([k, v]) => !DROP.has(k) && v !== "" && v !== null && v !== undefined);
37
+ if (entries.length > MAX_KEYS)
38
+ throw new BadRequestException(`A view can hold at most ${MAX_KEYS} parameters`);
39
+ const out = {};
40
+ for (const [k, v] of entries) {
41
+ if (typeof v !== "string" && typeof v !== "number" && typeof v !== "boolean")
42
+ throw new BadRequestException(`${k} must be a string`);
43
+ const s = String(v);
44
+ if (k.length > 100 || s.length > 1000)
45
+ throw new BadRequestException(`${k} is too long`);
46
+ out[k] = s;
47
+ }
48
+ const { view, ...rest } = out;
49
+ if (view !== undefined && view !== "table" && !(view in entity.views))
50
+ throw new BadRequestException(`Unknown view "${view}"`);
51
+ try {
52
+ // `$me` stays symbolic in the stored query and resolves per viewer.
53
+ parseListQuery(entity, rest, this.service.queryAccess(entity, ctx));
54
+ }
55
+ catch (e) {
56
+ if (e instanceof QueryError)
57
+ throw new BadRequestException(e.message);
58
+ throw e;
59
+ }
60
+ return out;
61
+ }
62
+ out(v, ctx) {
63
+ return { id: v.id, resource: v.resource, name: v.name, query: v.query, shared: v.shared, mine: v.userId === ctx.principal?.id, createdAt: v.createdAt };
64
+ }
65
+ async list(resource, ctx) {
66
+ const p = this.principal(ctx);
67
+ const entity = this.readableEntity(resource, ctx);
68
+ const rows = await this.prisma.savedView.findMany({
69
+ where: { resource: entity.path, ...this.orgWhere(ctx), OR: [{ userId: p.id }, { shared: true }] },
70
+ orderBy: [{ shared: "asc" }, { name: "asc" }],
71
+ });
72
+ return { data: rows.map((r) => this.out(r, ctx)) };
73
+ }
74
+ name(v) {
75
+ const name = typeof v === "string" ? v.trim() : "";
76
+ if (!name)
77
+ throw new BadRequestException("Name is required");
78
+ if (name.length > 80)
79
+ throw new BadRequestException("Name must be at most 80 characters");
80
+ return name;
81
+ }
82
+ checkShare(shared, ctx) {
83
+ if (shared === undefined || shared === false)
84
+ return false;
85
+ if (shared !== true)
86
+ throw new BadRequestException("shared must be true or false");
87
+ if (!ctx.principal.roles.includes("admin"))
88
+ throw new ForbiddenException("Only admins can share views");
89
+ return true;
90
+ }
91
+ async create(body, ctx) {
92
+ const p = this.principal(ctx);
93
+ const entity = this.readableEntity(body?.resource, ctx);
94
+ const data = { name: this.name(body.name), query: this.sanitize(entity, body.query, ctx), shared: this.checkShare(body.shared, ctx) };
95
+ const count = await this.prisma.savedView.count({ where: { resource: entity.path, userId: p.id, ...this.orgWhere(ctx) } });
96
+ if (count >= MAX_VIEWS_PER_RESOURCE)
97
+ throw new BadRequestException(`You can save at most ${MAX_VIEWS_PER_RESOURCE} views per list`);
98
+ const row = await this.prisma.savedView.create({ data: { ...data, resource: entity.path, userId: p.id, ...this.orgWhere(ctx) } });
99
+ return this.out(row, ctx);
100
+ }
101
+ async find(id, ctx) {
102
+ const p = this.principal(ctx);
103
+ const row = /^[0-9a-f-]{36}$/i.test(id) ? await this.prisma.savedView.findFirst({ where: { id, ...this.orgWhere(ctx), OR: [{ userId: p.id }, { shared: true }] } }) : null;
104
+ if (!row)
105
+ throw new NotFoundException("View not found");
106
+ return row;
107
+ }
108
+ async update(id, body, ctx) {
109
+ const row = await this.find(id, ctx);
110
+ if (row.userId !== ctx.principal.id)
111
+ throw new ForbiddenException("Only the author can change this view");
112
+ const entity = this.readableEntity(row.resource, ctx);
113
+ const data = {};
114
+ if ("name" in (body ?? {}))
115
+ data.name = this.name(body.name);
116
+ if ("query" in (body ?? {}))
117
+ data.query = this.sanitize(entity, body.query, ctx);
118
+ if ("shared" in (body ?? {}))
119
+ data.shared = this.checkShare(body.shared, ctx);
120
+ return this.out(await this.prisma.savedView.update({ where: { id }, data }), ctx);
121
+ }
122
+ async remove(id, ctx) {
123
+ const row = await this.find(id, ctx);
124
+ const mine = row.userId === ctx.principal.id;
125
+ if (!mine && !(row.shared && ctx.principal.roles.includes("admin")))
126
+ throw new ForbiddenException("Only the author or an admin can delete this view");
127
+ await this.prisma.savedView.delete({ where: { id } });
128
+ }
129
+ }
130
+ //# sourceMappingURL=saved-views.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saved-views.js","sourceRoot":"","sources":["../src/saved-views.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { ForgeRequestContext } from "./extension.js";
2
+ import type { ForgeService } from "./service.js";
3
+ export interface SearchGroup {
4
+ entity: string;
5
+ path: string;
6
+ label: string;
7
+ icon: string;
8
+ results: {
9
+ id: string;
10
+ label: string;
11
+ href: string;
12
+ }[];
13
+ }
14
+ /** Global (⌘K) search across every entity the caller can read, on the fields they can read. */
15
+ export declare function globalSearch(service: ForgeService, query: Record<string, unknown>, ctx: ForgeRequestContext): Promise<{
16
+ data: SearchGroup[];
17
+ }>;
package/dist/search.js ADDED
@@ -0,0 +1,42 @@
1
+ import { authorize } from "@tekki-forge/runtime-permissions";
2
+ import { BadRequestException } from "@nestjs/common";
3
+ import { searchWhere } from "./query.js";
4
+ import { label } from "./serialize.js";
5
+ /** Global (⌘K) search across every entity the caller can read, on the fields they can read. */
6
+ export async function globalSearch(service, query, ctx) {
7
+ const q = typeof query.q === "string" ? query.q.trim() : "";
8
+ if (q.length > 100)
9
+ throw new BadRequestException("Search terms must be at most 100 characters");
10
+ if (!q || !ctx.principal)
11
+ return { data: [] };
12
+ const limit = Math.min(10, Math.max(1, Number(query.limit) || 5));
13
+ const groups = await Promise.all(service.app.entities.map(async (entity) => {
14
+ if (!authorize(entity, "read", ctx.principal).allowed)
15
+ return null;
16
+ if (entity.features.tenant && !ctx.principal?.organizationId)
17
+ return null;
18
+ const access = service.queryAccess(entity, ctx);
19
+ const where = searchWhere(entity, q, access);
20
+ if (!where)
21
+ return null;
22
+ // An unreadable display field (field-level permission) falls back to the id.
23
+ const display = entity.display !== "id" && (!access.queryable || access.queryable.has(entity.display)) ? entity.display : "id";
24
+ const rows = await service.delegate(entity).findMany({
25
+ where: { AND: [...service.readWhere(entity, ctx), where] },
26
+ select: { id: true, ...(display !== "id" ? { [display]: true } : {}) },
27
+ orderBy: entity.features.timestamps ? [{ updatedAt: "desc" }, { id: "asc" }] : [{ id: "asc" }],
28
+ take: limit,
29
+ });
30
+ if (!rows.length)
31
+ return null;
32
+ return {
33
+ entity: entity.name,
34
+ path: entity.path,
35
+ label: entity.pluralLabel,
36
+ icon: entity.icon,
37
+ results: rows.map((r) => ({ id: String(r.id), label: display === "id" ? String(r.id) : label(entity, r), href: `/${entity.path}/${String(r.id)}` })),
38
+ };
39
+ }));
40
+ return { data: groups.filter((g) => g !== null) };
41
+ }
42
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
package/dist/seed.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { AppIR } from "@tekki-forge/compiler";
2
+ /**
3
+ * Populate empty tables with a few realistic-looking rows so a fresh app is explorable.
4
+ * Idempotent: entities that already have rows (in the organization) are skipped.
5
+ */
6
+ export declare function seedDemo(options: {
7
+ app: AppIR;
8
+ prisma: any;
9
+ userId: string;
10
+ organizationId?: string | null;
11
+ count?: number;
12
+ }): Promise<Record<string, number>>;
package/dist/seed.js ADDED
@@ -0,0 +1,132 @@
1
+ import { ComputedEngine } from "./computed.js";
2
+ const WORDS = ["Aurora", "Beacon", "Cobalt", "Delta", "Ember", "Falcon", "Granite", "Harbor", "Juniper", "Keystone"];
3
+ const COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#0ea5e9"];
4
+ function sampleValue(entity, f, i) {
5
+ const clamp = (n) => Math.min(f.validation.max ?? Number.MAX_SAFE_INTEGER, Math.max(f.validation.min ?? 0, n));
6
+ const word = WORDS[i % WORDS.length];
7
+ switch (f.kind) {
8
+ case "string": {
9
+ const base = f.name === entity.display ? `${word} ${entity.label}` : `${word} ${f.label.toLowerCase()}`;
10
+ return `${base}${i >= WORDS.length ? ` ${i + 1}` : ""}`.slice(0, f.validation.maxLength ?? 255);
11
+ }
12
+ case "text":
13
+ case "richtext":
14
+ return `Sample ${f.label.toLowerCase()} for ${word}.`;
15
+ case "email":
16
+ return `${word.toLowerCase()}${i + 1}@example.com`;
17
+ case "url":
18
+ return `https://example.com/${word.toLowerCase()}`;
19
+ case "phone":
20
+ return `+1 555 010${i % 10}`;
21
+ case "color":
22
+ return COLORS[i % COLORS.length];
23
+ case "integer":
24
+ return clamp((i + 1) * 10);
25
+ case "percentage":
26
+ return clamp(((i + 1) * 20) % 100);
27
+ case "decimal":
28
+ case "money":
29
+ return clamp((i + 1) * 1250).toFixed(2);
30
+ case "boolean":
31
+ return i % 2 === 0;
32
+ case "date":
33
+ return new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), 1 + i * 3));
34
+ case "datetime":
35
+ return new Date(Date.now() + i * 86_400_000);
36
+ case "enum":
37
+ return f.enumValues[i % f.enumValues.length].value;
38
+ case "json":
39
+ return { sample: true };
40
+ default:
41
+ return null;
42
+ }
43
+ }
44
+ /** Order entities so that relation targets are created before the records referencing them. */
45
+ function dependencyOrder(entities) {
46
+ const byName = new Map(entities.map((e) => [e.name, e]));
47
+ const done = new Set();
48
+ const out = [];
49
+ const visit = (e, stack) => {
50
+ if (done.has(e.name) || stack.has(e.name))
51
+ return;
52
+ stack.add(e.name);
53
+ for (const f of e.fields) {
54
+ const target = f.relation && byName.get(f.relation.entity);
55
+ if (target && target !== e)
56
+ visit(target, stack);
57
+ }
58
+ done.add(e.name);
59
+ out.push(e);
60
+ };
61
+ for (const e of entities)
62
+ visit(e, new Set());
63
+ return out;
64
+ }
65
+ /**
66
+ * Populate empty tables with a few realistic-looking rows so a fresh app is explorable.
67
+ * Idempotent: entities that already have rows (in the organization) are skipped.
68
+ */
69
+ export async function seedDemo(options) {
70
+ const { app, prisma, userId, organizationId } = options;
71
+ const count = options.count ?? 6;
72
+ const created = new Map();
73
+ const engine = new ComputedEngine(app);
74
+ const summary = {};
75
+ // Plugin entities (webhook endpoints, subscriptions…) hold configuration, not demo content.
76
+ const pluginEntities = new Set(app.plugins.flatMap((p) => p.entities));
77
+ for (const entity of dependencyOrder(app.entities)) {
78
+ if (pluginEntities.has(entity.name))
79
+ continue;
80
+ const delegate = prisma[entity.delegate];
81
+ const scope = entity.features.tenant ? { organizationId } : {};
82
+ const existing = await delegate.findMany({ where: scope, select: { id: true }, take: 50 });
83
+ if (existing.length > 0) {
84
+ created.set(entity.name, existing.map((r) => r.id));
85
+ continue;
86
+ }
87
+ const ids = [];
88
+ for (let i = 0; i < count; i++) {
89
+ const data = { ...scope };
90
+ let skip = false;
91
+ for (const f of entity.fields) {
92
+ if (f.computed || f.file)
93
+ continue;
94
+ if (f.relation?.type === "many-to-many") {
95
+ const pool = f.relation.entity === "User" ? [userId] : (created.get(f.relation.entity) ?? []);
96
+ const pick = pool.filter((_, j) => (i + j) % 3 !== 0).slice(0, 3);
97
+ if (pick.length)
98
+ data[f.name] = { connect: pick.map((id) => ({ id })) };
99
+ continue;
100
+ }
101
+ if (f.kind === "location") {
102
+ // Scatter points around Dakar so the map view has something to show.
103
+ data[`${f.name}Lat`] = 14.6937 + ((i * 37) % 11) / 100 - 0.05;
104
+ data[`${f.name}Lng`] = -17.4441 + ((i * 53) % 13) / 100 - 0.06;
105
+ continue;
106
+ }
107
+ if (f.relation) {
108
+ const pool = f.relation.entity === "User" ? [userId] : f.relation.entity === "Organization" ? [organizationId] : created.get(f.relation.entity);
109
+ const value = pool?.length ? pool[i % pool.length] : undefined;
110
+ if (value)
111
+ data[f.relation.foreignKey] = value;
112
+ else if (f.required)
113
+ skip = true;
114
+ continue;
115
+ }
116
+ const v = sampleValue(entity, f, i);
117
+ if (v !== null && (f.required || i % 3 !== 2))
118
+ data[f.name] = v;
119
+ }
120
+ if (skip)
121
+ break;
122
+ const row = await delegate.create({ data });
123
+ const fresh = await engine.refresh(prisma, entity, row);
124
+ await engine.refreshParents(prisma, entity, null, fresh);
125
+ ids.push(row.id);
126
+ }
127
+ created.set(entity.name, ids);
128
+ summary[entity.name] = ids.length;
129
+ }
130
+ return summary;
131
+ }
132
+ //# sourceMappingURL=seed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.js","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { EntityIR, FieldIR } from "@tekki-forge/compiler";
2
+ type Row = Record<string, any>;
3
+ export declare const FILE_SELECT: {
4
+ readonly id: true;
5
+ readonly name: true;
6
+ readonly size: true;
7
+ readonly mimeType: true;
8
+ };
9
+ /** Prisma `include` selecting just enough of each related record / file to render it. */
10
+ export declare function relationInclude(entity: EntityIR): Row | undefined;
11
+ export declare function fileOut(prefix: string, file: Row | null | undefined): {
12
+ id: any;
13
+ name: any;
14
+ size: any;
15
+ mimeType: any;
16
+ url: string;
17
+ } | null;
18
+ /**
19
+ * Convert a Prisma record into the public API representation.
20
+ * `omit` names fields the caller may not read (field-level permissions).
21
+ */
22
+ export declare function serialize(entity: EntityIR, record: Row, options?: {
23
+ omit?: Set<string>;
24
+ apiPrefix?: string;
25
+ }): Row;
26
+ /** Convert validated API input into Prisma write data. */
27
+ export declare function toPrismaData(entity: EntityIR, input: Row, mode?: "create" | "update"): Row;
28
+ /** Ids referenced by a many-to-many field in Prisma write data (connect/set), or undefined when untouched. */
29
+ export declare function manyIds(data: Row, f: FieldIR): string[] | undefined;
30
+ export declare function label(entity: EntityIR, record: Row): string;
31
+ /** Field-level diff for the audit log (ids of relations/files, not their labels). */
32
+ export declare function diff(before: Row, after: Row): Record<string, {
33
+ from: unknown;
34
+ to: unknown;
35
+ }>;
36
+ export declare function toCsv(entity: EntityIR, rows: Row[], omit?: Set<string>): string;
37
+ export {};
@@ -0,0 +1,165 @@
1
+ function scalarOut(kind, value) {
2
+ if (value === null || value === undefined)
3
+ return null;
4
+ if (value instanceof Date)
5
+ return kind === "date" ? value.toISOString().slice(0, 10) : value.toISOString();
6
+ if (typeof value === "object" && value !== null && (kind === "decimal" || kind === "money"))
7
+ return value.toString();
8
+ return value;
9
+ }
10
+ const refSelect = (display) => (display !== "id" ? { id: true, [display]: true } : { id: true });
11
+ export const FILE_SELECT = { id: true, name: true, size: true, mimeType: true };
12
+ /** Prisma `include` selecting just enough of each related record / file to render it. */
13
+ export function relationInclude(entity) {
14
+ const include = {};
15
+ for (const f of entity.fields) {
16
+ if (f.hidden)
17
+ continue;
18
+ if (f.relation) {
19
+ const select = refSelect(f.relation.targetDisplay);
20
+ include[f.name] = f.relation.type === "many-to-many" ? { select, orderBy: { id: "asc" }, take: 200 } : { select };
21
+ }
22
+ else if (f.file)
23
+ include[f.name] = { select: FILE_SELECT };
24
+ }
25
+ return Object.keys(include).length ? include : undefined;
26
+ }
27
+ const ref = (f, rel) => ({ id: rel.id, label: String(rel[f.relation.targetDisplay] ?? rel.id) });
28
+ export function fileOut(prefix, file) {
29
+ if (!file)
30
+ return null;
31
+ return { id: file.id, name: file.name, size: file.size, mimeType: file.mimeType, url: `${prefix}/files/${file.id}` };
32
+ }
33
+ function writeField(out, f, record, apiPrefix) {
34
+ if (f.relation?.type === "many-to-many") {
35
+ const rels = record[f.name];
36
+ if (rels === undefined)
37
+ return;
38
+ out[f.apiName] = rels.map((r) => r.id);
39
+ out[f.name] = rels.map((r) => ref(f, r));
40
+ }
41
+ else if (f.relation) {
42
+ out[f.relation.foreignKey] = record[f.relation.foreignKey] ?? null;
43
+ const rel = record[f.name];
44
+ if (rel !== undefined)
45
+ out[f.name] = rel ? ref(f, rel) : null;
46
+ }
47
+ else if (f.kind === "location") {
48
+ const lat = record[`${f.name}Lat`];
49
+ const lng = record[`${f.name}Lng`];
50
+ out[f.name] = lat == null || lng == null ? null : { lat, lng };
51
+ }
52
+ else if (f.file) {
53
+ out[f.apiName] = record[f.apiName] ?? null;
54
+ if (record[f.name] !== undefined)
55
+ out[f.name] = fileOut(apiPrefix, record[f.name]);
56
+ }
57
+ else {
58
+ out[f.name] = scalarOut(f.kind, record[f.name]);
59
+ }
60
+ }
61
+ /**
62
+ * Convert a Prisma record into the public API representation.
63
+ * `omit` names fields the caller may not read (field-level permissions).
64
+ */
65
+ export function serialize(entity, record, options = {}) {
66
+ const out = { id: record.id };
67
+ for (const f of entity.fields) {
68
+ if (!f.hidden && !options.omit?.has(f.name))
69
+ writeField(out, f, record, options.apiPrefix ?? "/api");
70
+ }
71
+ if (entity.features.tenant)
72
+ out.organizationId = record.organizationId;
73
+ if (entity.features.timestamps) {
74
+ out.createdAt = scalarOut("datetime", record.createdAt);
75
+ out.updatedAt = scalarOut("datetime", record.updatedAt);
76
+ }
77
+ if (entity.features.softDelete)
78
+ out.deletedAt = scalarOut("datetime", record.deletedAt);
79
+ return out;
80
+ }
81
+ /** Convert validated API input into Prisma write data. */
82
+ export function toPrismaData(entity, input, mode = "update") {
83
+ const data = {};
84
+ for (const f of entity.fields) {
85
+ if (!(f.apiName in input))
86
+ continue;
87
+ const v = input[f.apiName];
88
+ if (f.relation?.type === "many-to-many") {
89
+ const ids = (v ?? []).map((id) => ({ id }));
90
+ data[f.name] = mode === "create" ? { connect: ids } : { set: ids };
91
+ }
92
+ else if (f.kind === "location") {
93
+ const loc = v;
94
+ data[`${f.name}Lat`] = loc?.lat ?? null;
95
+ data[`${f.name}Lng`] = loc?.lng ?? null;
96
+ }
97
+ else if (v !== null && (f.kind === "date" || f.kind === "datetime")) {
98
+ data[f.apiName] = f.kind === "date" ? new Date(`${String(v).slice(0, 10)}T00:00:00.000Z`) : new Date(String(v));
99
+ }
100
+ else if (f.kind === "json" && v === null) {
101
+ // Prisma distinguishes JSON null from database NULL; clearing a field means database NULL.
102
+ data[f.apiName] = { __forgeDbNull: true };
103
+ }
104
+ else
105
+ data[f.apiName] = v;
106
+ }
107
+ return data;
108
+ }
109
+ /** Ids referenced by a many-to-many field in Prisma write data (connect/set), or undefined when untouched. */
110
+ export function manyIds(data, f) {
111
+ const v = data[f.name];
112
+ if (!v)
113
+ return undefined;
114
+ return (v.connect ?? v.set ?? []).map((x) => x.id);
115
+ }
116
+ export function label(entity, record) {
117
+ const v = record[entity.display];
118
+ return v === null || v === undefined || v === "" ? String(record.id) : String(v);
119
+ }
120
+ const isRefList = (v) => Array.isArray(v) && v.some((x) => x && typeof x === "object" && "label" in x);
121
+ const isRef = (v) => !!v && typeof v === "object" && !Array.isArray(v) && ("label" in v || "url" in v);
122
+ /** Field-level diff for the audit log (ids of relations/files, not their labels). */
123
+ export function diff(before, after) {
124
+ const changes = {};
125
+ for (const key of Object.keys(after)) {
126
+ if (["updatedAt", "createdAt"].includes(key))
127
+ continue;
128
+ const b = after[key];
129
+ if (isRef(b) || isRefList(b) || isRef(before[key]) || isRefList(before[key]))
130
+ continue;
131
+ const a = before[key];
132
+ if (JSON.stringify(a) !== JSON.stringify(b))
133
+ changes[key] = { from: a ?? null, to: b ?? null };
134
+ }
135
+ return changes;
136
+ }
137
+ function cellText(v) {
138
+ if (Array.isArray(v))
139
+ return v.map((x) => (x && typeof x === "object" ? String(x.label ?? x.id) : String(x))).join("; ");
140
+ if (v && typeof v === "object") {
141
+ const o = v;
142
+ if ("label" in o)
143
+ return String(o.label);
144
+ if ("url" in o)
145
+ return String(o.name);
146
+ if ("lat" in o)
147
+ return `${o.lat},${o.lng}`;
148
+ return JSON.stringify(v);
149
+ }
150
+ return String(v);
151
+ }
152
+ export function toCsv(entity, rows, omit) {
153
+ const fields = entity.fields.filter((f) => !f.hidden && !omit?.has(f.name));
154
+ const columns = ["id", ...fields.map((f) => f.name), ...(entity.features.timestamps ? ["createdAt", "updatedAt"] : [])];
155
+ const escape = (v) => {
156
+ if (v === null || v === undefined)
157
+ return "";
158
+ const s = cellText(v);
159
+ // Neutralize spreadsheet formula injection.
160
+ const safe = typeof v === "string" && /^[=+\-@\t\r]/.test(s) ? `'${s}` : s;
161
+ return /[",\n\r]/.test(safe) ? `"${safe.replaceAll('"', '""')}"` : safe;
162
+ };
163
+ return [columns.join(","), ...rows.map((r) => columns.map((c) => escape(r[c])).join(","))].join("\n") + "\n";
164
+ }
165
+ //# sourceMappingURL=serialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"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"}