@remit/backend 0.0.49 → 0.0.51

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/.env.test CHANGED
@@ -1,7 +1,4 @@
1
1
  SERVER_PORT=5433
2
- DYNAMODB_PORT=5434
3
- DYNAMODB_TABLE_NAME=remit-local
4
- DYNAMODB_PAGINATION_SALT=test-salt
5
2
  SQS_QUEUE_URL=http://localhost:9324/queue/remit-local
6
3
  NODE_ENV=development
7
4
  LOG_LEVEL=debug
@@ -327,8 +327,7 @@ app.listen(Number(port), "0.0.0.0", () => {
327
327
  {
328
328
  port: Number(port),
329
329
  url: `http://localhost:${port}`,
330
- dynamodbPort: env.DYNAMODB_PORT,
331
- dynamodbTable: env.DYNAMODB_TABLE_NAME,
330
+ dataBackend: process.env.DATA_BACKEND,
332
331
  nodeEnv: env.NODE_ENV,
333
332
  ...(isSelfHostBackend
334
333
  ? {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/backend",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "Remit Mail Inspector API backend",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -1,7 +1,9 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { describe, it } from "node:test";
3
3
  import type { OrganizeInput } from "@remit/api-openapi-types";
4
+ import { BadRequestError } from "@remit/data-ports/errors";
4
5
  import { FilterMatchOperator } from "@remit/domain-enums";
6
+ import { handleError } from "../error.js";
5
7
  import { predicateFromInput } from "./organize.js";
6
8
 
7
9
  const input = (over: Partial<OrganizeInput> = {}): OrganizeInput => ({
@@ -41,3 +43,21 @@ describe("predicateFromInput (move back-apply accepted)", () => {
41
43
  assert.equal(predicate.actionLabelId, "None");
42
44
  });
43
45
  });
46
+
47
+ // previewOrganize rejects a body-content (HasWords) clause by throwing
48
+ // BadRequestError (see service/organize.test.ts for the throw site). Proving
49
+ // the 4xx actually reaches the wire — not just that the right class is
50
+ // thrown — means proving the shared error handler maps it, since a plain
51
+ // Error would otherwise fall through to a 500 (reader #457).
52
+ describe("previewOrganize rejected-rule response (reader #457)", () => {
53
+ it("maps a rejected HasWords clause to a 400 naming the reason, not a 500", async () => {
54
+ const error = new BadRequestError(
55
+ "Organize literal matching cannot evaluate a body-content (HasWords) clause without the vector pipeline — it requires the semantic widen path",
56
+ );
57
+
58
+ const response = await handleError(error);
59
+
60
+ assert.equal(response.statusCode, 400);
61
+ assert.match(JSON.parse(response.body).message, /HasWords/);
62
+ });
63
+ });
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { afterEach, beforeEach, describe, it } from "node:test";
3
3
  import type { FilterAnchorItem, FilterItem } from "@remit/data-ports";
4
- import { NotFoundError } from "@remit/data-ports/errors";
4
+ import { BadRequestError, NotFoundError } from "@remit/data-ports/errors";
5
5
  import { FilterMatchOperator, FilterState } from "@remit/domain-enums";
6
6
  import type {
7
7
  AnchorPayload,
@@ -487,7 +487,7 @@ describe("matchOrganize on a deployment without the vector pipeline", () => {
487
487
  );
488
488
  });
489
489
 
490
- it("fails loud on a body-content (HasWords) clause rather than matching it against a preview", async () => {
490
+ it("rejects a body-content (HasWords) clause as a 400 rather than matching it against a preview", async () => {
491
491
  const deps = vectorlessDeps([
492
492
  candidate("msg-1", { subject: "Dinner reservation" }),
493
493
  ]);
@@ -499,7 +499,12 @@ describe("matchOrganize on a deployment without the vector pipeline", () => {
499
499
  anchorMessageId: "None",
500
500
  literalClauses: [{ field: "HasWords", value: "invoice" }],
501
501
  }),
502
- /HasWords/,
502
+ (error: unknown) => {
503
+ assert.ok(error instanceof BadRequestError);
504
+ assert.equal(error.statusCode, 400);
505
+ assert.match(error.message, /HasWords/);
506
+ return true;
507
+ },
503
508
  "the vector-free literal path must not silently narrow a body match to a preview",
504
509
  );
505
510
  assert.equal(deps.semanticUsed(), false);
@@ -3,7 +3,7 @@ import type {
3
3
  IFilterAnchorRepository,
4
4
  OrganizeJobRequestItem,
5
5
  } from "@remit/data-ports";
6
- import { NotFoundError } from "@remit/data-ports/errors";
6
+ import { BadRequestError, NotFoundError } from "@remit/data-ports/errors";
7
7
  import { FilterClauseField, FilterState } from "@remit/domain-enums";
8
8
  import {
9
9
  buildMatchText,
@@ -280,16 +280,14 @@ const matchSemantic = async (
280
280
  * happened to be indexed. Body-content matching therefore requires the widen
281
281
  * (vector) path — {@link matchSemantic} reconstructs body text from chunk
282
282
  * previews there. This guard keeps the two matchers from diverging silently: a
283
- * body-content clause reaching the vector-free path fails loud instead of
284
- * returning a wrong set. It is unreachable today — no product surface emits a
285
- * `HasWords` clause (the organize UI sends empty `literalClauses`, the filter
286
- * builder emits none) — and stays a fail-fast for any future surface that does.
283
+ * body-content clause reaching the vector-free path is rejected instead of
284
+ * returning a wrong set.
287
285
  */
288
286
  const assertNoBodyContentClause = (
289
287
  clauses: OrganizePredicate["literalClauses"],
290
288
  ): void => {
291
289
  if (clauses.some((clause) => clause.field === FilterClauseField.HasWords)) {
292
- throw new Error(
290
+ throw new BadRequestError(
293
291
  "Organize literal matching cannot evaluate a body-content (HasWords) clause without the vector pipeline — it requires the semantic widen path",
294
292
  );
295
293
  }