@mastra/factory 0.2.2-alpha.1 → 0.2.2-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/auth.js +401 -334
  3. package/dist/auth.js.map +1 -1
  4. package/dist/capabilities/connection.js +1 -1
  5. package/dist/capabilities/intake.js +1 -1
  6. package/dist/capabilities/version-control.js +1 -1
  7. package/dist/factory.js +441 -39622
  8. package/dist/factory.js.map +1 -1
  9. package/dist/index.js +2 -39633
  10. package/dist/integrations/base.js +1 -1
  11. package/dist/integrations/github/config.js +36 -22
  12. package/dist/integrations/github/config.js.map +1 -1
  13. package/dist/integrations/github/factory-session.js +33 -37
  14. package/dist/integrations/github/factory-session.js.map +1 -1
  15. package/dist/integrations/github/integration.js +1037 -3672
  16. package/dist/integrations/github/integration.js.map +1 -1
  17. package/dist/integrations/github/issue-triage.js +71 -71
  18. package/dist/integrations/github/issue-triage.js.map +1 -1
  19. package/dist/integrations/github/pat.js +47 -36
  20. package/dist/integrations/github/pat.js.map +1 -1
  21. package/dist/integrations/github/provenance.js +72 -101
  22. package/dist/integrations/github/provenance.js.map +1 -1
  23. package/dist/integrations/github/routes.js +1272 -1881
  24. package/dist/integrations/github/routes.js.map +1 -1
  25. package/dist/integrations/github/rules.js +203 -471
  26. package/dist/integrations/github/rules.js.map +1 -1
  27. package/dist/integrations/github/sandbox.js +400 -345
  28. package/dist/integrations/github/sandbox.js.map +1 -1
  29. package/dist/integrations/github/session-subscriptions.js +165 -223
  30. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  31. package/dist/integrations/github/subscriptions.js +43 -51
  32. package/dist/integrations/github/subscriptions.js.map +1 -1
  33. package/dist/integrations/github/token-refresh.js +15 -18
  34. package/dist/integrations/github/token-refresh.js.map +1 -1
  35. package/dist/integrations/github/webhook.js +359 -343
  36. package/dist/integrations/github/webhook.js.map +1 -1
  37. package/dist/integrations/linear/agent-tools.js +77 -100
  38. package/dist/integrations/linear/agent-tools.js.map +1 -1
  39. package/dist/integrations/linear/integration.js +611 -1309
  40. package/dist/integrations/linear/integration.js.map +1 -1
  41. package/dist/integrations/linear/routes.js +227 -230
  42. package/dist/integrations/linear/routes.js.map +1 -1
  43. package/dist/integrations/linear/rules.js +126 -346
  44. package/dist/integrations/linear/rules.js.map +1 -1
  45. package/dist/integrations/linear/storage.js +1 -1
  46. package/dist/integrations/platform/api-client.js +131 -135
  47. package/dist/integrations/platform/api-client.js.map +1 -1
  48. package/dist/integrations/platform/github/event-worker.js +275 -573
  49. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  50. package/dist/integrations/platform/github/integration.js +845 -4022
  51. package/dist/integrations/platform/github/integration.js.map +1 -1
  52. package/dist/integrations/platform/linear/integration.js +457 -1309
  53. package/dist/integrations/platform/linear/integration.js.map +1 -1
  54. package/dist/integrations/workos/integration.js +80 -85
  55. package/dist/integrations/workos/integration.js.map +1 -1
  56. package/dist/routes/config.d.ts.map +1 -1
  57. package/dist/routes/config.js +933 -912
  58. package/dist/routes/config.js.map +1 -1
  59. package/dist/routes/custom-provider-source.js +110 -93
  60. package/dist/routes/custom-provider-source.js.map +1 -1
  61. package/dist/routes/fs.js +401 -349
  62. package/dist/routes/fs.js.map +1 -1
  63. package/dist/routes/intake.js +165 -165
  64. package/dist/routes/intake.js.map +1 -1
  65. package/dist/routes/oauth.js +447 -25166
  66. package/dist/routes/oauth.js.map +1 -1
  67. package/dist/routes/projects.js +437 -376
  68. package/dist/routes/projects.js.map +1 -1
  69. package/dist/routes/provider-credentials.js +76 -58
  70. package/dist/routes/provider-credentials.js.map +1 -1
  71. package/dist/routes/route.js +12 -8
  72. package/dist/routes/route.js.map +1 -1
  73. package/dist/routes/skills.js +160 -173
  74. package/dist/routes/skills.js.map +1 -1
  75. package/dist/routes/surface.js +250 -28615
  76. package/dist/routes/surface.js.map +1 -1
  77. package/dist/routes/tenant-credentials.js +141 -166
  78. package/dist/routes/tenant-credentials.js.map +1 -1
  79. package/dist/routes/work-items.js +654 -862
  80. package/dist/routes/work-items.js.map +1 -1
  81. package/dist/rules/binding-context.js +23 -35
  82. package/dist/rules/binding-context.js.map +1 -1
  83. package/dist/rules/defaults.js +200 -309
  84. package/dist/rules/defaults.js.map +1 -1
  85. package/dist/rules/dispatcher.js +438 -683
  86. package/dist/rules/dispatcher.js.map +1 -1
  87. package/dist/rules/index.js +5 -599
  88. package/dist/rules/processor.js +344 -596
  89. package/dist/rules/processor.js.map +1 -1
  90. package/dist/rules/resolve.js +21 -18
  91. package/dist/rules/resolve.js.map +1 -1
  92. package/dist/rules/start-coordinator.js +168 -157
  93. package/dist/rules/start-coordinator.js.map +1 -1
  94. package/dist/rules/tools.js +50 -85
  95. package/dist/rules/tools.js.map +1 -1
  96. package/dist/rules/transition-service.js +183 -454
  97. package/dist/rules/transition-service.js.map +1 -1
  98. package/dist/rules/types.js +33 -29
  99. package/dist/rules/types.js.map +1 -1
  100. package/dist/rules/validation.js +266 -281
  101. package/dist/rules/validation.js.map +1 -1
  102. package/dist/sandbox/fleet.js +272 -250
  103. package/dist/sandbox/fleet.js.map +1 -1
  104. package/dist/sandbox/reattach.js +13 -6
  105. package/dist/sandbox/reattach.js.map +1 -1
  106. package/dist/server-error.js +12 -16
  107. package/dist/server-error.js.map +1 -1
  108. package/dist/skills/service.js +30 -35
  109. package/dist/skills/service.js.map +1 -1
  110. package/dist/spa-static.js +91 -65
  111. package/dist/spa-static.js.map +1 -1
  112. package/dist/state-signing.js +73 -44
  113. package/dist/state-signing.js.map +1 -1
  114. package/dist/storage/domains/audit/agent-audit.js +60 -59
  115. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  116. package/dist/storage/domains/audit/base.js +169 -112
  117. package/dist/storage/domains/audit/base.js.map +1 -1
  118. package/dist/storage/domains/audit/domain.js +155 -153
  119. package/dist/storage/domains/audit/domain.js.map +1 -1
  120. package/dist/storage/domains/credentials/base.js +259 -243
  121. package/dist/storage/domains/credentials/base.js.map +1 -1
  122. package/dist/storage/domains/custom-providers/base.js +138 -143
  123. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  124. package/dist/storage/domains/intake/base.js +75 -57
  125. package/dist/storage/domains/intake/base.js.map +1 -1
  126. package/dist/storage/domains/integrations/base.js +310 -245
  127. package/dist/storage/domains/integrations/base.js.map +1 -1
  128. package/dist/storage/domains/memory-settings/base.js +117 -102
  129. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  130. package/dist/storage/domains/model-packs/base.js +89 -88
  131. package/dist/storage/domains/model-packs/base.js.map +1 -1
  132. package/dist/storage/domains/projects/base.js +97 -92
  133. package/dist/storage/domains/projects/base.js.map +1 -1
  134. package/dist/storage/domains/queue-health/base.js +111 -76
  135. package/dist/storage/domains/queue-health/base.js.map +1 -1
  136. package/dist/storage/domains/source-control/base.js +777 -706
  137. package/dist/storage/domains/source-control/base.js.map +1 -1
  138. package/dist/storage/domains/source-control/inmemory.js +322 -348
  139. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  140. package/dist/storage/domains/work-items/base.js +1266 -1148
  141. package/dist/storage/domains/work-items/base.js.map +1 -1
  142. package/dist/storage/domains/work-items/metrics.js +202 -184
  143. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  144. package/dist/timing.js +29 -22
  145. package/dist/timing.js.map +1 -1
  146. package/dist/workspace.js +236 -477
  147. package/dist/workspace.js.map +1 -1
  148. package/package.json +8 -8
  149. package/dist/capabilities/connection.js.map +0 -1
  150. package/dist/capabilities/intake.js.map +0 -1
  151. package/dist/capabilities/version-control.js.map +0 -1
  152. package/dist/index.js.map +0 -1
  153. package/dist/integrations/base.js.map +0 -1
  154. package/dist/integrations/linear/storage.js.map +0 -1
  155. package/dist/rules/index.js.map +0 -1
