@reaatech/prompt-version-control-server 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 (136) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +261 -0
  4. package/dist/api/routes/deployments.d.ts +4 -0
  5. package/dist/api/routes/deployments.d.ts.map +1 -0
  6. package/dist/api/routes/deployments.js +48 -0
  7. package/dist/api/routes/deployments.js.map +1 -0
  8. package/dist/api/routes/docs.d.ts +4 -0
  9. package/dist/api/routes/docs.d.ts.map +1 -0
  10. package/dist/api/routes/docs.js +75 -0
  11. package/dist/api/routes/docs.js.map +1 -0
  12. package/dist/api/routes/evaluations.d.ts +4 -0
  13. package/dist/api/routes/evaluations.d.ts.map +1 -0
  14. package/dist/api/routes/evaluations.js +63 -0
  15. package/dist/api/routes/evaluations.js.map +1 -0
  16. package/dist/api/routes/metrics.d.ts +4 -0
  17. package/dist/api/routes/metrics.d.ts.map +1 -0
  18. package/dist/api/routes/metrics.js +38 -0
  19. package/dist/api/routes/metrics.js.map +1 -0
  20. package/dist/api/routes/promotions.d.ts +4 -0
  21. package/dist/api/routes/promotions.d.ts.map +1 -0
  22. package/dist/api/routes/promotions.js +95 -0
  23. package/dist/api/routes/promotions.js.map +1 -0
  24. package/dist/api/routes/prompts.d.ts +4 -0
  25. package/dist/api/routes/prompts.d.ts.map +1 -0
  26. package/dist/api/routes/prompts.js +80 -0
  27. package/dist/api/routes/prompts.js.map +1 -0
  28. package/dist/api/routes/render.d.ts +4 -0
  29. package/dist/api/routes/render.d.ts.map +1 -0
  30. package/dist/api/routes/render.js +44 -0
  31. package/dist/api/routes/render.js.map +1 -0
  32. package/dist/api/routes/tags.d.ts +4 -0
  33. package/dist/api/routes/tags.d.ts.map +1 -0
  34. package/dist/api/routes/tags.js +41 -0
  35. package/dist/api/routes/tags.js.map +1 -0
  36. package/dist/api/routes/webhooks.d.ts +4 -0
  37. package/dist/api/routes/webhooks.d.ts.map +1 -0
  38. package/dist/api/routes/webhooks.js +49 -0
  39. package/dist/api/routes/webhooks.js.map +1 -0
  40. package/dist/db/client.d.ts +3 -0
  41. package/dist/db/client.d.ts.map +1 -0
  42. package/dist/db/client.js +21 -0
  43. package/dist/db/client.js.map +1 -0
  44. package/dist/db/redis.d.ts +3 -0
  45. package/dist/db/redis.d.ts.map +1 -0
  46. package/dist/db/redis.js +18 -0
  47. package/dist/db/redis.js.map +1 -0
  48. package/dist/errors.d.ts +22 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +39 -0
  51. package/dist/errors.js.map +1 -0
  52. package/dist/index.d.ts +4 -0
  53. package/dist/index.d.ts.map +1 -0
  54. package/dist/index.js +83 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/middleware/audit.d.ts +3 -0
  57. package/dist/middleware/audit.d.ts.map +1 -0
  58. package/dist/middleware/audit.js +39 -0
  59. package/dist/middleware/audit.js.map +1 -0
  60. package/dist/middleware/auth.d.ts +3 -0
  61. package/dist/middleware/auth.d.ts.map +1 -0
  62. package/dist/middleware/auth.js +49 -0
  63. package/dist/middleware/auth.js.map +1 -0
  64. package/dist/middleware/error-handler.d.ts +3 -0
  65. package/dist/middleware/error-handler.d.ts.map +1 -0
  66. package/dist/middleware/error-handler.js +36 -0
  67. package/dist/middleware/error-handler.js.map +1 -0
  68. package/dist/middleware/metrics.d.ts +3 -0
  69. package/dist/middleware/metrics.d.ts.map +1 -0
  70. package/dist/middleware/metrics.js +13 -0
  71. package/dist/middleware/metrics.js.map +1 -0
  72. package/dist/middleware/rate-limit.d.ts +8 -0
  73. package/dist/middleware/rate-limit.d.ts.map +1 -0
  74. package/dist/middleware/rate-limit.js +89 -0
  75. package/dist/middleware/rate-limit.js.map +1 -0
  76. package/dist/middleware/request-id.d.ts +3 -0
  77. package/dist/middleware/request-id.d.ts.map +1 -0
  78. package/dist/middleware/request-id.js +10 -0
  79. package/dist/middleware/request-id.js.map +1 -0
  80. package/dist/services/audit.service.d.ts +6 -0
  81. package/dist/services/audit.service.d.ts.map +1 -0
  82. package/dist/services/audit.service.js +26 -0
  83. package/dist/services/audit.service.js.map +1 -0
  84. package/dist/services/deployment.service.d.ts +101 -0
  85. package/dist/services/deployment.service.d.ts.map +1 -0
  86. package/dist/services/deployment.service.js +127 -0
  87. package/dist/services/deployment.service.js.map +1 -0
  88. package/dist/services/diff.engine.d.ts +13 -0
  89. package/dist/services/diff.engine.d.ts.map +1 -0
  90. package/dist/services/diff.engine.js +101 -0
  91. package/dist/services/diff.engine.js.map +1 -0
  92. package/dist/services/eval.service.d.ts +55 -0
  93. package/dist/services/eval.service.d.ts.map +1 -0
  94. package/dist/services/eval.service.js +151 -0
  95. package/dist/services/eval.service.js.map +1 -0
  96. package/dist/services/event.bus.d.ts +18 -0
  97. package/dist/services/event.bus.d.ts.map +1 -0
  98. package/dist/services/event.bus.js +35 -0
  99. package/dist/services/event.bus.js.map +1 -0
  100. package/dist/services/metric.service.d.ts +33 -0
  101. package/dist/services/metric.service.d.ts.map +1 -0
  102. package/dist/services/metric.service.js +64 -0
  103. package/dist/services/metric.service.js.map +1 -0
  104. package/dist/services/prometheus.service.d.ts +7 -0
  105. package/dist/services/prometheus.service.d.ts.map +1 -0
  106. package/dist/services/prometheus.service.js +27 -0
  107. package/dist/services/prometheus.service.js.map +1 -0
  108. package/dist/services/prompt.service.d.ts +59 -0
  109. package/dist/services/prompt.service.d.ts.map +1 -0
  110. package/dist/services/prompt.service.js +144 -0
  111. package/dist/services/prompt.service.js.map +1 -0
  112. package/dist/services/tag.service.d.ts +46 -0
  113. package/dist/services/tag.service.d.ts.map +1 -0
  114. package/dist/services/tag.service.js +84 -0
  115. package/dist/services/tag.service.js.map +1 -0
  116. package/dist/services/webhook.service.d.ts +50 -0
  117. package/dist/services/webhook.service.d.ts.map +1 -0
  118. package/dist/services/webhook.service.js +196 -0
  119. package/dist/services/webhook.service.js.map +1 -0
  120. package/dist/types/hono.d.ts +10 -0
  121. package/dist/types/hono.d.ts.map +1 -0
  122. package/dist/types/hono.js +2 -0
  123. package/dist/types/hono.js.map +1 -0
  124. package/dist/utils/context.d.ts +3 -0
  125. package/dist/utils/context.d.ts.map +1 -0
  126. package/dist/utils/context.js +9 -0
  127. package/dist/utils/context.js.map +1 -0
  128. package/dist/utils/logger.d.ts +3 -0
  129. package/dist/utils/logger.d.ts.map +1 -0
  130. package/dist/utils/logger.js +20 -0
  131. package/dist/utils/logger.js.map +1 -0
  132. package/dist/utils/pagination.d.ts +10 -0
  133. package/dist/utils/pagination.d.ts.map +1 -0
  134. package/dist/utils/pagination.js +10 -0
  135. package/dist/utils/pagination.js.map +1 -0
  136. package/package.json +74 -0
