@open-mercato/core 0.10.1-develop.7741.1.61a2652671 → 0.10.1-develop.7749.1.ea3edf75e3

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 (159) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/attachment_partition/index.js +2 -0
  3. package/dist/generated/entities/attachment_partition/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +1 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/helpers/integration/attachmentAccessFixtures.js +144 -0
  7. package/dist/helpers/integration/attachmentAccessFixtures.js.map +7 -0
  8. package/dist/modules/attachments/api/file/[id]/route.js +16 -10
  9. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  10. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +15 -9
  11. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  12. package/dist/modules/attachments/api/library/[id]/route.js +100 -56
  13. package/dist/modules/attachments/api/library/[id]/route.js.map +3 -3
  14. package/dist/modules/attachments/api/library/route.js +56 -17
  15. package/dist/modules/attachments/api/library/route.js.map +2 -2
  16. package/dist/modules/attachments/api/partitions/route.js +2 -0
  17. package/dist/modules/attachments/api/partitions/route.js.map +2 -2
  18. package/dist/modules/attachments/api/route.js +74 -34
  19. package/dist/modules/attachments/api/route.js.map +3 -3
  20. package/dist/modules/attachments/api/transfer/route.js +63 -37
  21. package/dist/modules/attachments/api/transfer/route.js.map +2 -2
  22. package/dist/modules/attachments/data/entities.js +3 -0
  23. package/dist/modules/attachments/data/entities.js.map +2 -2
  24. package/dist/modules/attachments/data/validators.js +41 -0
  25. package/dist/modules/attachments/data/validators.js.map +7 -0
  26. package/dist/modules/attachments/di.js +5 -2
  27. package/dist/modules/attachments/di.js.map +2 -2
  28. package/dist/modules/attachments/generators.js +29 -0
  29. package/dist/modules/attachments/generators.js.map +7 -0
  30. package/dist/modules/attachments/index.js +8 -1
  31. package/dist/modules/attachments/index.js.map +2 -2
  32. package/dist/modules/attachments/lib/access-errors.js +35 -0
  33. package/dist/modules/attachments/lib/access-errors.js.map +7 -0
  34. package/dist/modules/attachments/lib/access-mutation.js +23 -0
  35. package/dist/modules/attachments/lib/access-mutation.js.map +7 -0
  36. package/dist/modules/attachments/lib/access-protection.js +73 -0
  37. package/dist/modules/attachments/lib/access-protection.js.map +7 -0
  38. package/dist/modules/attachments/lib/access-query.js +74 -0
  39. package/dist/modules/attachments/lib/access-query.js.map +7 -0
  40. package/dist/modules/attachments/lib/access-registry.js +87 -0
  41. package/dist/modules/attachments/lib/access-registry.js.map +7 -0
  42. package/dist/modules/attachments/lib/access-runner.js +161 -0
  43. package/dist/modules/attachments/lib/access-runner.js.map +7 -0
  44. package/dist/modules/attachments/lib/access-types.js +1 -0
  45. package/dist/modules/attachments/lib/access-types.js.map +7 -0
  46. package/dist/modules/attachments/lib/attachment-service.js +14 -6
  47. package/dist/modules/attachments/lib/attachment-service.js.map +2 -2
  48. package/dist/modules/attachments/lib/createFromBuffer.js +2 -0
  49. package/dist/modules/attachments/lib/createFromBuffer.js.map +2 -2
  50. package/dist/modules/attachments/lib/partitions.js +3 -2
  51. package/dist/modules/attachments/lib/partitions.js.map +2 -2
  52. package/dist/modules/attachments/lib/scoped-upload-service.js +2 -0
  53. package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
  54. package/dist/modules/attachments/lib/target-access-service.js +12 -3
  55. package/dist/modules/attachments/lib/target-access-service.js.map +2 -2
  56. package/dist/modules/attachments/migrations/Migration20261001120000_attachments.js +38 -0
  57. package/dist/modules/attachments/migrations/Migration20261001120000_attachments.js.map +7 -0
  58. package/dist/modules/attachments/setup.js +10 -0
  59. package/dist/modules/attachments/setup.js.map +2 -2
  60. package/dist/modules/notifications/api/[id]/action/route.js +8 -0
  61. package/dist/modules/notifications/api/[id]/action/route.js.map +2 -2
  62. package/dist/modules/notifications/api/route.js +17 -0
  63. package/dist/modules/notifications/api/route.js.map +2 -2
  64. package/dist/modules/notifications/data/validators.js +1 -1
  65. package/dist/modules/notifications/data/validators.js.map +2 -2
  66. package/dist/modules/notifications/lib/notificationService.js +45 -3
  67. package/dist/modules/notifications/lib/notificationService.js.map +2 -2
  68. package/dist/modules/notifications/lib/routeHelpers.js +27 -0
  69. package/dist/modules/notifications/lib/routeHelpers.js.map +2 -2
  70. package/dist/modules/payment_gateways/lib/gateway-service.js +50 -34
  71. package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
  72. package/dist/modules/payment_gateways/lib/status-machine.js +10 -1
  73. package/dist/modules/payment_gateways/lib/status-machine.js.map +2 -2
  74. package/dist/modules/payment_gateways/lib/webhook-processor.js +1 -1
  75. package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
  76. package/dist/modules/staff/notifications.js +4 -2
  77. package/dist/modules/staff/notifications.js.map +2 -2
  78. package/dist/modules/workflows/data/entities.js.map +2 -2
  79. package/dist/modules/workflows/lib/activity-executor.js +18 -4
  80. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  81. package/dist/modules/workflows/lib/activity-types.js +2 -2
  82. package/dist/modules/workflows/lib/activity-types.js.map +2 -2
  83. package/dist/modules/workflows/lib/condition-handler.js +7 -1
  84. package/dist/modules/workflows/lib/condition-handler.js.map +2 -2
  85. package/dist/modules/workflows/lib/duration.js +13 -0
  86. package/dist/modules/workflows/lib/duration.js.map +2 -2
  87. package/dist/modules/workflows/lib/step-handler.js +9 -2
  88. package/dist/modules/workflows/lib/step-handler.js.map +2 -2
  89. package/dist/modules/workflows/lib/task-quick-action.js +13 -10
  90. package/dist/modules/workflows/lib/task-quick-action.js.map +2 -2
  91. package/dist/modules/workflows/lib/transition-handler.js +2 -1
  92. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  93. package/dist/modules/workflows/notifications.js +4 -0
  94. package/dist/modules/workflows/notifications.js.map +2 -2
  95. package/generated/entities/attachment_partition/index.ts +1 -0
  96. package/generated/entity-fields-registry.ts +1 -0
  97. package/package.json +7 -7
  98. package/src/helpers/integration/attachmentAccessFixtures.ts +141 -0
  99. package/src/modules/attachments/api/file/[id]/route.ts +12 -9
  100. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +10 -8
  101. package/src/modules/attachments/api/library/[id]/route.ts +87 -58
  102. package/src/modules/attachments/api/library/route.ts +40 -11
  103. package/src/modules/attachments/api/partitions/route.ts +2 -0
  104. package/src/modules/attachments/api/route.ts +68 -35
  105. package/src/modules/attachments/api/transfer/route.ts +65 -42
  106. package/src/modules/attachments/data/entities.ts +4 -0
  107. package/src/modules/attachments/data/validators.ts +36 -0
  108. package/src/modules/attachments/di.ts +4 -1
  109. package/src/modules/attachments/generators.ts +27 -0
  110. package/src/modules/attachments/i18n/de.json +4 -0
  111. package/src/modules/attachments/i18n/en.json +4 -0
  112. package/src/modules/attachments/i18n/es.json +4 -0
  113. package/src/modules/attachments/i18n/ko.json +4 -0
  114. package/src/modules/attachments/i18n/pl.json +4 -0
  115. package/src/modules/attachments/index.ts +18 -0
  116. package/src/modules/attachments/lib/access-errors.ts +32 -0
  117. package/src/modules/attachments/lib/access-mutation.ts +26 -0
  118. package/src/modules/attachments/lib/access-protection.ts +83 -0
  119. package/src/modules/attachments/lib/access-query.ts +86 -0
  120. package/src/modules/attachments/lib/access-registry.ts +117 -0
  121. package/src/modules/attachments/lib/access-runner.ts +183 -0
  122. package/src/modules/attachments/lib/access-types.ts +78 -0
  123. package/src/modules/attachments/lib/attachment-service.ts +8 -4
  124. package/src/modules/attachments/lib/createFromBuffer.ts +3 -0
  125. package/src/modules/attachments/lib/partitions.ts +3 -2
  126. package/src/modules/attachments/lib/scoped-upload-service.ts +2 -0
  127. package/src/modules/attachments/lib/target-access-service.ts +10 -3
  128. package/src/modules/attachments/migrations/.snapshot-open-mercato.json +17 -0
  129. package/src/modules/attachments/migrations/Migration20261001120000_attachments.ts +35 -0
  130. package/src/modules/attachments/setup.ts +10 -0
  131. package/src/modules/auth/i18n/de.json +3 -0
  132. package/src/modules/auth/i18n/en.json +3 -0
  133. package/src/modules/auth/i18n/es.json +3 -0
  134. package/src/modules/auth/i18n/ko.json +3 -0
  135. package/src/modules/auth/i18n/pl.json +3 -0
  136. package/src/modules/notifications/api/[id]/action/route.ts +8 -0
  137. package/src/modules/notifications/api/route.ts +17 -0
  138. package/src/modules/notifications/data/validators.ts +1 -1
  139. package/src/modules/notifications/i18n/de.json +2 -0
  140. package/src/modules/notifications/i18n/en.json +2 -0
  141. package/src/modules/notifications/i18n/es.json +2 -0
  142. package/src/modules/notifications/i18n/ko.json +2 -0
  143. package/src/modules/notifications/i18n/pl.json +2 -0
  144. package/src/modules/notifications/lib/notificationService.ts +67 -1
  145. package/src/modules/notifications/lib/routeHelpers.ts +38 -0
  146. package/src/modules/payment_gateways/lib/gateway-service.ts +50 -33
  147. package/src/modules/payment_gateways/lib/status-machine.ts +19 -0
  148. package/src/modules/payment_gateways/lib/webhook-processor.ts +1 -1
  149. package/src/modules/staff/notifications.ts +2 -0
  150. package/src/modules/workflows/AGENTS.md +2 -2
  151. package/src/modules/workflows/data/entities.ts +6 -0
  152. package/src/modules/workflows/lib/activity-executor.ts +36 -4
  153. package/src/modules/workflows/lib/activity-types.ts +2 -2
  154. package/src/modules/workflows/lib/condition-handler.ts +16 -1
  155. package/src/modules/workflows/lib/duration.ts +25 -0
  156. package/src/modules/workflows/lib/step-handler.ts +16 -1
  157. package/src/modules/workflows/lib/task-quick-action.ts +19 -10
  158. package/src/modules/workflows/lib/transition-handler.ts +2 -1
  159. package/src/modules/workflows/notifications.ts +4 -0