@@ -1,99 +1,104 @@
1
- // src/storage/domains/projects/base.ts
2
1
  import { FactoryStorageDomain } from "@mastra/core/storage";
3
- var FACTORY_PROJECTS_SCHEMA = {
4
- name: "factory_projects",
5
- columns: {
6
- id: { type: "uuid-pk" },
7
- org_id: { type: "text" },
8
- created_by: { type: "text" },
9
- name: { type: "text" },
10
- description: { type: "text", nullable: true },
11
- default_model_id: { type: "text", nullable: true },
12
- created_at: { type: "timestamp" },
13
- updated_at: { type: "timestamp" }
14
- },
15
- indexes: [{ name: "factory_projects_org_updated_at_idx", columns: ["org_id", "updated_at"] }]
2
+ //#region src/storage/domains/projects/base.ts
3
+ const FACTORY_PROJECTS_SCHEMA = {
4
+ name: "factory_projects",
5
+ columns: {
6
+ id: { type: "uuid-pk" },
7
+ org_id: { type: "text" },
8
+ created_by: { type: "text" },
9
+ name: { type: "text" },
10
+ description: {
11
+ type: "text",
12
+ nullable: true
13
+ },
14
+ default_model_id: {
15
+ type: "text",
16
+ nullable: true
17
+ },
18
+ created_at: { type: "timestamp" },
19
+ updated_at: { type: "timestamp" }
20
+ },
21
+ indexes: [{
22
+ name: "factory_projects_org_updated_at_idx",
23
+ columns: ["org_id", "updated_at"]
24
+ }]
16
25
  };
17
26
  function toFactoryProject(row) {
18
- return {
19
- id: row.id,
20
- orgId: row.org_id,
21
- createdBy: row.created_by,
22
- name: row.name,
23
- description: row.description,
24
- defaultModelId: row.default_model_id,
25
- createdAt: row.created_at,
26
- updatedAt: row.updated_at
27
- };
27
+ return {
28
+ id: row.id,
29
+ orgId: row.org_id,
30
+ createdBy: row.created_by,
31
+ name: row.name,
32
+ description: row.description,
33
+ defaultModelId: row.default_model_id,
34
+ createdAt: row.created_at,
35
+ updatedAt: row.updated_at
36
+ };
28
37
  }
29
38
  var FactoryProjectsStorage = class extends FactoryStorageDomain {
30
- constructor() {
31
- super("projects");
32
- }
33
- async init() {
34
- await this.ensureCollections([FACTORY_PROJECTS_SCHEMA]);
35
- }
36
- async dangerouslyClearAll() {
37
- await this.ops.deleteMany("factory_projects", {});
38
- }
39
- get #db() {
40
- return this.ops;
41
- }
42
- async create({
43
- orgId,
44
- userId,
45
- input
46
- }) {
47
- const now = /* @__PURE__ */ new Date();
48
- const row = await this.#db.insertOne("factory_projects", {
49
- org_id: orgId,
50
- created_by: userId,
51
- name: input.name,
52
- description: input.description ?? null,
53
- default_model_id: input.defaultModelId ?? null,
54
- created_at: now,
55
- updated_at: now
56
- });
57
- return toFactoryProject(row);
58
- }
59
- async list({ orgId }) {
60
- const rows = await this.#db.findMany(
61
- "factory_projects",
62
- { org_id: orgId },
63
- { orderBy: [["updated_at", "desc"]] }
64
- );
65
- return rows.map(toFactoryProject);
66
- }
67
- async get({ orgId, id }) {
68
- const row = await this.#db.findOne("factory_projects", { org_id: orgId, id });
69
- return row ? toFactoryProject(row) : null;
70
- }
71
- async getById({ id }) {
72
- const row = await this.#db.findOne("factory_projects", { id });
73
- return row ? toFactoryProject(row) : null;
74
- }
75
- async update({
76
- orgId,
77
- id,
78
- input
79
- }) {
80
- const row = await this.#db.updateAtomic("factory_projects", { org_id: orgId, id }, () => ({
81
- ...input.name !== void 0 ? { name: input.name } : {},
82
- ...input.description !== void 0 ? { description: input.description } : {},
83
- ...input.defaultModelId !== void 0 ? { default_model_id: input.defaultModelId } : {},
84
- updated_at: /* @__PURE__ */ new Date()
85
- }));
86
- return row ? toFactoryProject(row) : null;
87
- }
88
- async delete({ orgId, id }) {
89
- const project = await this.get({ orgId, id });
90
- if (!project) return null;
91
- const deleted = await this.#db.deleteMany("factory_projects", { org_id: orgId, id });
92
- return deleted > 0 ? project : null;
93
- }
94
- };
95
- export {
96
- FACTORY_PROJECTS_SCHEMA,
97
- FactoryProjectsStorage
39
+ constructor() {
40
+ super("projects");
41
+ }
42
+ async init() {
43
+ await this.ensureCollections([FACTORY_PROJECTS_SCHEMA]);
44
+ }
45
+ async dangerouslyClearAll() {
46
+ await this.ops.deleteMany("factory_projects", {});
47
+ }
48
+ get #db() {
49
+ return this.ops;
50
+ }
51
+ async create({ orgId, userId, input }) {
52
+ const now = /* @__PURE__ */ new Date();
53
+ return toFactoryProject(await this.#db.insertOne("factory_projects", {
54
+ org_id: orgId,
55
+ created_by: userId,
56
+ name: input.name,
57
+ description: input.description ?? null,
58
+ default_model_id: input.defaultModelId ?? null,
59
+ created_at: now,
60
+ updated_at: now
61
+ }));
62
+ }
63
+ async list({ orgId }) {
64
+ return (await this.#db.findMany("factory_projects", { org_id: orgId }, { orderBy: [["updated_at", "desc"]] })).map(toFactoryProject);
65
+ }
66
+ async get({ orgId, id }) {
67
+ const row = await this.#db.findOne("factory_projects", {
68
+ org_id: orgId,
69
+ id
70
+ });
71
+ return row ? toFactoryProject(row) : null;
72
+ }
73
+ async getById({ id }) {
74
+ const row = await this.#db.findOne("factory_projects", { id });
75
+ return row ? toFactoryProject(row) : null;
76
+ }
77
+ async update({ orgId, id, input }) {
78
+ const row = await this.#db.updateAtomic("factory_projects", {
79
+ org_id: orgId,
80
+ id
81
+ }, () => ({
82
+ ...input.name !== void 0 ? { name: input.name } : {},
83
+ ...input.description !== void 0 ? { description: input.description } : {},
84
+ ...input.defaultModelId !== void 0 ? { default_model_id: input.defaultModelId } : {},
85
+ updated_at: /* @__PURE__ */ new Date()
86
+ }));
87
+ return row ? toFactoryProject(row) : null;
88
+ }
89
+ async delete({ orgId, id }) {
90
+ const project = await this.get({
91
+ orgId,
92
+ id
93
+ });
94
+ if (!project) return null;
95
+ return await this.#db.deleteMany("factory_projects", {
96
+ org_id: orgId,
97
+ id
98
+ }) > 0 ? project : null;
99
+ }
98
100
  };
