@remit/backend 0.0.1 → 0.0.3

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,82 +1,86 @@
1
1
  {
2
- "name": "@remit/backend",
3
- "version": "0.0.1",
4
- "description": "Remit Mail Inspector API backend",
5
- "license": "MIT",
6
- "author": "",
7
- "type": "module",
8
- "main": "src/index.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./src/index.ts",
12
- "default": "./src/index.ts"
13
- },
14
- "./client": {
15
- "types": "./src/service/dynamodb.ts",
16
- "default": "./src/service/dynamodb.ts"
17
- },
18
- "./organize": {
19
- "types": "./src/service/organize.ts",
20
- "default": "./src/service/organize.ts"
21
- },
22
- "./trigger-sync": {
23
- "types": "./src/service/trigger-sync.ts",
24
- "default": "./src/service/trigger-sync.ts"
25
- },
26
- "./content-handler": {
27
- "types": "./dev-server/content-handler.ts",
28
- "default": "./dev-server/content-handler.ts"
29
- }
30
- },
31
- "scripts": {
32
- "test:typecheck": "tsgo --noEmit",
33
- "test:run": "node --env-file .env.test --import tsx --test 'src/**/*.test.ts' 'dev-server/**/*.test.ts'",
34
- "test": "npm run test:typecheck && npm run test:run",
35
- "bundle": "esbuild 'src/index.ts' --sourcemap --bundle --platform=node --outfile=dist/index.js",
36
- "dev": "node --env-file .env.test --watch --import tsx dev-server/server.ts",
37
- "dev:remote": "node --env-file ../../.env.dev --watch --import tsx dev-server/server.ts"
38
- },
39
- "devDependencies": {
40
- "@aws-sdk/client-secrets-manager": "*",
41
- "@aws-sdk/client-sqs": "*",
42
- "@aws-sdk/core": "*",
43
- "@remit/data-ports": "*",
44
- "@remit/electrodb-entities": "*",
45
- "@remit/auth-service": "*",
46
- "@remit/drizzle-service": "*",
47
- "drizzle-orm": "^0.45.2",
48
- "@remit/domain-enums": "*",
49
- "@remit/mail-oauth-service": "*",
50
- "@remit/mailbox-service": "*",
51
- "@remit/secrets-service": "*",
52
- "@remit/api-openapi-types": "*",
53
- "@remit/search-index-worker": "*",
54
- "@remit/search-service": "*",
55
- "@remit/sqs-client": "*",
56
- "@remit/storage-service": "*",
57
- "@types/aws-lambda": "*",
58
- "@types/express": "^5.0.3",
59
- "@types/swagger-ui-express": "^4.1.8",
60
- "electrodb": "*",
61
- "esbuild": "^0.27.7",
62
- "expect-env": "*",
63
- "express": "^5.1.0",
64
- "openapi-backend": "*",
65
- "p-map": "*",
66
- "@remit/logger-lambda": "*",
67
- "swagger-ui-express": "^5.0.1",
68
- "tsx": "*"
69
- },
70
- "dependencies": {
71
- "jose": "^6.2.3",
72
- "mailparser": "^3.9.14"
73
- },
74
- "publishConfig": {
75
- "access": "public"
76
- },
77
- "repository": {
78
- "type": "git",
79
- "url": "git+https://github.com/remit-mail/remit.git",
80
- "directory": "packages/backend"
81
- }
2
+ "name": "@remit/backend",
3
+ "version": "0.0.3",
4
+ "description": "Remit Mail Inspector API backend",
5
+ "license": "MIT",
6
+ "author": "",
7
+ "type": "module",
8
+ "main": "src/index.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./src/index.ts",
12
+ "default": "./src/index.ts"
13
+ },
14
+ "./client": {
15
+ "types": "./src/service/dynamodb.ts",
16
+ "default": "./src/service/dynamodb.ts"
17
+ },
18
+ "./create-remit-client": {
19
+ "types": "./src/service/create-remit-client.ts",
20
+ "default": "./src/service/create-remit-client.ts"
21
+ },
22
+ "./organize": {
23
+ "types": "./src/service/organize.ts",
24
+ "default": "./src/service/organize.ts"
25
+ },
26
+ "./trigger-sync": {
27
+ "types": "./src/service/trigger-sync.ts",
28
+ "default": "./src/service/trigger-sync.ts"
29
+ },
30
+ "./content-handler": {
31
+ "types": "./dev-server/content-handler.ts",
32
+ "default": "./dev-server/content-handler.ts"
33
+ }
34
+ },
35
+ "scripts": {
36
+ "test:typecheck": "tsgo --noEmit",
37
+ "test:run": "node --env-file .env.test --import tsx --test 'src/**/*.test.ts' 'dev-server/**/*.test.ts'",
38
+ "test": "npm run test:typecheck && npm run test:run",
39
+ "bundle": "esbuild 'src/index.ts' --sourcemap --bundle --platform=node --outfile=dist/index.js",
40
+ "dev": "node --env-file .env.test --watch --import tsx dev-server/server.ts",
41
+ "dev:remote": "node --env-file ../../.env.dev --watch --import tsx dev-server/server.ts"
42
+ },
43
+ "devDependencies": {
44
+ "@aws-sdk/client-secrets-manager": "*",
45
+ "@aws-sdk/client-sqs": "*",
46
+ "@aws-sdk/core": "*",
47
+ "@remit/electrodb-entities": "*",
48
+ "drizzle-orm": "^0.45.2",
49
+ "@remit/search-index-worker": "*",
50
+ "@types/aws-lambda": "*",
51
+ "@types/express": "^5.0.3",
52
+ "@types/swagger-ui-express": "^4.1.8",
53
+ "electrodb": "*",
54
+ "esbuild": "^0.27.7",
55
+ "expect-env": "*",
56
+ "express": "^5.1.0",
57
+ "openapi-backend": "*",
58
+ "p-map": "*",
59
+ "swagger-ui-express": "^5.0.1",
60
+ "tsx": "*"
61
+ },
62
+ "dependencies": {
63
+ "jose": "^6.2.3",
64
+ "mailparser": "^3.9.14",
65
+ "@remit/data-ports": "*",
66
+ "@remit/domain-enums": "*",
67
+ "@remit/api-openapi-types": "*",
68
+ "@remit/logger-lambda": "*",
69
+ "@remit/mail-oauth-service": "*",
70
+ "@remit/secrets-service": "*",
71
+ "@remit/mailbox-service": "*",
72
+ "@remit/search-service": "*",
73
+ "@remit/storage-service": "*",
74
+ "@remit/auth-service": "*",
75
+ "@remit/drizzle-service": "*",
76
+ "@remit/sqs-client": "*"
77
+ },
78
+ "publishConfig": {
79
+ "access": "public"
80
+ },
81
+ "repository": {
82
+ "type": "git",
83
+ "url": "git+https://github.com/remit-mail/remit.git",
84
+ "directory": "packages/backend"
85
+ }
82
86
  }