@@ -106,6 +106,21 @@ export function resolveMaxConditionAttempts(): number {
106
106
  return parsed
107
107
  }
108
108
 
109
+ /**
110
+ * The attempt cap one waiter actually gets: the configured floor, raised to the
111
+ * number of polls its own timeout needs (`timeoutMs / pollIntervalMs`, plus
112
+ * the entry poll). A fixed 1000 would force a 90-day wait polled hourly to
113
+ * time out after ~42 days. The deadline still ends the wait first; this only
114
+ * bounds a loop whose deadline arithmetic went wrong.
115
+ */
116
+ export function resolveConditionAttemptCap(
117
+ config: Pick<WaitForConditionConfig, 'timeoutMs' | 'pollIntervalMs'>
118
+ ): number {
119
+ const floor = resolveMaxConditionAttempts()
120
+ if (!(config.timeoutMs > 0) || !(config.pollIntervalMs > 0)) return floor
121
+ return Math.max(floor, Math.ceil(config.timeoutMs / config.pollIntervalMs) + 1)
122
+ }
123
+
109
124
  /**
110
125
  * Read and normalise a WAIT_FOR_CONDITION step's config. Fails closed: the
111
126
  * predicate and the timeout are both mandatory, and the poll interval is
@@ -499,7 +514,7 @@ export async function evaluateWaitCondition(
499
514
 
500
515
  const deadlineMs = Date.parse(options.deadlineAt)
501
516
  const deadlinePassed = Number.isFinite(deadlineMs) ? nowMs >= deadlineMs : true
502
- const attemptCapReached = attempts >= resolveMaxConditionAttempts()
517
+ const attemptCapReached = attempts >= resolveConditionAttemptCap(waiter.config)
503
518
 
504
519
  if (deadlinePassed || attemptCapReached) {
505
520
  const eventLogger = container.resolve<typeof eventLoggerModule>('eventLogger')
@@ -147,6 +147,31 @@ export function calculateDueDate(duration: string, from: Date = new Date()): Dat
147
147
  return new Date(from.getTime() + parseDuration(duration))
148
148
  }
149
149
 
150
+ /**
151
+ * Largest delay Node's `setTimeout` honours (a signed 32-bit millisecond count,
152
+ * ~24.86 days). A longer delay is not rejected — it fires after 1 ms with only a
153
+ * `TimeoutOverflowWarning` — so an in-process wait must refuse it explicitly.
154
+ */
155
+ export const MAX_TIMER_DELAY_MS = 2_147_483_647
156
+
157
+ /**
158
+ * A WAIT longer than this is not slept inside the executing request: when it is
159
+ * the last activity of its transition it is handed to the activity queue (the
160
+ * path an `async: true` WAIT already takes). One minute keeps the short pauses
161
+ * people use for pacing inline while anything that would hold a request or a
162
+ * worker slot open for minutes becomes durable.
163
+ */
164
+ export const MAX_INLINE_WAIT_MS = 60_000
165
+
166
+ export function assertInProcessWaitDelay(delayMs: number): number {
167
+ if (delayMs > MAX_TIMER_DELAY_MS) {
168
+ throw new Error(
169
+ `WAIT activity: a ${delayMs}ms delay cannot be slept in-process (limit ${MAX_TIMER_DELAY_MS}ms); make it the last activity of its transition, mark it async, or use a WAIT_FOR_TIMER step`
170
+ )
171
+ }
172
+ return delayMs
173
+ }
174
+
150
175
  export function calculateWaitDelayMs(config: { duration?: string; until?: string }): number {
151
176
  if (config.until) {
152
177
  const targetDate = new Date(config.until)
@@ -222,6 +222,21 @@ async function emitStepMilestone(em: EntityManager, stepInstance: StepInstance):
222
222
  }
223
223
  }