@@ -0,0 +1,144 @@
1
+ import { calculateChecksum } from '@reaatech/prompt-version-control-shared';
2
+ import { prisma } from '../db/client.js';
3
+ import { ConflictError, NotFoundError } from '../errors.js';
4
+ import { paginateResult } from '../utils/pagination.js';
5
+ import { promptVersionsCreated } from './prometheus.service.js';
6
+ export class PromptService {
7
+ async createPrompt(projectId, data) {
8
+ const existing = await prisma.prompt.findFirst({
9
+ where: { projectId, name: data.name },
10
+ });
11
+ if (existing) {
12
+ throw new ConflictError('Prompt name already exists in this project');
13
+ }
14
+ const prompt = await prisma.prompt.create({
15
+ data: {
16
+ ...data,
17
+ projectId,
18
+ variables: data.variables ?? {},
19
+ metadata: data.metadata,
20
+ },
21
+ });
22
+ return prompt;
23
+ }
24
+ async getPrompt(projectId, id) {
25
+ const prompt = await prisma.prompt.findFirst({
26
+ where: { id, projectId },
27
+ });
28
+ if (!prompt) {
29
+ throw new NotFoundError('Prompt', id);
30
+ }
31
+ return prompt;
32
+ }
33
+ async listPrompts(projectId, opts) {
34
+ const prompts = await prisma.prompt.findMany({
35
+ where: { projectId, archived: false },
36
+ take: opts.limit + 1,
37
+ cursor: opts.cursor ? { id: opts.cursor } : undefined,
38
+ orderBy: { createdAt: 'desc' },
39
+ });
40
+ return paginateResult(prompts, opts.limit);
41
+ }
42
+ async updatePrompt(projectId, id, data) {
43
+ await this.getPrompt(projectId, id);
44
+ return prisma.prompt.update({
45
+ where: { id },
46
+ data: data,
47
+ });
48
+ }
49
+ async archivePrompt(projectId, id) {
50
+ await this.getPrompt(projectId, id);
51
+ const prompt = await prisma.prompt.update({
52
+ where: { id },
53
+ data: { archived: true },
54
+ });
55
+ return prompt;
56
+ }
57
+ /**
58
+ * Create a new version. Wrapped in a transaction with a unique-violation
59
+ * retry loop so concurrent creates against the same prompt don't both win
60
+ * the same version number.
61
+ */
62
+ async createVersion(projectId, promptId, data) {
63
+ const prompt = await prisma.prompt.findFirst({
64
+ where: { id: promptId, projectId },
65
+ select: { id: true, projectId: true },
66
+ });
67
+ if (!prompt) {
68
+ throw new NotFoundError('Prompt', promptId);
69
+ }
70
+ const checksum = calculateChecksum(data.content);
71
+ const MAX_ATTEMPTS = 5;
72
+ let lastError;
73
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
74
+ const maxVersion = await prisma.version.findFirst({
75
+ where: { promptId },
76
+ orderBy: { number: 'desc' },
77
+ select: { number: true },
78
+ });
79
+ const nextNumber = (maxVersion?.number ?? 0) + 1;
80
+ try {
81
+ const version = await prisma.version.create({
82
+ data: {
83
+ promptId,
84
+ number: nextNumber,
85
+ content: data.content,
86
+ template: data.template,
87
+ variables: data.variables ?? {},
88
+ checksum,
89
+ metadata: data.metadata,
90
+ },
91
+ });
92
+ promptVersionsCreated.inc({ project: prompt.projectId });
93
+ return version;
94
+ }
95
+ catch (err) {
96
+ // P2002 = Prisma unique constraint violation. Another concurrent create
97
+ // claimed our number — recompute and retry.
98
+ if (err.code === 'P2002') {
99
+ lastError = err;
100
+ continue;
101
+ }
102
+ throw err;
103
+ }
104
+ }
105
+ throw lastError ?? new Error('Failed to allocate version number');
106
+ }
107
+ async getVersion(projectId, promptId, versionId) {
108
+ const version = await prisma.version.findFirst({
109
+ where: { id: versionId, promptId, prompt: { projectId } },
110
+ });
111
+ if (!version) {
112
+ throw new NotFoundError('Version', versionId);
113
+ }
114
+ return version;
115
+ }
116
+ async getVersionByNumber(projectId, promptId, number) {
117
+ const version = await prisma.version.findFirst({
118
+ where: { promptId, number, prompt: { projectId } },
119
+ });
120
+ if (!version) {
121
+ throw new NotFoundError('Version', String(number));
122
+ }
123
+ return version;
124
+ }
125
+ async listVersions(projectId, promptId, opts) {
126
+ // Confirm caller owns the prompt before paging through its versions.
127
+ const prompt = await prisma.prompt.findFirst({
128
+ where: { id: promptId, projectId },
129
+ select: { id: true },
130
+ });
131
+ if (!prompt) {
132
+ throw new NotFoundError('Prompt', promptId);
133
+ }
134
+ const versions = await prisma.version.findMany({
135
+ where: { promptId },
136
+ take: opts.limit + 1,
137
+ cursor: opts.cursor ? { id: opts.cursor } : undefined,
138
+ orderBy: { number: 'desc' },
139
+ });
140
+ return paginateResult(versions, opts.limit);
141
+ }
142
+ }
143
+ export const promptService = new PromptService();
144
+ //# sourceMappingURL=prompt.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.service.js","sourceRoot":"","sources":["../../src/services/prompt.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { Tag } from '@prisma/client';
2
+ import type { TagName } from '@reaatech/prompt-version-control-shared';
3
+ export declare class TagService {
4
+ listTags(projectId: string, promptId: string): Promise<({
5
+ version: {
6
+ number: number;
7
+ id: string;
8
+ createdAt: Date;
9
+ template: string;
10
+ variables: import("@prisma/client/runtime/library").JsonValue;
11
+ metadata: import("@prisma/client/runtime/library").JsonValue | null;
12
+ promptId: string;
13
+ content: string;
14
+ checksum: string;
15
+ };
16
+ } & {
17
+ id: string;
18
+ projectId: string;
19
+ name: string;
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
+ promptId: string;
23
+ versionId: string;
24
+ })[]>;
25
+ getTag(projectId: string, promptId: string, name: TagName): Promise<Tag & {
26
+ version: {
27
+ number: number;
28
+ id: string;
29
+ };
30
+ }>;
31
+ moveTag(projectId: string, promptId: string, name: TagName, versionId: string): Promise<Tag>;
32
+ removeTag(projectId: string, promptId: string, name: TagName): Promise<void>;
33
+ getProductionVersion(projectId: string, promptId: string): Promise<{
34
+ number: number;
35
+ id: string;
36
+ createdAt: Date;
37
+ template: string;
38
+ variables: import("@prisma/client/runtime/library").JsonValue;
39
+ metadata: import("@prisma/client/runtime/library").JsonValue | null;
40
+ promptId: string;
41
+ content: string;
42
+ checksum: string;
43
+ }>;
44
+ }
45
+ export declare const tagService: TagService;
46
+ //# sourceMappingURL=tag.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.service.d.ts","sourceRoot":"","sources":["../../src/services/tag.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yCAAyC,CAAC;AAYvE,qBAAa,UAAU;IACf,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;IAS5C,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,GAAG,GAAG;QAAE,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAavD,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC;IAkCT,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5E,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;CAY/D;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { prisma } from '../db/client.js';
2
+ import { NotFoundError } from '../errors.js';
3
+ async function assertPromptInProject(projectId, promptId) {
4
+ const prompt = await prisma.prompt.findFirst({
5
+ where: { id: promptId, projectId },
6
+ select: { id: true },
7
+ });
8
+ if (!prompt)
9
+ throw new NotFoundError('Prompt', promptId);
10
+ }
11
+ export class TagService {
12
+ async listTags(projectId, promptId) {
13
+ await assertPromptInProject(projectId, promptId);
14
+ return prisma.tag.findMany({
15
+ where: { promptId, projectId },
16
+ include: { version: true },
17
+ orderBy: { name: 'asc' },
18
+ });
19
+ }
20
+ async getTag(projectId, promptId, name) {
21
+ const tag = await prisma.tag.findFirst({
22
+ where: { promptId, projectId, name },
23
+ include: { version: { select: { id: true, number: true } } },
24
+ });
25
+ if (!tag) {
26
+ throw new NotFoundError('Tag', `${promptId}/${name}`);
27
+ }
28
+ return tag;
29
+ }
30
+ async moveTag(projectId, promptId, name, versionId) {
31
+ await assertPromptInProject(projectId, promptId);
32
+ // Verify the version exists, belongs to this prompt, and is in this project.
33
+ const version = await prisma.version.findFirst({
34
+ where: { id: versionId, promptId, prompt: { projectId } },
35
+ select: { id: true },
36
+ });
37
+ if (!version) {
38
+ throw new NotFoundError('Version', versionId);
39
+ }
40
+ const tag = await prisma.tag.upsert({
41
+ where: {
42
+ promptId_name: {
43
+ promptId,
44
+ name,
45
+ },
46
+ },
47
+ update: {
48
+ versionId,
49
+ projectId,
50
+ },
51
+ create: {
52
+ projectId,
53
+ promptId,
54
+ versionId,
55
+ name,
56
+ },
57
+ });
58
+ return tag;
59
+ }
60
+ async removeTag(projectId, promptId, name) {
61
+ const tag = await prisma.tag.findFirst({
62
+ where: { promptId, projectId, name },
63
+ select: { id: true },
64
+ });
65
+ if (!tag) {
66
+ throw new NotFoundError('Tag', `${promptId}/${name}`);
67
+ }
68
+ await prisma.tag.delete({
69
+ where: { id: tag.id },
70
+ });
71
+ }
72
+ async getProductionVersion(projectId, promptId) {
73
+ const tag = await prisma.tag.findFirst({
74
+ where: { promptId, projectId, name: 'production' },
75
+ include: { version: true },
76
+ });
77
+ if (!tag) {
78
+ throw new NotFoundError('Production version', promptId);
79
+ }
80
+ return tag.version;
81
+ }
82
+ }
83
+ export const tagService = new TagService();
84
+ //# sourceMappingURL=tag.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tag.service.js","sourceRoot":"","sources":["../../src/services/tag.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { type EventType } from './event.bus.js';
2
+ /**
3
+ * Reject URLs that point at private/loopback/link-local addresses to prevent
4
+ * SSRF. Set WEBHOOK_ALLOW_PRIVATE=1 to disable (only safe in trusted dev envs).
5
+ */
6
+ declare function assertPublicUrl(rawUrl: string): Promise<URL>;
7
+ declare function isPrivateAddress(addr: string): boolean;
8
+ export declare class WebhookService {
9
+ private retryDelays;
10
+ constructor();
11
+ createSubscription(projectId: string, data: {
12
+ url: string;
13
+ events: EventType[];
14
+ secret: string;
15
+ }): Promise<{
16
+ id: string;
17
+ projectId: string;
18
+ createdAt: Date;
19
+ updatedAt: Date;
20
+ active: boolean;
21
+ url: string;
22
+ events: string[];
23
+ secret: string;
24
+ }>;
25
+ listSubscriptions(projectId: string): Promise<{
26
+ id: string;
27
+ projectId: string;
28
+ createdAt: Date;
29
+ updatedAt: Date;
30
+ active: boolean;
31
+ url: string;
32
+ events: string[];
33
+ secret: string;
34
+ }[]>;
35
+ deleteSubscription(projectId: string, id: string): Promise<import("@prisma/client").Prisma.BatchPayload>;
36
+ testDelivery(projectId: string, id: string): Promise<{
37
+ success: boolean;
38
+ status?: number;
39
+ error?: string;
40
+ }>;
41
+ private deliver;
42
+ private send;
43
+ }
44
+ export declare const webhookService: WebhookService;
45
+ export declare const __test__: {
46
+ assertPublicUrl: typeof assertPublicUrl;
47
+ isPrivateAddress: typeof isPrivateAddress;
48
+ };
49
+ export {};
50
+ //# sourceMappingURL=webhook.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.service.d.ts","sourceRoot":"","sources":["../../src/services/webhook.service.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiB,KAAK,SAAS,EAAY,MAAM,gBAAgB,CAAC;AAKzE;;;GAGG;AACH,iBAAe,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAiC3D;AAED,iBAAS,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA6B/C;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAkC;;IAW/C,kBAAkB,CACtB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,SAAS,EAAE,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;KAChB;;;;;;;;;;IAeG,iBAAiB,CAAC,SAAS,EAAE,MAAM;;;;;;;;;;IAMnC,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM;IAMhD,YAAY,CAChB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;YAiBnD,OAAO;YAwBP,IAAI;CAqCnB;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC;AAEnD,eAAO,MAAM,QAAQ;;;CAAwC,CAAC"}
@@ -0,0 +1,196 @@
1
+ import { createHmac } from 'node:crypto';
2
+ import { lookup } from 'node:dns/promises';
3
+ import { isIP } from 'node:net';
4
+ import { sleep } from '@reaatech/prompt-version-control-shared';
5
+ import { prisma } from '../db/client.js';
6
+ import { logger } from '../utils/logger.js';
7
+ import { eventBus } from './event.bus.js';
8
+ const WEBHOOK_TIMEOUT_MS = 10_000;
9
+ const ALLOW_PRIVATE = process.env.WEBHOOK_ALLOW_PRIVATE === '1';
10
+ /**
11
+ * Reject URLs that point at private/loopback/link-local addresses to prevent
12
+ * SSRF. Set WEBHOOK_ALLOW_PRIVATE=1 to disable (only safe in trusted dev envs).
13
+ */
14
+ async function assertPublicUrl(rawUrl) {
15
+ let url;
16
+ try {
17
+ url = new URL(rawUrl);
18
+ }
19
+ catch {
20
+ throw new Error(`invalid webhook url: ${rawUrl}`);
21
+ }
22
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
23
+ throw new Error(`webhook url must be http(s): ${url.protocol}`);
24
+ }
25
+ if (ALLOW_PRIVATE)
26
+ return url;
27
+ const host = url.hostname.replace(/^\[|\]$/g, '');
28
+ const candidates = [];
29
+ if (isIP(host)) {
30
+ candidates.push(host);
31
+ }
32
+ else {
33
+ try {
34
+ const records = await lookup(host, { all: true });
35
+ for (const r of records)
36
+ candidates.push(r.address);
37
+ }
38
+ catch {
39
+ throw new Error(`could not resolve webhook host: ${host}`);
40
+ }
41
+ }
42
+ for (const addr of candidates) {
43
+ if (isPrivateAddress(addr)) {
44
+ throw new Error(`webhook url resolves to disallowed address: ${addr}`);
45
+ }
46
+ }
47
+ return url;
48
+ }
49
+ function isPrivateAddress(addr) {
50
+ if (isIP(addr) === 4) {
51
+ const parts = addr.split('.').map(Number);
52
+ const [a, b] = parts;
53
+ if (a === undefined || b === undefined)
54
+ return true;
55
+ if (a === 10)
56
+ return true;
57
+ if (a === 127)
58
+ return true;
59
+ if (a === 0)
60
+ return true;
61
+ if (a === 169 && b === 254)
62
+ return true; // link-local & metadata
63
+ if (a === 172 && b >= 16 && b <= 31)
64
+ return true;
65
+ if (a === 192 && b === 168)
66
+ return true;
67
+ if (a >= 224)
68
+ return true; // multicast/reserved
69
+ return false;
70
+ }
71
+ if (isIP(addr) === 6) {
72
+ const lower = addr.toLowerCase();
73
+ if (lower === '::1')
74
+ return true;
75
+ if (lower === '::')
76
+ return true;
77
+ if (lower.startsWith('fc') || lower.startsWith('fd'))
78
+ return true; // ULA
79
+ if (lower.startsWith('fe80'))
80
+ return true; // link-local
81
+ if (lower.startsWith('ff'))
82
+ return true; // multicast
83
+ if (lower.startsWith('::ffff:')) {
84
+ // IPv4-mapped — recurse on the v4 part
85
+ const v4 = lower.slice('::ffff:'.length);
86
+ return isPrivateAddress(v4);
87
+ }
88
+ return false;
89
+ }
90
+ return true;
91
+ }
92
+ export class WebhookService {
93
+ retryDelays = [1_000, 5_000, 15_000, 60_000]; // ms
94
+ constructor() {
95
+ eventBus.on('version.created', (e) => this.deliver(e));
96
+ eventBus.on('tag.moved', (e) => this.deliver(e));
97
+ eventBus.on('eval.completed', (e) => this.deliver(e));
98
+ eventBus.on('promotion.requested', (e) => this.deliver(e));
99
+ eventBus.on('promotion.approved', (e) => this.deliver(e));
100
+ eventBus.on('promotion.rejected', (e) => this.deliver(e));
101
+ }
102
+ async createSubscription(projectId, data) {
103
+ // Validate destination at creation time so users get immediate feedback.
104
+ await assertPublicUrl(data.url);
105
+ return prisma.webhookSubscription.create({
106
+ data: {
107
+ projectId,
108
+ url: data.url,
109
+ events: data.events,
110
+ secret: data.secret,
111
+ active: true,
112
+ },
113
+ });
114
+ }
115
+ async listSubscriptions(projectId) {
116
+ return prisma.webhookSubscription.findMany({
117
+ where: { projectId },
118
+ });
119
+ }
120
+ async deleteSubscription(projectId, id) {
121
+ return prisma.webhookSubscription.deleteMany({
122
+ where: { id, projectId },
123
+ });
124
+ }
125
+ async testDelivery(projectId, id) {
126
+ const sub = await prisma.webhookSubscription.findFirst({
127
+ where: { id, projectId },
128
+ });
129
+ if (!sub)
130
+ return { success: false, error: 'Subscription not found' };
131
+ const event = {
132
+ id: `test_${Date.now()}`,
133
+ type: 'version.created',
134
+ projectId,
135
+ payload: { test: true },
136
+ createdAt: new Date(),
137
+ };
138
+ return this.send(sub.url, sub.secret, event);
139
+ }
140
+ async deliver(event) {
141
+ const subs = await prisma.webhookSubscription.findMany({
142
+ where: {
143
+ projectId: event.projectId,
144
+ active: true,
145
+ events: { has: event.type },
146
+ },
147
+ });
148
+ // Deliver in parallel; per-subscription retries shouldn't block other subs.
149
+ await Promise.all(subs.map(async (sub) => {
150
+ for (let attempt = 0; attempt <= this.retryDelays.length; attempt++) {
151
+ const result = await this.send(sub.url, sub.secret, event);
152
+ if (result.success)
153
+ return;
154
+ if (attempt < this.retryDelays.length) {
155
+ // biome-ignore lint/style/noNonNullAssertion: safe — guarded by length check
156
+ await sleep(this.retryDelays[attempt]);
157
+ }
158
+ }
159
+ }));
160
+ }
161
+ async send(url, secret, event) {
162
+ try {
163
+ await assertPublicUrl(url);
164
+ }
165
+ catch (err) {
166
+ logger.warn({ err: err.message, url }, 'webhook destination rejected');
167
+ return { success: false, error: err.message };
168
+ }
169
+ try {
170
+ const payload = JSON.stringify(event);
171
+ const signature = createHmac('sha256', secret).update(payload).digest('hex');
172
+ const res = await fetch(url, {
173
+ method: 'POST',
174
+ headers: {
175
+ 'Content-Type': 'application/json',
176
+ 'X-Webhook-Signature': `sha256=${signature}`,
177
+ 'X-Event-Id': event.id,
178
+ 'X-Event-Type': event.type,
179
+ },
180
+ body: payload,
181
+ signal: AbortSignal.timeout(WEBHOOK_TIMEOUT_MS),
182
+ });
183
+ if (res.ok) {
184
+ return { success: true, status: res.status };
185
+ }
186
+ return { success: false, status: res.status };
187
+ }
188
+ catch (err) {
189
+ logger.warn({ err, url, eventId: event.id }, 'webhook delivery failed');
190
+ return { success: false, error: err.message };
191
+ }
192
+ }
193
+ }
194
+ export const webhookService = new WebhookService();
195
+ export const __test__ = { assertPublicUrl, isPrivateAddress };
196
+ //# sourceMappingURL=webhook.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.service.js","sourceRoot":"","sources":["../../src/services/webhook.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,yCAAyC,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAiC,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEzE,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC;AAEhE;;;GAGG;AACH,KAAK,UAAU,eAAe,CAAC,MAAc;IAC3C,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,aAAa;QAAE,OAAO,GAAG,CAAC;IAE9B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACf,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,KAAK,MAAM,CAAC,IAAI,OAAO;gBAAE,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,+CAA+C,IAAI,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACpD,IAAI,CAAC,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,CAAC,wBAAwB;QACjE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QACjD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG;YAAE,OAAO,IAAI,CAAC,CAAC,qBAAqB;QAChD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;QACjC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAChC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,MAAM;QACzE,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,aAAa;QACxD,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,YAAY;QACrD,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,uCAAuC;YACvC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACzC,OAAO,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,cAAc;IACjB,WAAW,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK;IAE3D;QACE,QAAQ,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACjD,QAAQ,CAAC,EAAE,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,QAAQ,CAAC,EAAE,CAAC,qBAAqB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,QAAQ,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,QAAQ,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,kBAAkB,CACtB,SAAiB,EACjB,IAIC;QAED,yEAAyE;QACzE,MAAM,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC;YACvC,IAAI,EAAE;gBACJ,SAAS;gBACT,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,MAAM,EAAE,IAAI;aACb;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,SAAiB;QACvC,OAAO,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC;YACzC,KAAK,EAAE,EAAE,SAAS,EAAE;SACrB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,SAAiB,EAAE,EAAU;QACpD,OAAO,MAAM,CAAC,mBAAmB,CAAC,UAAU,CAAC;YAC3C,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;SACzB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,SAAiB,EACjB,EAAU;QAEV,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,SAAS,CAAC;YACrD,KAAK,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE;SACzB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC;QAErE,MAAM,KAAK,GAAa;YACtB,EAAE,EAAE,QAAQ,IAAI,CAAC,GAAG,EAAE,EAAE;YACxB,IAAI,EAAE,iBAAiB;YACvB,SAAS;YACT,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;YACvB,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC;QAEF,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,KAAe;QACnC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC;YACrD,KAAK,EAAE;gBACL,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE;aAC5B;SACF,CAAC,CAAC;QAEH,4EAA4E;QAC5E,MAAM,OAAO,CAAC,GAAG,CACf,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACrB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;gBACpE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBAC3D,IAAI,MAAM,CAAC,OAAO;oBAAE,OAAO;gBAC3B,IAAI,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;oBACtC,6EAA6E;oBAC7E,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAE,CAAC,CAAC;gBAC1C,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,IAAI,CAChB,GAAW,EACX,MAAc,EACd,KAAe;QAEf,IAAI,CAAC;YACH,MAAM,eAAe,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAG,GAAa,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,8BAA8B,CAAC,CAAC;YAClF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACtC,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE7E,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,qBAAqB,EAAE,UAAU,SAAS,EAAE;oBAC5C,YAAY,EAAE,KAAK,CAAC,EAAE;oBACtB,cAAc,EAAE,KAAK,CAAC,IAAI;iBAC3B;gBACD,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC;aAChD,CAAC,CAAC;YAEH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;YAC/C,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,yBAAyB,CAAC,CAAC;YACxE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;QAC3D,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;AAEnD,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ApiKey, Project } from '@prisma/client';
2
+ declare module 'hono' {
3
+ interface ContextVariableMap {
4
+ requestId: string;
5
+ projectId: string;
6
+ apiKey: ApiKey;
7
+ project: Project;
8
+ }
9
+ }
10
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/types/hono.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGtD,OAAO,QAAQ,MAAM,CAAC;IACpB,UAAU,kBAAkB;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/types/hono.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { Context } from 'hono';
2
+ export declare function getProjectId(c: Context): string;
3
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/utils/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGpC,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAM/C"}
@@ -0,0 +1,9 @@
1
+ import { AppError } from '../errors.js';
2
+ export function getProjectId(c) {
3
+ const projectId = c.get('projectId');
4
+ if (!projectId) {
5
+ throw new AppError('INTERNAL', 500, 'Auth context missing: projectId not set');
6
+ }
7
+ return projectId;
8
+ }
9
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/utils/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,yCAAyC,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,SAAmB,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import pino from 'pino';
2
+ export declare const logger: pino.Logger<never, boolean>;
3
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,eAAO,MAAM,MAAM,6BAkBjB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import pino from 'pino';
2
+ export const logger = pino({
3
+ level: process.env.LOG_LEVEL || 'info',
4
+ formatters: {
5
+ level: (label) => ({ level: label }),
6
+ },
7
+ redact: {
8
+ paths: ['*.apiKey', '*.password', '*.secret', '*.keyHash', '*.token'],
9
+ censor: '[REDACTED]',
10
+ },
11
+ transport: process.env.NODE_ENV === 'development'
12
+ ? {
13
+ target: 'pino-pretty',
14
+ options: {
15
+ colorize: true,
16
+ },
17
+ }
18
+ : undefined,
19
+ });
20
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;IACtC,UAAU,EAAE;QACV,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KACrC;IACD,MAAM,EAAE;QACN,KAAK,EAAE,CAAC,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,CAAC;QACrE,MAAM,EAAE,YAAY;KACrB;IACD,SAAS,EACP,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa;QACpC,CAAC,CAAC;YACE,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE;gBACP,QAAQ,EAAE,IAAI;aACf;SACF;QACH,CAAC,CAAC,SAAS;CAChB,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare function paginateResult<T extends {
2
+ id: string;
3
+ }>(items: T[], limit: number): {
4
+ data: T[];
5
+ meta: {
6
+ limit: number;
7
+ nextCursor?: string;
8
+ };
9
+ };
10
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/utils/pagination.ts"],"names":[],"mappings":"AAAA,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACrD,KAAK,EAAE,CAAC,EAAE,EACV,KAAK,EAAE,MAAM,GACZ;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAQ7D"}