@@ -1,6 +1,6 @@
1
- import type { AutoMovedInfo } from "@remit/api-openapi-types";
2
1
  import type { MessageItem } from "@remit/data-ports";
3
2
  import { PlacementConfidence } from "@remit/domain-enums";
3
+ import type { AutoMovedInfo } from "@remit/api-openapi-types";
4
4
 
5
5
  /**
6
6
  * Project a Message's internal placement verdict to the public `AutoMovedInfo`
@@ -1,4 +1,3 @@
1
- import type { ThreadMessageResponse } from "@remit/api-openapi-types";
2
1
  import type {
3
2
  AddressItem,
4
3
  MessageItem,
@@ -6,6 +5,7 @@ import type {
6
5
  } from "@remit/data-ports";
7
6
  import { deriveAddressId } from "@remit/data-ports/id";
8
7
  import { MessageCategory, SenderTrust, StarColor } from "@remit/domain-enums";
8
+ import type { ThreadMessageResponse } from "@remit/api-openapi-types";
9
9
  import { deriveAutoMoved } from "./autoMoved.js";
10
10
  import { deriveSenderTrust } from "./senderTrust.js";
11
11
 
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
- import type { ThreadMessageResponse } from "@remit/api-openapi-types";
4
3
  import { MessageCategory, SenderTrust, StarColor } from "@remit/domain-enums";
4
+ import type { ThreadMessageResponse } from "@remit/api-openapi-types";
5
5
  import {
6
6
  filterByOffRowCriteria,
7
7
  hasOffRowCriteria,
@@ -1,5 +1,5 @@
1
- import type { AddressFlags } from "@remit/api-openapi-types";
2
1
  import { SenderTrust } from "@remit/domain-enums";
2
+ import type { AddressFlags } from "@remit/api-openapi-types";
3
3
 
4
4
  export type SenderTrustValue = (typeof SenderTrust)[keyof typeof SenderTrust];
5
5
 
@@ -1,10 +1,10 @@
1
+ import type { AccountItem } from "@remit/data-ports";
2
+ import { ConflictError } from "@remit/data-ports/errors";
3
+ import { AccountAuthType } from "@remit/domain-enums";
1
4
  import type {
2
5
  AccountResponse,
3
6
  FolderAppointment,
4
7
  } from "@remit/api-openapi-types";
5
- import type { AccountItem } from "@remit/data-ports";
6
- import { ConflictError } from "@remit/data-ports/errors";
7
- import { AccountAuthType } from "@remit/domain-enums";
8
8
  import type { AccountOverrides } from "./account-overrides.js";
9
9
  import type { AccountSignature } from "./account-signature.js";
10
10
 
@@ -1,4 +1,3 @@
1
- import type { MutedFlag } from "@remit/api-openapi-types";
2
1
  import type {
3
2
  AccountSettingItem,
4
3
  IAccountSettingRepository,
@@ -8,6 +7,7 @@ import {
8
7
  composeSettingName,
9
8
  SETTING_NAME_SEPARATOR,
10
9
  } from "@remit/data-ports/account-settings";
10
+ import type { MutedFlag } from "@remit/api-openapi-types";
11
11
 
12
12
  /**
13
13
  * RFC 032 Tier 1: the per-account display name and mute flag, and the
@@ -1,12 +1,4 @@
1
1
  import { SendMessageCommand } from "@aws-sdk/client-sqs";
2
- import type {
3
- AccountResponse,
4
- CreateAccountInput,
5
- DeleteAccountResponse,
6
- TestConnectionInput,
7
- TestConnectionResponse,
8
- UpdateAccountInput,
9
- } from "@remit/api-openapi-types";
10
2
  import type { AccountItem, IAccountRepository } from "@remit/data-ports";
11
3
  import { AccountAuthType, ConnectionState } from "@remit/domain-enums";
12
4
  import { logger } from "@remit/logger-lambda";
@@ -20,6 +12,14 @@ import {
20
12
  testImapConnection,
21
13
  testSmtpConnection,
22
14
  } from "@remit/mailbox-service";
15
+ import type {
16
+ AccountResponse,
17
+ CreateAccountInput,
18
+ DeleteAccountResponse,
19
+ TestConnectionInput,
20
+ TestConnectionResponse,
21
+ UpdateAccountInput,
22
+ } from "@remit/api-openapi-types";
23
23
  import {
24
24
  deserializeEncryptedPayload,
25
25
  type SecretsService,
@@ -1,9 +1,9 @@
1
+ import type { AddressItem, FlagsMergePatch } from "@remit/data-ports";
2
+ import { ForbiddenError } from "@remit/data-ports/errors";
1
3
  import type {
2
4
  AddressResponse,
3
5
  UpdateAddressInput,
4
6
  } from "@remit/api-openapi-types";
5
- import type { AddressItem, FlagsMergePatch } from "@remit/data-ports";
6
- import { ForbiddenError } from "@remit/data-ports/errors";
7
7
  import type { APIGatewayProxyEvent } from "aws-lambda";
8
8
  import type { Context } from "openapi-backend";
9
9
  import { getAccountConfigIdFromEvent } from "../auth.js";
@@ -1,11 +1,11 @@
1
1
  import type { SQSClient } from "@aws-sdk/client-sqs";
2
+ import type { AccountConfigItem, MailboxItem } from "@remit/data-ports";
3
+ import { NotFoundError } from "@remit/data-ports/errors";
4
+ import { logger } from "@remit/logger-lambda";
2
5
  import type {
3
6
  AccountConfigResponse,
4
7
  ConfigDescriptionResponse,
5
8
  } from "@remit/api-openapi-types";
6
- import type { AccountConfigItem, MailboxItem } from "@remit/data-ports";
7
- import { NotFoundError } from "@remit/data-ports/errors";
8
- import { logger } from "@remit/logger-lambda";
9
9
  import type { APIGatewayProxyEvent } from "aws-lambda";
10
10
  import { env } from "expect-env";
11
11
  import type { Context } from "openapi-backend";
@@ -1,11 +1,11 @@
1
+ import type { FilterItem } from "@remit/data-ports";
2
+ import { ClientError } from "@remit/data-ports/errors";
3
+ import { FilterScope } from "@remit/domain-enums";
1
4
  import type {
2
5
  CreateFilterInput,
3
6
  FilterResponse,
4
7
  UpdateFilterInput,
5
8
  } from "@remit/api-openapi-types";
6
- import type { FilterItem } from "@remit/data-ports";
7
- import { ClientError } from "@remit/data-ports/errors";
8
- import { FilterScope } from "@remit/domain-enums";
9
9
  import type { AnchorPayload } from "@remit/search-service";
10
10
  import type { APIGatewayProxyEvent } from "aws-lambda";
11
11
  import { getAccountConfigIdFromEvent } from "../auth.js";
@@ -1,7 +1,3 @@
1
- import type {
2
- CanonicalMailboxRole as CanonicalMailboxRoleValue,
3
- FolderAppointment,
4
- } from "@remit/api-openapi-types";
5
1
  import type {
6
2
  AccountSettingItem,
7
3
  IAccountSettingRepository,
@@ -17,6 +13,10 @@ import {
17
13
  CanonicalMailboxRole,
18
14
  MailboxSpecialUse,
19
15
  } from "@remit/domain-enums";
16
+ import type {
17
+ CanonicalMailboxRole as CanonicalMailboxRoleValue,
18
+ FolderAppointment,
19
+ } from "@remit/api-openapi-types";
20
20
 
21
21
  /**
22
22
  * RFC 032 exclusive-folder-appointment (#976): a per-account role→mailbox map.
@@ -1,10 +1,13 @@
1
+ import type {
2
+ IAccountSettingRepository,
3
+ MailboxItem,
4
+ } from "@remit/data-ports";
5
+ import { ForbiddenError, NotFoundError } from "@remit/data-ports/errors";
6
+ import { MessageSystemFlag } from "@remit/domain-enums";
1
7
  import type {
2
8
  MailboxResponse,
3
9
  RenameMailboxInput,
4
10
  } from "@remit/api-openapi-types";
5
- import type { IAccountSettingRepository, MailboxItem } from "@remit/data-ports";
6
- import { ForbiddenError, NotFoundError } from "@remit/data-ports/errors";
7
- import { MessageSystemFlag } from "@remit/domain-enums";
8
11
  import type { APIGatewayProxyEvent } from "aws-lambda";
9
12
  import { getAccountConfigIdFromEvent } from "../auth.js";
10
13
  import {
@@ -1,4 +1,6 @@
1
1
  import { SendMessageCommand } from "@aws-sdk/client-sqs";
2
+ import { BadRequestError, NotFoundError } from "@remit/data-ports/errors";
3
+ import { logger } from "@remit/logger-lambda";
2
4
  import type {
3
5
  AccountExportRequestResponse,
4
6
  CreateExportResponse,
@@ -6,8 +8,6 @@ import type {
6
8
  DeleteMeInput,
7
9
  VipSuggestionsResponse,
8
10
  } from "@remit/api-openapi-types";
9
- import { BadRequestError, NotFoundError } from "@remit/data-ports/errors";
10
- import { logger } from "@remit/logger-lambda";
11
11
  import type { APIGatewayProxyEvent } from "aws-lambda";
12
12
  import { env } from "expect-env";
13
13
  import type { Context } from "openapi-backend";
@@ -1,9 +1,3 @@
1
- import type {
2
- BodyPartResponse,
3
- EnvelopeAddressResponse,
4
- EnvelopeResponse,
5
- MessageSummaryResponse,
6
- } from "@remit/api-openapi-types";
7
1
  import type { MailboxItem } from "@remit/data-ports";
8
2
  import {
9
3
  BadRequestError,
@@ -25,6 +19,12 @@ import {
25
19
  isMessageBodySyncBroken,
26
20
  MailboxCursorPausedError,
27
21
  } from "@remit/mailbox-service";
22
+ import type {
23
+ BodyPartResponse,
24
+ EnvelopeAddressResponse,
25
+ EnvelopeResponse,
26
+ MessageSummaryResponse,
27
+ } from "@remit/api-openapi-types";
28
28
  import {
29
29
  isStorageNotFoundError as isStorageNotFoundErrorFromService,
30
30
  parseStorageUri,
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
- import type { OrganizeInput } from "@remit/api-openapi-types";
4
3
  import { FilterMatchOperator } from "@remit/domain-enums";
4
+ import type { OrganizeInput } from "@remit/api-openapi-types";
5
5
  import { predicateFromInput } from "./organize.js";
6
6
 
7
7
  const input = (over: Partial<OrganizeInput> = {}): OrganizeInput => ({
@@ -1,12 +1,12 @@
1
1
  import { SendMessageCommand } from "@aws-sdk/client-sqs";
2
+ import { NotFoundError } from "@remit/data-ports/errors";
3
+ import { logger } from "@remit/logger-lambda";
2
4
  import type {
3
5
  CreateOrganizeJobResponse,
4
6
  OrganizeInput,
5
7
  OrganizeJobResponse,
6
8
  OrganizePreviewResponse,
7
9
  } from "@remit/api-openapi-types";
8
- import { NotFoundError } from "@remit/data-ports/errors";
9
- import { logger } from "@remit/logger-lambda";
10
10
  import type { APIGatewayProxyEvent } from "aws-lambda";
11
11
  import { env } from "expect-env";
12
12
  import type { Context } from "openapi-backend";
@@ -1,10 +1,10 @@
1
+ import type { OutboxMessageItem } from "@remit/data-ports";
2
+ import { ForbiddenError } from "@remit/data-ports/errors";
1
3
  import type {
2
4
  CreateOutboxMessageInput,
3
5
  OutboxMessageResponse,
4
6
  UpdateOutboxMessageInput,
5
7
  } from "@remit/api-openapi-types";
6
- import type { OutboxMessageItem } from "@remit/data-ports";
7
- import { ForbiddenError } from "@remit/data-ports/errors";
8
8
  import type { APIGatewayProxyEvent } from "aws-lambda";
9
9
  import type { Context } from "openapi-backend";
10
10
  import { getAccountConfigIdFromEvent } from "../auth.js";
@@ -1,10 +1,10 @@
1
+ import type { ResultList, ThreadMessageItem } from "@remit/data-ports";
2
+ import { NotFoundError } from "@remit/data-ports/errors";
1
3
  import type {
2
4
  MessageCategory,
3
5
  SenderTrust,
4
6
  ThreadSearchResponse,
5
7
  } from "@remit/api-openapi-types";
6
- import type { ResultList, ThreadMessageItem } from "@remit/data-ports";
7
- import { NotFoundError } from "@remit/data-ports/errors";
8
8
  import type { APIGatewayProxyEvent } from "aws-lambda";
9
9
  import type { Context } from "openapi-backend";
10
10
  import { getAccountConfigIdFromEvent } from "../auth.js";
@@ -1,5 +1,5 @@
1
- import type { VipSuggestionEntry } from "@remit/api-openapi-types";
2
1
  import type { AddressItem } from "@remit/data-ports";
2
+ import type { VipSuggestionEntry } from "@remit/api-openapi-types";
3
3
 
4
4
  /**
5
5
  * Map an Address row to the wire-format `VipSuggestionEntry` returned by
@@ -33,7 +33,10 @@ import {
33
33
  MessageMoveService,
34
34
  OutboxQueueService,
35
35
  } from "@remit/mailbox-service";
36
- import { createSearchService, type SearchService } from "@remit/search-service";
36
+ import {
37
+ createSearchService,
38
+ type SearchService,
39
+ } from "@remit/search-service";
37
40
  import {
38
41
  buildEmbeddingServiceFromEnv,
39
42
  buildVectorStoreFromEnv,
@@ -1,14 +1,11 @@
1
1
  import assert from "node:assert/strict";
2
- import { existsSync } from "node:fs";
3
2
  import { afterEach, test } from "node:test";
4
- import { fileURLToPath } from "node:url";
5
- import { _resetForTest, getClient, type RemitClient } from "./dynamodb.js";
6
-
7
- // The DynamoDB composition module is stripped from the open-core tree; its
8
- // no-regression guard runs where the module ships and skips where it does not.
9
- const hasDynamoComposition = existsSync(
10
- fileURLToPath(new URL("./compose-dynamodb.ts", import.meta.url)),
11
- );
3
+ import {
4
+ _resetForTest,
5
+ getClient,
6
+ type RemitClient,
7
+ setClient,
8
+ } from "./dynamodb.js";
12
9
 
13
10
  const REQUIRED_KEYS: ReadonlyArray<keyof RemitClient> = [
14
11
  "accountConfig",
@@ -72,29 +69,29 @@ test("getClient() with DATA_BACKEND=postgres constructs all services without thr
72
69
  }
73
70
  });
74
71
 
75
- test(
76
- "getClient() without DATA_BACKEND constructs all services without throwing (DynamoDB path, no regression)",
77
- { skip: !hasDynamoComposition },
78
- async () => {
79
- const savedBackend = process.env.DATA_BACKEND;
80
- delete process.env.DATA_BACKEND;
72
+ test("getClient() without DATA_BACKEND throws until a client is registered", () => {
73
+ const savedBackend = process.env.DATA_BACKEND;
74
+ delete process.env.DATA_BACKEND;
81
75
 
82
- try {
83
- const client = await getClient();
76
+ try {
77
+ assert.throws(() => getClient(), /no DynamoDB client registered/);
78
+ } finally {
79
+ if (savedBackend !== undefined) process.env.DATA_BACKEND = savedBackend;
80
+ }
81
+ });
84
82
 
85
- for (const key of REQUIRED_KEYS) {
86
- assert.ok(
87
- client[key] !== undefined && client[key] !== null,
88
- `RemitClient.${key} must be defined on the DynamoDB path`,
89
- );
90
- }
83
+ test("getClient() without DATA_BACKEND returns the injected client", async () => {
84
+ const savedBackend = process.env.DATA_BACKEND;
85
+ delete process.env.DATA_BACKEND;
91
86
 
92
- assert.equal(typeof client.createConnectionScope, "function");
93
- } finally {
94
- _resetForTest();
95
- if (savedBackend !== undefined) {
96
- process.env.DATA_BACKEND = savedBackend;
97
- }
98
- }
99
- },
100
- );
87
+ const injected = { account: {} } as unknown as RemitClient;
88
+ setClient(injected);
89
+
90
+ try {
91
+ const client = await getClient();
92
+ assert.equal(client, injected);
93
+ } finally {
94
+ _resetForTest();
95
+ if (savedBackend !== undefined) process.env.DATA_BACKEND = savedBackend;
96
+ }
97
+ });
@@ -5,26 +5,28 @@ export type {
5
5
  RemitClient,
6
6
  } from "./create-remit-client.js";
7
7
 
8
- // The DynamoDB composition module is stripped from the open-core export (it
9
- // imports the closed electrodb-service). The `as string` specifier keeps
10
- // tsgo from resolving it there — where the file is absent — while this named
11
- // contract keeps the call site typed rather than `any`. The relational tree
12
- // never takes this branch; the DynamoDB deploy, where the module is present,
13
- // resolves it at runtime.
14
- type ComposeDynamoDBModule = { buildDynamoDBClient: () => RemitClient };
15
-
16
8
  let clientPromise: Promise<RemitClient> | null = null;
9
+ let injected: RemitClient | null = null;
10
+
11
+ /**
12
+ * Register the DynamoDB-backed client from the composition root. The DynamoDB
13
+ * composition lives outside this shared, open-core module and is never imported
14
+ * here. Every DynamoDB entry point calls this before handling a request. The
15
+ * relational backends compose in-package below and never touch this seam.
16
+ */
17
+ export const setClient = (client: RemitClient): void => {
18
+ injected = client;
19
+ clientPromise = null;
20
+ };
17
21
 