224
224
 
225
+ /**
226
+ * `step_instances.execution_time_ms` is a Postgres `integer`, so a step that
227
+ * lasted 2^31 ms (~24.86 days) or longer cannot be stored there — the flush
228
+ * that completes the step would throw after its task already committed,
229
+ * stranding the run. Such a duration persists as `null` (every reader already
230
+ * falls back to `exitedAt - enteredAt`); the exact value still travels on the
231
+ * `STEP_EXITED` event, whose payload is jsonb.
232
+ */
233
+ export const MAX_PERSISTED_EXECUTION_TIME_MS = 2_147_483_647
234
+
235
+ export function toPersistableExecutionTimeMs(executionTimeMs: number | null): number | null {
236
+ if (executionTimeMs === null || !Number.isFinite(executionTimeMs)) return null
237
+ return Math.abs(executionTimeMs) > MAX_PERSISTED_EXECUTION_TIME_MS ? null : executionTimeMs
238
+ }
239
+
225
240
  export async function exitStep(
226
241
  em: EntityManager,
227
242
  stepInstance: StepInstance,
@@ -239,7 +254,7 @@ export async function exitStep(
239
254
  stepInstance.status = 'COMPLETED'
240
255
  stepInstance.outputData = outputData || null
241
256
  stepInstance.exitedAt = now
242
- stepInstance.executionTimeMs = executionTimeMs
257
+ stepInstance.executionTimeMs = toPersistableExecutionTimeMs(executionTimeMs)
243
258
  stepInstance.updatedAt = now
244
259
 
245
260
  await em.flush()
@@ -43,6 +43,24 @@ export const TASK_QUICK_ACTION_VIEW_ID = 'view'
43
43
 
44
44
  export const TASK_DEEP_LINK_HREF = '/backend/tasks/{sourceEntityId}'
45
45
 
46
+ /** Feature the clicking user must hold to complete a task from its notification. */
47
+ export const TASK_COMPLETE_REQUIRED_FEATURE = 'workflows.tasks.complete'
48
+
49
+ /**
50
+ * The one-click completion button. Shared by {@link buildTaskQuickActions} and the
51
+ * `workflows.task.assigned` type declaration, so the stored action id and command id always
52
+ * match what the notifications module allows to dispatch.
53
+ */
54
+ export function buildTaskQuickCompleteAction(): NotificationTypeAction {
55
+ return {
56
+ id: TASK_QUICK_ACTION_COMPLETE_ID,
57
+ labelKey: 'workflows.notifications.task.actions.complete',
58
+ variant: 'default',
59
+ icon: 'check',
60
+ commandId: TASK_COMPLETE_COMMAND_ID,
61
+ }
62
+ }
63
+
46
64
  export interface TaskQuickActionInput {
47
65
  /** Decision buttons resolved for this task. */
48
66
  decisions?: { id: string }[] | null
@@ -90,16 +108,7 @@ export function buildTaskQuickActions(input: TaskQuickActionInput): Notification
90
108
 
91
109
  if (!isOneClickTask(input)) return [viewAction]
92
110
 
93
- return [
94
- viewAction,
95
- {
96
- id: TASK_QUICK_ACTION_COMPLETE_ID,
97
- labelKey: 'workflows.notifications.task.actions.complete',
98
- variant: 'default',
99
- icon: 'check',
100
- commandId: TASK_COMPLETE_COMMAND_ID,
101
- },
102
- ]
111
+ return [viewAction, buildTaskQuickCompleteAction()]
103
112
  }
104
113
 
105
114
  /**
@@ -522,7 +522,8 @@ export async function executeTransitionForToken(
522
522
  em,
523
523
  container,
524
524
  transition.activities as ActivityDefinition[],
525
- activityContext
525
+ activityContext,
526
+ { queueLongInlineWaits: true }
526
527
  )
527
528
 
528
529
  activityResults.push(...results)
@@ -1,4 +1,5 @@
1
1
  import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+ import { buildTaskQuickCompleteAction, TASK_COMPLETE_REQUIRED_FEATURE } from './lib/task-quick-action'
2
3
 
3
4
  const taskDeepLink = '/backend/tasks/{sourceEntityId}'
4
5
 
@@ -21,6 +22,9 @@ export const notificationTypes: NotificationTypeDefinition[] = [
21
22
  icon: 'clipboard-list',
22
23
  severity: 'info',
23
24
  actions: [viewTaskAction],
25
+ optionalActions: [
26
+ { ...buildTaskQuickCompleteAction(), requiredFeatures: [TASK_COMPLETE_REQUIRED_FEATURE] },
27
+ ],
24
28
  linkHref: taskDeepLink,
25
29
  expiresAfterHours: 168, // 7 days
26
30
  },