101
+ //#endregion
102
+ export { FACTORY_PROJECTS_SCHEMA, FactoryProjectsStorage };
103
+
99
104
  //# sourceMappingURL=base.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/storage/domains/projects/base.ts"],"sourcesContent":["import { FactoryStorageDomain } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface FactoryProject {\n id: string;\n orgId: string;\n createdBy: string;\n name: string;\n description: string | null;\n /** Default model for sessions/runs started under this Factory (null = harness default). */\n defaultModelId: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface CreateFactoryProjectInput {\n name: string;\n description?: string | null;\n defaultModelId?: string | null;\n}\n\nexport interface UpdateFactoryProjectInput {\n name?: string;\n description?: string | null;\n defaultModelId?: string | null;\n}\n\nexport const FACTORY_PROJECTS_SCHEMA: CollectionSchema = {\n name: 'factory_projects',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n name: { type: 'text' },\n description: { type: 'text', nullable: true },\n default_model_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'factory_projects_org_updated_at_idx', columns: ['org_id', 'updated_at'] }],\n};\n\ninterface FactoryProjectDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n name: string;\n description: string | null;\n default_model_id: string | null;\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toFactoryProject(row: FactoryProjectDbRow): FactoryProject {\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n name: row.name,\n description: row.description,\n defaultModelId: row.default_model_id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport class FactoryProjectsStorage extends FactoryStorageDomain {\n constructor() {\n super('projects');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([FACTORY_PROJECTS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('factory_projects', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async create({\n orgId,\n userId,\n input,\n }: {\n orgId: string;\n userId: string;\n input: CreateFactoryProjectInput;\n }): Promise<FactoryProject> {\n const now = new Date();\n const row = await this.#db.insertOne<FactoryProjectDbRow>('factory_projects', {\n org_id: orgId,\n created_by: userId,\n name: input.name,\n description: input.description ?? null,\n default_model_id: input.defaultModelId ?? null,\n created_at: now,\n updated_at: now,\n });\n return toFactoryProject(row);\n }\n\n async list({ orgId }: { orgId: string }): Promise<FactoryProject[]> {\n const rows = await this.#db.findMany<FactoryProjectDbRow>(\n 'factory_projects',\n { org_id: orgId },\n { orderBy: [['updated_at', 'desc']] },\n );\n return rows.map(toFactoryProject);\n }\n\n async get({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id });\n return row ? toFactoryProject(row) : null;\n }\n\n async getById({ id }: { id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { id });\n return row ? toFactoryProject(row) : null;\n }\n\n async update({\n orgId,\n id,\n input,\n }: {\n orgId: string;\n id: string;\n input: UpdateFactoryProjectInput;\n }): Promise<FactoryProject | null> {\n const row = await this.#db.updateAtomic<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id }, () => ({\n ...(input.name !== undefined ? { name: input.name } : {}),\n ...(input.description !== undefined ? { description: input.description } : {}),\n ...(input.defaultModelId !== undefined ? { default_model_id: input.defaultModelId } : {}),\n updated_at: new Date(),\n }));\n return row ? toFactoryProject(row) : null;\n }\n\n async delete({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const project = await this.get({ orgId, id });\n if (!project) return null;\n const deleted = await this.#db.deleteMany('factory_projects', { org_id: orgId, id });\n return deleted > 0 ? project : null;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/projects/base.ts"],"sourcesContent":["import { FactoryStorageDomain } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface FactoryProject {\n id: string;\n orgId: string;\n createdBy: string;\n name: string;\n description: string | null;\n /** Default model for sessions/runs started under this Factory (null = harness default). */\n defaultModelId: string | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface CreateFactoryProjectInput {\n name: string;\n description?: string | null;\n defaultModelId?: string | null;\n}\n\nexport interface UpdateFactoryProjectInput {\n name?: string;\n description?: string | null;\n defaultModelId?: string | null;\n}\n\nexport const FACTORY_PROJECTS_SCHEMA: CollectionSchema = {\n name: 'factory_projects',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n name: { type: 'text' },\n description: { type: 'text', nullable: true },\n default_model_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'factory_projects_org_updated_at_idx', columns: ['org_id', 'updated_at'] }],\n};\n\ninterface FactoryProjectDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n name: string;\n description: string | null;\n default_model_id: string | null;\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toFactoryProject(row: FactoryProjectDbRow): FactoryProject {\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n name: row.name,\n description: row.description,\n defaultModelId: row.default_model_id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport class FactoryProjectsStorage extends FactoryStorageDomain {\n constructor() {\n super('projects');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([FACTORY_PROJECTS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('factory_projects', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async create({\n orgId,\n userId,\n input,\n }: {\n orgId: string;\n userId: string;\n input: CreateFactoryProjectInput;\n }): Promise<FactoryProject> {\n const now = new Date();\n const row = await this.#db.insertOne<FactoryProjectDbRow>('factory_projects', {\n org_id: orgId,\n created_by: userId,\n name: input.name,\n description: input.description ?? null,\n default_model_id: input.defaultModelId ?? null,\n created_at: now,\n updated_at: now,\n });\n return toFactoryProject(row);\n }\n\n async list({ orgId }: { orgId: string }): Promise<FactoryProject[]> {\n const rows = await this.#db.findMany<FactoryProjectDbRow>(\n 'factory_projects',\n { org_id: orgId },\n { orderBy: [['updated_at', 'desc']] },\n );\n return rows.map(toFactoryProject);\n }\n\n async get({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id });\n return row ? toFactoryProject(row) : null;\n }\n\n async getById({ id }: { id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { id });\n return row ? toFactoryProject(row) : null;\n }\n\n async update({\n orgId,\n id,\n input,\n }: {\n orgId: string;\n id: string;\n input: UpdateFactoryProjectInput;\n }): Promise<FactoryProject | null> {\n const row = await this.#db.updateAtomic<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id }, () => ({\n ...(input.name !== undefined ? { name: input.name } : {}),\n ...(input.description !== undefined ? { description: input.description } : {}),\n ...(input.defaultModelId !== undefined ? { default_model_id: input.defaultModelId } : {}),\n updated_at: new Date(),\n }));\n return row ? toFactoryProject(row) : null;\n }\n\n async delete({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const project = await this.get({ orgId, id });\n if (!project) return null;\n const deleted = await this.#db.deleteMany('factory_projects', { org_id: orgId, id });\n return deleted > 0 ? project : null;\n }\n}\n"],"mappings":";;AA2BA,MAAa,0BAA4C;CACvD,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,YAAY,EAAE,MAAM,OAAO;EAC3B,MAAM,EAAE,MAAM,OAAO;EACrB,aAAa;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC5C,kBAAkB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACjD,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,SAAS,CAAC;EAAE,MAAM;EAAuC,SAAS,CAAC,UAAU,YAAY;CAAE,CAAC;AAC9F;AAaA,SAAS,iBAAiB,KAA0C;CAClE,OAAO;EACL,IAAI,IAAI;EACR,OAAO,IAAI;EACX,WAAW,IAAI;EACf,MAAM,IAAI;EACV,aAAa,IAAI;EACjB,gBAAgB,IAAI;EACpB,WAAW,IAAI;EACf,WAAW,IAAI;CACjB;AACF;AAEA,IAAa,yBAAb,cAA4C,qBAAqB;CAC/D,cAAc;EACZ,MAAM,UAAU;CAClB;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,uBAAuB,CAAC;CACxD;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,oBAAoB,CAAC,CAAC;CAClD;CAEA,IAAIA,MAAyB;EAC3B,OAAO,KAAK;CACd;CAEA,MAAM,OAAO,EACX,OACA,QACA,SAK0B;EAC1B,MAAM,sBAAM,IAAI,KAAK;EAUrB,OAAO,iBAAiB,MATN,KAAKA,IAAI,UAA+B,oBAAoB;GAC5E,QAAQ;GACR,YAAY;GACZ,MAAM,MAAM;GACZ,aAAa,MAAM,eAAe;GAClC,kBAAkB,MAAM,kBAAkB;GAC1C,YAAY;GACZ,YAAY;EACd,CAAC,CAC0B;CAC7B;CAEA,MAAM,KAAK,EAAE,SAAuD;EAMlE,QAAO,MALY,KAAKA,IAAI,SAC1B,oBACA,EAAE,QAAQ,MAAM,GAChB,EAAE,SAAS,CAAC,CAAC,cAAc,MAAM,CAAC,EAAE,CACtC,EAAA,CACY,IAAI,gBAAgB;CAClC;CAEA,MAAM,IAAI,EAAE,OAAO,MAAqE;EACtF,MAAM,MAAM,MAAM,KAAKA,IAAI,QAA6B,oBAAoB;GAAE,QAAQ;GAAO;EAAG,CAAC;EACjG,OAAO,MAAM,iBAAiB,GAAG,IAAI;CACvC;CAEA,MAAM,QAAQ,EAAE,MAAsD;EACpE,MAAM,MAAM,MAAM,KAAKA,IAAI,QAA6B,oBAAoB,EAAE,GAAG,CAAC;EAClF,OAAO,MAAM,iBAAiB,GAAG,IAAI;CACvC;CAEA,MAAM,OAAO,EACX,OACA,IACA,SAKiC;EACjC,MAAM,MAAM,MAAM,KAAKA,IAAI,aAAkC,oBAAoB;GAAE,QAAQ;GAAO;EAAG,UAAU;GAC7G,GAAI,MAAM,SAAS,KAAA,IAAY,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;GACvD,GAAI,MAAM,gBAAgB,KAAA,IAAY,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;GAC5E,GAAI,MAAM,mBAAmB,KAAA,IAAY,EAAE,kBAAkB,MAAM,eAAe,IAAI,CAAC;GACvF,4BAAY,IAAI,KAAK;EACvB,EAAE;EACF,OAAO,MAAM,iBAAiB,GAAG,IAAI;CACvC;CAEA,MAAM,OAAO,EAAE,OAAO,MAAqE;EACzF,MAAM,UAAU,MAAM,KAAK,IAAI;GAAE;GAAO;EAAG,CAAC;EAC5C,IAAI,CAAC,SAAS,OAAO;EAErB,OAAO,MADe,KAAKA,IAAI,WAAW,oBAAoB;GAAE,QAAQ;GAAO;EAAG,CAAC,IAClE,IAAI,UAAU;CACjC;AACF"}