18
- // The API process and the imap-worker share this composition root. Each backend
19
- // is loaded through a dynamic import of its own composition module, so the
20
- // module that a given deploy never runs never enters its graph: `@remit/remit-
21
- // drizzle-service`/`drizzle-orm` stay out of a Lambda bundle (both `external`
22
- // for the Lambda esbuild build; `remit-lambda-bundles.test.ts` enforces this),
23
- // and the DynamoDB composition which imports the closed `@remit/remit-
24
- // electrodb-service` stays out of the relational (open-core) tree, where its
25
- // module is not shipped at all. esbuild still bundles the DynamoDB path into
26
- // the Lambda because `./compose-dynamodb.js` is a relative import, not an
27
- // `external`.
22
+ // The API process and the imap-worker share this composition root. The
23
+ // relational backends are loaded through a lazy `import()` of their own
24
+ // in-package composition module, so the module a given deploy never runs never
25
+ // enters its graph: `@remit/drizzle-service`/`drizzle-orm` stay out of a
26
+ // Lambda bundle (both `external` for the Lambda esbuild build;
27
+ // `remit-lambda-bundles.test.ts` enforces this). The DynamoDB backend is
28
+ // injected by the composition root through `setClient`, so this module carries
29
+ // no import of the DynamoDB composition.
28
30
  export const getClient = (): Promise<RemitClient> => {
29
31
  if (!clientPromise) {
30
32
  if (process.env.DATA_BACKEND === "postgres") {
@@ -36,23 +38,20 @@ export const getClient = (): Promise<RemitClient> => {
36
38
  m.buildSqliteClient(),
37
39
  );
38
40
  } else {
39
- clientPromise = (
40
- import(
41
- "./compose-dynamodb.js" as string
42
- ) as Promise<ComposeDynamoDBModule>
43
- ).then((m) => m.buildDynamoDBClient());
41
+ if (!injected) {
42
+ throw new Error(
43
+ "no DynamoDB client registered — register one with setClient() from your composition root",
44
+ );
45
+ }
46
+ clientPromise = Promise.resolve(injected);
44
47
  }
45
48
  }
46
49
 
47
50
  return clientPromise;
48
51
  };
49
52
 
50
- /** Reset the singleton — test use only. */
53
+ /** Reset the singleton and any injected client — test use only. */
51
54
  export const _resetForTest = (): void => {
52
55
  clientPromise = null;
53
- };
54
-
55
- /** Inject a (usually partial) client — test use only. */
56
- export const _setClientForTest = (override: RemitClient): void => {
57
- clientPromise = Promise.resolve(override);
56
+ injected = null;
58
57
  };
@@ -1,4 +1,7 @@
1
- import { type AnchorPayload, buildMessageAnchor } from "@remit/search-service";
1
+ import {
2
+ type AnchorPayload,
3
+ buildMessageAnchor,
4
+ } from "@remit/search-service";
2
5
  import {
3
6
  buildEmbeddingServiceFromEnv,
4
7
  buildVectorStoreFromEnv,
@@ -1,4 +1,7 @@
1
- import type { FilterItem, OrganizeJobRequestItem } from "@remit/data-ports";
1
+ import type {
2
+ FilterItem,
3
+ OrganizeJobRequestItem,
4
+ } from "@remit/data-ports";
2
5
  import {
3
6
  DEFAULT_SEMANTIC_MATCH_THRESHOLD,
4
7
  type FilterMessage,