@ossy/platform 3.23.0 → 3.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/platform",
3
- "version": "3.23.0",
3
+ "version": "3.24.1",
4
4
  "description": "Ossy application server runtime",
5
5
  "repository": {
6
6
  "type": "git",
@@ -49,18 +49,18 @@
49
49
  "@aws-sdk/util-format-url": "^3.972.17",
50
50
  "@modelcontextprotocol/sdk": "^1.12.1",
51
51
  "@ossy/config": "^3.0.9",
52
- "@ossy/email": "^3.23.0",
53
- "@ossy/event-store": "^3.20.1",
52
+ "@ossy/email": "^3.24.1",
53
+ "@ossy/event-store": "^3.24.1",
54
54
  "@ossy/locale": "^3.4.0",
55
- "@ossy/manifest": "^3.20.1",
56
- "@ossy/observability": "^3.0.9",
57
- "@ossy/policies": "^3.0.9",
55
+ "@ossy/manifest": "^3.24.0",
56
+ "@ossy/observability": "^3.24.1",
57
+ "@ossy/policies": "^3.24.1",
58
58
  "@ossy/router": "^3.0.9",
59
- "@ossy/schema": "^3.20.1",
60
- "@ossy/sdk": "^3.20.1",
61
- "@ossy/tokens": "^3.11.1",
62
- "@ossy/users": "^3.20.1",
63
- "@ossy/workspaces": "^3.22.0",
59
+ "@ossy/schema": "^3.24.0",
60
+ "@ossy/sdk": "^3.24.0",
61
+ "@ossy/tokens": "^3.24.1",
62
+ "@ossy/users": "^3.24.1",
63
+ "@ossy/workspaces": "^3.24.1",
64
64
  "cookie-parser": "^1.4.7",
65
65
  "dotenv": ">=16.0.0 <18.0.0",
66
66
  "express": ">=5.0.0 <6.0.0",
@@ -81,5 +81,5 @@
81
81
  "Dockerfile",
82
82
  "docker-healthcheck.js"
83
83
  ],
84
- "gitHead": "fba601366c69184bbd82d990429d1a43152645c9"
84
+ "gitHead": "d0e7eda0397239d5e8a8cd5115d9c91959b4103f"
85
85
  }
@@ -58,7 +58,7 @@ export function ProxyInternal () {
58
58
  }
59
59
 