@@ -1,87 +1,122 @@
1
- // src/storage/domains/queue-health/base.ts
2
1
  import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
3
- var DEFAULT_QUEUE_HEALTH_CONFIG = {
4
- thresholdsSeconds: [14400, 86400, 259200]
5
- };
2
+ //#region src/storage/domains/queue-health/base.ts
3
+ /**
4
+ * Queue-health threshold configuration domain: per-project age buckets for the
5
+ * Factory Overview queue-health chart.
6
+ *
7
+ * Stored per `(org, github_project)` — each connected GitHub project gets its
8
+ * own age thresholds so a fast-moving automated pipeline and a slow human one
9
+ * can bucket "how old is the work" differently. `thresholdsSeconds` is an
10
+ * ordered-ascending list of age boundaries in seconds: an item younger than
11
+ * the first boundary is green, younger than the second amber, younger than the
12
+ * third orange, and red otherwise. Seconds (not hours) so sub-minute buckets
13
+ * are representable for fast automated flows.
14
+ */
15
+ /** Default: green <4h, amber <24h, orange <72h, red 72h+. */
16
+ const DEFAULT_QUEUE_HEALTH_CONFIG = { thresholdsSeconds: [
17
+ 14400,
18
+ 86400,
19
+ 259200
20
+ ] };
21
+ /**
22
+ * Throw unless `thresholdsSeconds` is a non-empty ascending number list. A
23
+ * descending config would silently invert bucket semantics, so validate at the
24
+ * write boundary rather than trusting callers.
25
+ */
6
26
  function assertValidThresholds(config) {
7
- const t = config.thresholdsSeconds;
8
- if (!Array.isArray(t) || t.length === 0 || t.some((v) => typeof v !== "number" || !Number.isFinite(v))) {
9
- throw new Error("[QueueHealthStorage] thresholdsSeconds must be a non-empty array of finite numbers.");
10
- }
11
- for (let i = 1; i < t.length; i++) {
12
- if (t[i] <= t[i - 1]) {
13
- throw new Error("[QueueHealthStorage] thresholdsSeconds must be strictly ascending.");
14
- }
15
- }
27
+ const t = config.thresholdsSeconds;
28
+ if (!Array.isArray(t) || t.length === 0 || t.some((v) => typeof v !== "number" || !Number.isFinite(v))) throw new Error("[QueueHealthStorage] thresholdsSeconds must be a non-empty array of finite numbers.");
29
+ for (let i = 1; i < t.length; i++) if (t[i] <= t[i - 1]) throw new Error("[QueueHealthStorage] thresholdsSeconds must be strictly ascending.");
16
30
  }
31
+ /** Validate untrusted JSON into a `QueueHealthConfig`, or `null` when invalid. */
17
32
  function parseQueueHealthConfig(body) {
18
- if (typeof body !== "object" || body === null) return null;
19
- const config = body;
20
- if (!Array.isArray(config.thresholdsSeconds)) return null;
21
- try {
22
- assertValidThresholds(config);
23
- } catch {
24
- return null;
25
- }
26
- return { thresholdsSeconds: [...config.thresholdsSeconds] };
33
+ if (typeof body !== "object" || body === null) return null;
34
+ const config = body;
35
+ if (!Array.isArray(config.thresholdsSeconds)) return null;
36
+ try {
37
+ assertValidThresholds(config);
38
+ } catch {
39
+ return null;
40
+ }
41
+ return { thresholdsSeconds: [...config.thresholdsSeconds] };
27
42
  }
43
+ /**
44
+ * Return `config.thresholdsSeconds` when valid, else the default. Validation
45
+ * lives at the `saveConfig` write boundary, but `getConfig` round-trips a
46
+ * stored JSON row — a corrupted or hand-edited row (empty / non-ascending)
47
+ * would otherwise reach the chart and invert bucket colors, so the read route
48
+ * re-validates and falls back.
49
+ */
28
50
  function thresholdsOrDefault(config) {
29
- return parseQueueHealthConfig(config)?.thresholdsSeconds ?? DEFAULT_QUEUE_HEALTH_CONFIG.thresholdsSeconds;
51
+ return parseQueueHealthConfig(config)?.thresholdsSeconds ?? DEFAULT_QUEUE_HEALTH_CONFIG.thresholdsSeconds;
30
52
  }
31
- var QUEUE_HEALTH_SETTINGS_SCHEMA = {
32
- name: "queue_health_settings",
33
- columns: {
34
- id: { type: "uuid-pk" },
35
- org_id: { type: "text" },
36
- factory_project_id: { type: "text" },
37
- config: { type: "json" },
38
- created_at: { type: "timestamp" },
39
- updated_at: { type: "timestamp" }
40
- },
41
- uniqueIndexes: [{ name: "queue_health_settings_org_project_unique", columns: ["org_id", "factory_project_id"] }]
53
+ const QUEUE_HEALTH_SETTINGS_SCHEMA = {
54
+ name: "queue_health_settings",
55
+ columns: {
56
+ id: { type: "uuid-pk" },
57
+ org_id: { type: "text" },
58
+ factory_project_id: { type: "text" },
59
+ config: { type: "json" },
60
+ created_at: { type: "timestamp" },
61
+ updated_at: { type: "timestamp" }
62
+ },
63
+ uniqueIndexes: [{
64
+ name: "queue_health_settings_org_project_unique",
65
+ columns: ["org_id", "factory_project_id"]
66
+ }]
42
67
  };
68
+ /**
69
+ * Queue-health settings storage, written once against the generic
70
+ * `FactoryStorageOps` surface — works on any `FactoryStorage` backend.
71
+ */
43
72
  var QueueHealthStorage = class extends FactoryStorageDomain {
44
- constructor() {
45
- super("queue-health");
46
- }
47
- async init() {
48
- await this.ensureCollections([QUEUE_HEALTH_SETTINGS_SCHEMA]);
49
- }
50
- async dangerouslyClearAll() {
51
- await this.ops.deleteMany("queue_health_settings", {});
52
- }
53
- get #db() {
54
- return this.ops;
55
- }
56
- /** Read the project's queue-health config, falling back to {@link DEFAULT_QUEUE_HEALTH_CONFIG}. */
57
- async getConfig(orgId, factoryProjectId) {
58
- const row = await this.#db.findOne("queue_health_settings", {
59
- org_id: orgId,
60
- factory_project_id: factoryProjectId
61
- });
62
- return structuredClone(parseQueueHealthConfig(row?.config) ?? DEFAULT_QUEUE_HEALTH_CONFIG);
63
- }
64
- /** Upsert the project's queue-health config (`created_at` is preserved on update). */
65
- async saveConfig(orgId, factoryProjectId, config) {
66
- assertValidThresholds(config);
67
- const where = { org_id: orgId, factory_project_id: factoryProjectId };
68
- const updateExisting = () => this.#db.updateAtomic("queue_health_settings", where, () => ({ config, updated_at: /* @__PURE__ */ new Date() }));
69
- if (await updateExisting()) return;
70
- const now = /* @__PURE__ */ new Date();
71
- try {
72
- await this.#db.insertOne("queue_health_settings", { ...where, config, created_at: now, updated_at: now });
73
- } catch (error) {
74
- if (!(error instanceof UniqueViolationError)) throw error;
75
- if (!await updateExisting()) throw error;
76
- }
77
- }
78
- };
79
- export {
80
- DEFAULT_QUEUE_HEALTH_CONFIG,
81
- QUEUE_HEALTH_SETTINGS_SCHEMA,
82
- QueueHealthStorage,
83
- assertValidThresholds,
84
- parseQueueHealthConfig,
85
- thresholdsOrDefault
73
+ constructor() {
74
+ super("queue-health");
75
+ }
76
+ async init() {
77
+ await this.ensureCollections([QUEUE_HEALTH_SETTINGS_SCHEMA]);
78
+ }
79
+ async dangerouslyClearAll() {
80
+ await this.ops.deleteMany("queue_health_settings", {});
81
+ }
82
+ get #db() {
83
+ return this.ops;
84
+ }
85
+ /** Read the project's queue-health config, falling back to {@link DEFAULT_QUEUE_HEALTH_CONFIG}. */
86
+ async getConfig(orgId, factoryProjectId) {
87
+ const row = await this.#db.findOne("queue_health_settings", {
88
+ org_id: orgId,
89
+ factory_project_id: factoryProjectId
90
+ });
91
+ return structuredClone(parseQueueHealthConfig(row?.config) ?? DEFAULT_QUEUE_HEALTH_CONFIG);
92
+ }
93
+ /** Upsert the project's queue-health config (`created_at` is preserved on update). */
94
+ async saveConfig(orgId, factoryProjectId, config) {
95
+ assertValidThresholds(config);
96
+ const where = {
97
+ org_id: orgId,
98
+ factory_project_id: factoryProjectId
99
+ };
100
+ const updateExisting = () => this.#db.updateAtomic("queue_health_settings", where, () => ({
101
+ config,
102
+ updated_at: /* @__PURE__ */ new Date()
103
+ }));
104
+ if (await updateExisting()) return;
105
+ const now = /* @__PURE__ */ new Date();
106
+ try {
107
+ await this.#db.insertOne("queue_health_settings", {
108
+ ...where,
109
+ config,
110
+ created_at: now,
111
+ updated_at: now
112
+ });
113
+ } catch (error) {
114
+ if (!(error instanceof UniqueViolationError)) throw error;
115
+ if (!await updateExisting()) throw error;
116
+ }
117
+ }
86
118
  };