60
60
  if (req.originalUrl.startsWith('/@ossy/users/me/app-settings') && req.method === 'GET') {
61
- log.info('[@ossy/platform][proxy] GET /@ossy/users/me/app-settings')
61
+ log.debug('[@ossy/platform][proxy] GET /@ossy/users/me/app-settings')
62
62
  const userSettings = readUserAppSettings(req)
63
63
  const workspaceId = readWorkspaceIdFromCookies(req)
64
64
  res.status(200)
@@ -68,13 +68,13 @@ export function ProxyInternal () {
68
68
 
69
69
  if (!isRemoteOssyApiUrl()) {
70
70
  const dest = ossyProxyLocalPath(req.originalUrl)
71
- log.info(`[@ossy/platform][proxy] ${req.method} ${req.originalUrl} → ${dest} (same origin)`)
71
+ log.debug(`[@ossy/platform][proxy] ${req.method} ${req.originalUrl} → ${dest} (same origin)`)
72
72
  req.url = dest
73
73
  req.originalUrl = dest
74
74
  return next()
75
75
  }
76
76
 
77
- log.info(`[@ossy/platform][proxy] ${req.method} ${req.originalUrl}`)
77
+ log.debug(`[@ossy/platform][proxy] ${req.method} ${req.originalUrl}`)
78
78
 
79
79
  const domain = String(process.env.OSSY_API_URL).trim()
80
80
  .replace(/\/api\/v0\/?$/, '')
@@ -21,7 +21,7 @@ export async function ackInboundMessages (messages, { file, deleteMessage, log }
21
21
  for (const message of messages ?? []) {
22
22
  try {
23
23
  const result = await file(message.Body)
24
- if (result?.skipped) log?.info('[ses/inbound] skipped receipt', result)
24
+ if (result?.skipped) log?.debug('[ses/inbound] skipped receipt', result)
25
25
  await deleteMessage(message)
26
26
  } catch (error) {
27
27
  if (error?.name === 'NoSuchKey') {
@@ -128,7 +128,7 @@ export async function loadSite (domain) {
128
128
  const buildDir = path.join(BASE_DIR, domain)
129
129
  const cmsLocation = `/@ossy/apps/${domain}`
130
130
 
131
- log.info(`[@ossy/platform] Loading site for ${domain} (workspace ${workspaceId}) from CMS ${cmsLocation}…`)
131
+ log.debug(`[@ossy/platform] Loading site for ${domain} (workspace ${workspaceId}) from CMS ${cmsLocation}…`)
132
132
 
133
133
  const sdk = SDK.of({
134
134
  apiUrl: API_URL,
@@ -149,7 +149,7 @@ export async function loadSite (domain) {
149
149
 
150
150
  await runWithConcurrency(tasks, DOWNLOAD_CONCURRENCY)
151
151
 
152
- log.info(`[@ossy/platform] Site ${domain} ready — ${files.length} file(s) in ${buildDir}`)
152
+ log.debug(`[@ossy/platform] Site ${domain} ready — ${files.length} file(s) in ${buildDir}`)
153
153
 
154
154
  cache.set(domain, { buildDir, workspaceId, loadedAt: Date.now() })
155
155
  return buildDir
@@ -161,5 +161,5 @@ export async function loadSite (domain) {
161
161
  */
162
162
  export function invalidateSite (domain) {
163
163
  cache.delete(domain)
164
- log.info(`[@ossy/platform] Cache invalidated for ${domain}`)
164
+ log.debug(`[@ossy/platform] Cache invalidated for ${domain}`)
165
165
  }
@@ -57,7 +57,7 @@ export function createS3StorageClient ({
57
57
  },
58
58
 
59
59
  createUploadUrl ({ ContentType, ContentLength, Key, expiresIn = 60 * 60 * 24 }) {
60
- log.info('[S3Storage][createUploadUrl] Creating upload URL')
60
+ log.debug('[S3Storage][createUploadUrl] Creating upload URL')
61
61
 
62
62
  const clientParams = {
63
63
  Bucket: bucket,
@@ -88,7 +88,7 @@ export function createS3StorageClient ({
88
88
  const Key = toS3Key(logicalKey)
89
89
  const s3Client = createAwsClient()
90
90
  const Body = Buffer.isBuffer(body) ? body : Buffer.from(body)
91
- log.info('[S3Storage][save] Putting object', { key: logicalKey, bytes: Body.length })
91
+ log.debug('[S3Storage][save] Putting object', { key: logicalKey, bytes: Body.length })
92
92
  await s3Client.send(new PutObjectCommand({
93
93
  Bucket: bucket,
94
94
  Key,
@@ -46,7 +46,7 @@ export class S3Client {
46
46
  }
47
47
 
48
48
  static createUploadUrl({ ContentType, ContentLength, Key, Bucket, expiresIn = 60 * 60 * 24 }) {
49
- log.info('[S3Client][createUploadUrl()] Creating upload URL')
49
+ log.debug('[S3Client][createUploadUrl()] Creating upload URL')
50
50
 
51
51
  const bucket = Bucket ?? ConfigService.MediaRepository
52
52
  if (!bucket) {
@@ -157,7 +157,7 @@ export class ChangeStream {
157
157
  })
158
158
 
159
159
  changeStream.on('change', (change) => {
160
- log.info('[ChangeStream] Change detected')
160
+ log.debug('[ChangeStream] Change detected')
161
161
  const event = change.fullDocument
162
162
  if (event) {
163
163
  ProjectionRebuild.dispatch(event).catch((error) => {
@@ -370,20 +370,27 @@ export class TaskService {
370
370
  if (!event) return
371
371
  if (AUDIT_DISPATCH_TYPES.has(event.type)) return
372
372
 
373
- const baseSdk = sdk ?? await sdkBoundTo()
374
- const effectiveSdk = typeof baseSdk?.withEventContext === 'function'
375
- ? baseSdk.withEventContext(event)
376
- : baseSdk
377
-
378
373
  const workspaceId = event.payload?.belongsTo ?? event.belongsTo ?? null
374
+ const matches = []
379
375
 
380
- for (const { metadata, handler } of TaskService._tasks) {
381
- const { triggers, armed } = await TaskService._eventTriggersForDispatch(metadata, workspaceId)
376
+ for (const task of TaskService._tasks) {
377
+ const { triggers, armed } = await TaskService._eventTriggersForDispatch(task.metadata, workspaceId)
382
378
  if (!armed) continue
383
379
 
384
380
  const matchedTrigger = triggers.find((trigger) => TaskService._matchesEventTrigger(trigger, event))
385
381
  if (!matchedTrigger) continue
382
+ matches.push({ ...task, matchedTrigger })
383
+ }
384
+
385
+ // Loading the bot reads the user aggregate. Do that only when a task will run.
386
+ if (matches.length === 0) return
387
+
388
+ const baseSdk = sdk ?? await sdkBoundTo()
389
+ const effectiveSdk = typeof baseSdk?.withEventContext === 'function'
390
+ ? baseSdk.withEventContext(event)
391
+ : baseSdk
386
392
 
393
+ for (const { metadata, handler, matchedTrigger } of matches) {
387
394
  const _taskId = metadata.id
388
395
  const claimKey = buildEventClaimKey(_taskId, event.id)
389
396
 
@@ -391,11 +398,11 @@ export class TaskService {
391
398
  void (async () => {
392
399
  const won = await tryClaimDispatch(claimKey)
393
400
  if (!won) {
394
- _serviceLog.info(`Skipping task "${_taskId}" — dispatch claim lost`)
401
+ _serviceLog.debug(`Skipping task "${_taskId}" — dispatch claim lost`)
395
402
  return
396
403
  }
397
404
 
398
- _serviceLog.info(`Dispatching task "${_taskId}"`)
405
+ _serviceLog.debug(`Dispatching task "${_taskId}"`)
399
406
 
400
407
  const taskContext = buildTaskContext(
401
408
  { sdk: effectiveSdk, integrations: IntegrationService, log: createLogger(_taskId) },
@@ -470,11 +477,11 @@ export class TaskService {
470
477
  void (async () => {
471
478
  const won = await tryClaimDispatch(claimKey)
472
479
  if (!won) {
473
- _serviceLog.info(`Skipping on_action task "${_taskId}" — dispatch claim lost`)
480
+ _serviceLog.debug(`Skipping on_action task "${_taskId}" — dispatch claim lost`)
474
481
  return
475
482
  }
476
483
 
477
- _serviceLog.info(`Dispatching on_action task "${_taskId}" for action "${actionId}"`)
484
+ _serviceLog.debug(`Dispatching on_action task "${_taskId}" for action "${actionId}"`)
478
485
 
479
486
  const taskContext = buildTaskContext(
480
487
  {
@@ -587,14 +594,14 @@ export class TaskService {
587
594
  void (async () => {
588
595
  const won = await tryClaimDispatch(claimKey)
589
596
  if (!won) {
590
- _serviceLog.info(
597
+ _serviceLog.debug(
591
598
  `Skipping schedule for task "${metadata.id}" — dispatch claim lost`
592
599
  + (job.workspaceId ? ` (workspace ${job.workspaceId})` : ''),
593
600
  )
594
601
  return
595
602
  }
596
603
 
597
- _serviceLog.info(
604
+ _serviceLog.debug(
598
605
  `Schedule fired for task "${metadata.id}"`
599
606
  + (job.workspaceId ? ` (workspace ${job.workspaceId})` : ''),
600
607
  )
@@ -40,7 +40,7 @@ export class TokenService {
40
40
  }))
41
41
  }
42
42
 
43
- log.info('[TokenService]: Token verified')
43
+ log.debug('[TokenService]: Token verified')
44
44
  resolve(payload)
45
45
  })
46
46
 
@@ -71,7 +71,7 @@ export class UsersMiddleware {
71
71
  return asAnonymous()
72
72
  }
73
73
 
74
- log.info('[UsersMiddleware] Authenticating')
74
+ log.debug('[UsersMiddleware] Authenticating')
75
75
  log.debug('[UsersMiddleware] authToken', { authToken })
76
76
 
77
77
  const authStarted = performance.now()
@@ -98,7 +98,7 @@ export class UsersMiddleware {
98
98
  return PoliciesQueries.GetPoliciesForUser(user.id)
99
99
  .then(policies => {
100
100
  finishAuth(() => {
101
- log.info('[UsersMiddleware] Resolved user', {
101
+ log.debug('[UsersMiddleware] Resolved user', {
102
102
  elapsedMs: Math.round(performance.now() - authStarted),
103
103
  })
104
104
  req.userId = user.id
@@ -83,10 +83,16 @@ export async function upsertEmbedding (doc) {
83
83
  }
84
84
 
85
85
  /**
86
- * Delete the embedding for a resource (e.g. when the resource is removed).
86
+ * Load the stored embedding for one resource.
87
87
  *
88
88
  * @param {string} resourceId
89
+ * @returns {Promise<object | null>}
89
90
  */
91
+ export async function getEmbedding (resourceId) {
92
+ if (!resourceId) return null
93
+ return col().findOne({ resourceId }, { projection: { _id: 0 } })
94
+ }
95
+
90
96
  export async function deleteEmbedding (resourceId) {
91
97
  await col().deleteOne({ resourceId })
92
98
  }