119
+ //#endregion
120
+ export { DEFAULT_QUEUE_HEALTH_CONFIG, QUEUE_HEALTH_SETTINGS_SCHEMA, QueueHealthStorage, assertValidThresholds, parseQueueHealthConfig, thresholdsOrDefault };
121
+
87
122
  //# sourceMappingURL=base.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/storage/domains/queue-health/base.ts"],"sourcesContent":["/**\n * Queue-health threshold configuration domain: per-project age buckets for the\n * Factory Overview queue-health chart.\n *\n * Stored per `(org, github_project)` — each connected GitHub project gets its\n * own age thresholds so a fast-moving automated pipeline and a slow human one\n * can bucket \"how old is the work\" differently. `thresholdsSeconds` is an\n * ordered-ascending list of age boundaries in seconds: an item younger than\n * the first boundary is green, younger than the second amber, younger than the\n * third orange, and red otherwise. Seconds (not hours) so sub-minute buckets\n * are representable for fast automated flows.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface QueueHealthConfig {\n /** Ordered-ascending age boundaries in seconds, e.g. `[14400, 86400, 259200]`. */\n thresholdsSeconds: number[];\n}\n\n/** Default: green <4h, amber <24h, orange <72h, red 72h+. */\nexport const DEFAULT_QUEUE_HEALTH_CONFIG: QueueHealthConfig = {\n thresholdsSeconds: [14400, 86400, 259200],\n};\n\n/**\n * Throw unless `thresholdsSeconds` is a non-empty ascending number list. A\n * descending config would silently invert bucket semantics, so validate at the\n * write boundary rather than trusting callers.\n */\nexport function assertValidThresholds(config: QueueHealthConfig): void {\n const t = config.thresholdsSeconds;\n if (!Array.isArray(t) || t.length === 0 || t.some(v => typeof v !== 'number' || !Number.isFinite(v))) {\n throw new Error('[QueueHealthStorage] thresholdsSeconds must be a non-empty array of finite numbers.');\n }\n for (let i = 1; i < t.length; i++) {\n if (t[i]! <= t[i - 1]!) {\n throw new Error('[QueueHealthStorage] thresholdsSeconds must be strictly ascending.');\n }\n }\n}\n\n/** Validate untrusted JSON into a `QueueHealthConfig`, or `null` when invalid. */\nexport function parseQueueHealthConfig(body: unknown): QueueHealthConfig | null {\n if (typeof body !== 'object' || body === null) return null;\n const config = body as { thresholdsSeconds?: unknown };\n if (!Array.isArray(config.thresholdsSeconds)) return null;\n try {\n assertValidThresholds(config as QueueHealthConfig);\n } catch {\n return null;\n }\n return { thresholdsSeconds: [...(config as QueueHealthConfig).thresholdsSeconds] };\n}\n\n/**\n * Return `config.thresholdsSeconds` when valid, else the default. Validation\n * lives at the `saveConfig` write boundary, but `getConfig` round-trips a\n * stored JSON row — a corrupted or hand-edited row (empty / non-ascending)\n * would otherwise reach the chart and invert bucket colors, so the read route\n * re-validates and falls back.\n */\nexport function thresholdsOrDefault(config: QueueHealthConfig): number[] {\n return parseQueueHealthConfig(config)?.thresholdsSeconds ?? DEFAULT_QUEUE_HEALTH_CONFIG.thresholdsSeconds;\n}\n\nexport const QUEUE_HEALTH_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'queue_health_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'queue_health_settings_org_project_unique', columns: ['org_id', 'factory_project_id'] }],\n};\n\n/**\n * Queue-health settings storage, written once against the generic\n * `FactoryStorageOps` surface — works on any `FactoryStorage` backend.\n */\nexport class QueueHealthStorage extends FactoryStorageDomain {\n constructor() {\n super('queue-health');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([QUEUE_HEALTH_SETTINGS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('queue_health_settings', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /** Read the project's queue-health config, falling back to {@link DEFAULT_QUEUE_HEALTH_CONFIG}. */\n async getConfig(orgId: string, factoryProjectId: string): Promise<QueueHealthConfig> {\n const row = await this.#db.findOne<{ config: unknown }>('queue_health_settings', {\n org_id: orgId,\n factory_project_id: factoryProjectId,\n });\n return structuredClone(parseQueueHealthConfig(row?.config) ?? DEFAULT_QUEUE_HEALTH_CONFIG);\n }\n\n /** Upsert the project's queue-health config (`created_at` is preserved on update). */\n async saveConfig(orgId: string, factoryProjectId: string, config: QueueHealthConfig): Promise<void> {\n assertValidThresholds(config);\n const where = { org_id: orgId, factory_project_id: factoryProjectId };\n const updateExisting = () =>\n this.#db.updateAtomic('queue_health_settings', where, () => ({ config, updated_at: new Date() }));\n if (await updateExisting()) return;\n\n const now = new Date();\n try {\n await this.#db.insertOne('queue_health_settings', { ...where, config, created_at: now, updated_at: now });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the insert race — update the winning row under the backend's serialized write primitive.\n if (!(await updateExisting())) throw error;\n }\n }\n}\n"],"mappings":";AAaA,SAAS,sBAAsB,4BAA4B;AASpD,IAAM,8BAAiD;AAAA,EAC5D,mBAAmB,CAAC,OAAO,OAAO,MAAM;AAC1C;AAOO,SAAS,sBAAsB,QAAiC;AACrE,QAAM,IAAI,OAAO;AACjB,MAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,KAAK,EAAE,KAAK,OAAK,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC,GAAG;AACpG,UAAM,IAAI,MAAM,qFAAqF;AAAA,EACvG;AACA,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,EAAE,CAAC,KAAM,EAAE,IAAI,CAAC,GAAI;AACtB,YAAM,IAAI,MAAM,oEAAoE;AAAA,IACtF;AAAA,EACF;AACF;AAGO,SAAS,uBAAuB,MAAyC;AAC9E,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,SAAS;AACf,MAAI,CAAC,MAAM,QAAQ,OAAO,iBAAiB,EAAG,QAAO;AACrD,MAAI;AACF,0BAAsB,MAA2B;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,EAAE,mBAAmB,CAAC,GAAI,OAA6B,iBAAiB,EAAE;AACnF;AASO,SAAS,oBAAoB,QAAqC;AACvE,SAAO,uBAAuB,MAAM,GAAG,qBAAqB,4BAA4B;AAC1F;AAEO,IAAM,+BAAiD;AAAA,EAC5D,MAAM;AAAA,EACN,SAAS;AAAA,IACP,IAAI,EAAE,MAAM,UAAU;AAAA,IACtB,QAAQ,EAAE,MAAM,OAAO;AAAA,IACvB,oBAAoB,EAAE,MAAM,OAAO;AAAA,IACnC,QAAQ,EAAE,MAAM,OAAO;AAAA,IACvB,YAAY,EAAE,MAAM,YAAY;AAAA,IAChC,YAAY,EAAE,MAAM,YAAY;AAAA,EAClC;AAAA,EACA,eAAe,CAAC,EAAE,MAAM,4CAA4C,SAAS,CAAC,UAAU,oBAAoB,EAAE,CAAC;AACjH;AAMO,IAAM,qBAAN,cAAiC,qBAAqB;AAAA,EAC3D,cAAc;AACZ,UAAM,cAAc;AAAA,EACtB;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,KAAK,kBAAkB,CAAC,4BAA4B,CAAC;AAAA,EAC7D;AAAA,EAEA,MAAM,sBAAqC;AACzC,UAAM,KAAK,IAAI,WAAW,yBAAyB,CAAC,CAAC;AAAA,EACvD;AAAA,EAEA,IAAI,MAAyB;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,UAAU,OAAe,kBAAsD;AACnF,UAAM,MAAM,MAAM,KAAK,IAAI,QAA6B,yBAAyB;AAAA,MAC/E,QAAQ;AAAA,MACR,oBAAoB;AAAA,IACtB,CAAC;AACD,WAAO,gBAAgB,uBAAuB,KAAK,MAAM,KAAK,2BAA2B;AAAA,EAC3F;AAAA;AAAA,EAGA,MAAM,WAAW,OAAe,kBAA0B,QAA0C;AAClG,0BAAsB,MAAM;AAC5B,UAAM,QAAQ,EAAE,QAAQ,OAAO,oBAAoB,iBAAiB;AACpE,UAAM,iBAAiB,MACrB,KAAK,IAAI,aAAa,yBAAyB,OAAO,OAAO,EAAE,QAAQ,YAAY,oBAAI,KAAK,EAAE,EAAE;AAClG,QAAI,MAAM,eAAe,EAAG;AAE5B,UAAM,MAAM,oBAAI,KAAK;AACrB,QAAI;AACF,YAAM,KAAK,IAAI,UAAU,yBAAyB,EAAE,GAAG,OAAO,QAAQ,YAAY,KAAK,YAAY,IAAI,CAAC;AAAA,IAC1G,SAAS,OAAO;AACd,UAAI,EAAE,iBAAiB,sBAAuB,OAAM;AAEpD,UAAI,CAAE,MAAM,eAAe,EAAI,OAAM;AAAA,IACvC;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"file":"base.js","names":["#db"],"sources":["../../../../src/storage/domains/queue-health/base.ts"],"sourcesContent":["/**\n * Queue-health threshold configuration domain: per-project age buckets for the\n * Factory Overview queue-health chart.\n *\n * Stored per `(org, github_project)` — each connected GitHub project gets its\n * own age thresholds so a fast-moving automated pipeline and a slow human one\n * can bucket \"how old is the work\" differently. `thresholdsSeconds` is an\n * ordered-ascending list of age boundaries in seconds: an item younger than\n * the first boundary is green, younger than the second amber, younger than the\n * third orange, and red otherwise. Seconds (not hours) so sub-minute buckets\n * are representable for fast automated flows.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface QueueHealthConfig {\n /** Ordered-ascending age boundaries in seconds, e.g. `[14400, 86400, 259200]`. */\n thresholdsSeconds: number[];\n}\n\n/** Default: green <4h, amber <24h, orange <72h, red 72h+. */\nexport const DEFAULT_QUEUE_HEALTH_CONFIG: QueueHealthConfig = {\n thresholdsSeconds: [14400, 86400, 259200],\n};\n\n/**\n * Throw unless `thresholdsSeconds` is a non-empty ascending number list. A\n * descending config would silently invert bucket semantics, so validate at the\n * write boundary rather than trusting callers.\n */\nexport function assertValidThresholds(config: QueueHealthConfig): void {\n const t = config.thresholdsSeconds;\n if (!Array.isArray(t) || t.length === 0 || t.some(v => typeof v !== 'number' || !Number.isFinite(v))) {\n throw new Error('[QueueHealthStorage] thresholdsSeconds must be a non-empty array of finite numbers.');\n }\n for (let i = 1; i < t.length; i++) {\n if (t[i]! <= t[i - 1]!) {\n throw new Error('[QueueHealthStorage] thresholdsSeconds must be strictly ascending.');\n }\n }\n}\n\n/** Validate untrusted JSON into a `QueueHealthConfig`, or `null` when invalid. */\nexport function parseQueueHealthConfig(body: unknown): QueueHealthConfig | null {\n if (typeof body !== 'object' || body === null) return null;\n const config = body as { thresholdsSeconds?: unknown };\n if (!Array.isArray(config.thresholdsSeconds)) return null;\n try {\n assertValidThresholds(config as QueueHealthConfig);\n } catch {\n return null;\n }\n return { thresholdsSeconds: [...(config as QueueHealthConfig).thresholdsSeconds] };\n}\n\n/**\n * Return `config.thresholdsSeconds` when valid, else the default. Validation\n * lives at the `saveConfig` write boundary, but `getConfig` round-trips a\n * stored JSON row — a corrupted or hand-edited row (empty / non-ascending)\n * would otherwise reach the chart and invert bucket colors, so the read route\n * re-validates and falls back.\n */\nexport function thresholdsOrDefault(config: QueueHealthConfig): number[] {\n return parseQueueHealthConfig(config)?.thresholdsSeconds ?? DEFAULT_QUEUE_HEALTH_CONFIG.thresholdsSeconds;\n}\n\nexport const QUEUE_HEALTH_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'queue_health_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'queue_health_settings_org_project_unique', columns: ['org_id', 'factory_project_id'] }],\n};\n\n/**\n * Queue-health settings storage, written once against the generic\n * `FactoryStorageOps` surface — works on any `FactoryStorage` backend.\n */\nexport class QueueHealthStorage extends FactoryStorageDomain {\n constructor() {\n super('queue-health');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([QUEUE_HEALTH_SETTINGS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('queue_health_settings', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /** Read the project's queue-health config, falling back to {@link DEFAULT_QUEUE_HEALTH_CONFIG}. */\n async getConfig(orgId: string, factoryProjectId: string): Promise<QueueHealthConfig> {\n const row = await this.#db.findOne<{ config: unknown }>('queue_health_settings', {\n org_id: orgId,\n factory_project_id: factoryProjectId,\n });\n return structuredClone(parseQueueHealthConfig(row?.config) ?? DEFAULT_QUEUE_HEALTH_CONFIG);\n }\n\n /** Upsert the project's queue-health config (`created_at` is preserved on update). */\n async saveConfig(orgId: string, factoryProjectId: string, config: QueueHealthConfig): Promise<void> {\n assertValidThresholds(config);\n const where = { org_id: orgId, factory_project_id: factoryProjectId };\n const updateExisting = () =>\n this.#db.updateAtomic('queue_health_settings', where, () => ({ config, updated_at: new Date() }));\n if (await updateExisting()) return;\n\n const now = new Date();\n try {\n await this.#db.insertOne('queue_health_settings', { ...where, config, created_at: now, updated_at: now });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n // Lost the insert race — update the winning row under the backend's serialized write primitive.\n if (!(await updateExisting())) throw error;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsBA,MAAa,8BAAiD,EAC5D,mBAAmB;CAAC;CAAO;CAAO;AAAM,EAC1C;;;;;;AAOA,SAAgB,sBAAsB,QAAiC;CACrE,MAAM,IAAI,OAAO;CACjB,IAAI,CAAC,MAAM,QAAQ,CAAC,KAAK,EAAE,WAAW,KAAK,EAAE,MAAK,MAAK,OAAO,MAAM,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC,GACjG,MAAM,IAAI,MAAM,qFAAqF;CAEvG,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAC5B,IAAI,EAAE,MAAO,EAAE,IAAI,IACjB,MAAM,IAAI,MAAM,oEAAoE;AAG1F;;AAGA,SAAgB,uBAAuB,MAAyC;CAC9E,IAAI,OAAO,SAAS,YAAY,SAAS,MAAM,OAAO;CACtD,MAAM,SAAS;CACf,IAAI,CAAC,MAAM,QAAQ,OAAO,iBAAiB,GAAG,OAAO;CACrD,IAAI;EACF,sBAAsB,MAA2B;CACnD,QAAQ;EACN,OAAO;CACT;CACA,OAAO,EAAE,mBAAmB,CAAC,GAAI,OAA6B,iBAAiB,EAAE;AACnF;;;;;;;;AASA,SAAgB,oBAAoB,QAAqC;CACvE,OAAO,uBAAuB,MAAM,CAAC,EAAE,qBAAqB,4BAA4B;AAC1F;AAEA,MAAa,+BAAiD;CAC5D,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,oBAAoB,EAAE,MAAM,OAAO;EACnC,QAAQ,EAAE,MAAM,OAAO;EACvB,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CAAC;EAAE,MAAM;EAA4C,SAAS,CAAC,UAAU,oBAAoB;CAAE,CAAC;AACjH;;;;;AAMA,IAAa,qBAAb,cAAwC,qBAAqB;CAC3D,cAAc;EACZ,MAAM,cAAc;CACtB;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,4BAA4B,CAAC;CAC7D;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,yBAAyB,CAAC,CAAC;CACvD;CAEA,IAAIA,MAAyB;EAC3B,OAAO,KAAK;CACd;;CAGA,MAAM,UAAU,OAAe,kBAAsD;EACnF,MAAM,MAAM,MAAM,KAAKA,IAAI,QAA6B,yBAAyB;GAC/E,QAAQ;GACR,oBAAoB;EACtB,CAAC;EACD,OAAO,gBAAgB,uBAAuB,KAAK,MAAM,KAAK,2BAA2B;CAC3F;;CAGA,MAAM,WAAW,OAAe,kBAA0B,QAA0C;EAClG,sBAAsB,MAAM;EAC5B,MAAM,QAAQ;GAAE,QAAQ;GAAO,oBAAoB;EAAiB;EACpE,MAAM,uBACJ,KAAKA,IAAI,aAAa,yBAAyB,cAAc;GAAE;GAAQ,4BAAY,IAAI,KAAK;EAAE,EAAE;EAClG,IAAI,MAAM,eAAe,GAAG;EAE5B,MAAM,sBAAM,IAAI,KAAK;EACrB,IAAI;GACF,MAAM,KAAKA,IAAI,UAAU,yBAAyB;IAAE,GAAG;IAAO;IAAQ,YAAY;IAAK,YAAY;GAAI,CAAC;EAC1G,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;GAEpD,IAAI,CAAE,MAAM,eAAe,GAAI,MAAM;EACvC;CACF;AACF"}