@dev.smartpricing/message-composer-utils 4.3.1 → 4.3.2-chat.0

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.
@@ -1,6 +1,9 @@
1
1
  import { z } from "zod";
2
+ import { MESSAGE_GROUP_PRODUCTS, } from "../types/index.js";
2
3
  export const dynamicValuesQuerySchema = z.object({
3
- product: z.string({ error: "product query parameter is required" }),
4
+ product: z.enum(MESSAGE_GROUP_PRODUCTS, {
5
+ error: "product query parameter is required and must be a Product",
6
+ }),
4
7
  context: z.string().optional(),
5
8
  language: z.string().default("en"),
6
9
  });
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-values.js","sourceRoot":"","sources":["../../src/schemas/dynamic-values.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,qCAAqC,EAAE,CAAC;IACnE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACnC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CACjD,CAAC,CAAC,MAAM,EAAE,EACV,CAAC,CAAC,MAAM,CAAC;IACP,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,MAAM,CAAC;QACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC/B,CAAC,CACH;CACF,CAAC,CACwC,CAAC"}
1
+ {"version":3,"file":"dynamic-values.js","sourceRoot":"","sources":["../../src/schemas/dynamic-values.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAEL,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,sBAAsB,EAAE;QACtC,KAAK,EAAE,2DAA2D;KACnE,CAAC;IACF,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACnC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CACjD,CAAC,CAAC,MAAM,EAAE,EACV,CAAC,CAAC,MAAM,CAAC;IACP,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,MAAM,CAAC;QACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC/B,CAAC,CACH;CACF,CAAC,CACwC,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export * from "./common.js";
2
+ export * from "./pagination.js";
2
3
  export * from "./ai-integration.js";
3
4
  export * from "./brand.js";
4
5
  export * from "./dynamic-values.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
@@ -1,21 +1,39 @@
1
1
  import { z } from "zod";
2
- import { MESSAGE_GROUP_CATEGORIES, MESSAGE_GROUP_VISIBILITY, MESSAGE_STATUS, MESSAGE_RENDER_TYPES, } from "../types/index.js";
2
+ import { MESSAGE_GROUP_CATEGORIES, MESSAGE_GROUP_LIST_SORT, MESSAGE_GROUP_LIST_VISIBILITY, MESSAGE_GROUP_PRODUCTS, MESSAGE_GROUP_VISIBILITY, MESSAGE_STATUS, MESSAGE_RENDER_TYPES, SORT_ORDER, } from "../types/index.js";
3
3
  import { dbTimestampSchema } from "./common.js";
4
+ import { paginatedResponseSchema, paginationQuerySchema, } from "./pagination.js";
4
5
  import { messageResponseSchema } from "./message.js";
5
- export const listMessageGroupsQuerySchema = z.object({
6
- render_type: z.string().optional(),
7
- tags: z.union([z.string(), z.array(z.string())]).optional(),
8
- tags_mode: z.enum(["and", "or"]).default("and"),
6
+ /**
7
+ * GET /message-groups: server-side filter, sort and pagination (ADR 0012).
8
+ * `visibility` other than `public` is admin-only (403 otherwise, enforced by
9
+ * the route). An empty or whitespace `search` is treated as absent.
10
+ */
11
+ export const listMessageGroupsQuerySchema = paginationQuerySchema.extend({
12
+ search: z
13
+ .string()
14
+ .trim()
15
+ .max(200)
16
+ .optional()
17
+ .transform((value) => (value === "" ? undefined : value)),
18
+ category: z.enum(MESSAGE_GROUP_CATEGORIES).optional(),
19
+ status: z.enum(MESSAGE_STATUS).optional(),
20
+ product: z.enum(MESSAGE_GROUP_PRODUCTS).optional(),
21
+ visibility: z.enum(MESSAGE_GROUP_LIST_VISIBILITY).default("public"),
22
+ render_type: z.enum(MESSAGE_RENDER_TYPES).optional(),
23
+ sort: z.enum(MESSAGE_GROUP_LIST_SORT).default("updated_at"),
24
+ order: z.enum(SORT_ORDER).default("desc"),
9
25
  });
10
26
  export const updateMessageGroupBodySchema = z.object({
11
27
  name: z
12
28
  .string()
13
29
  .refine((name) => name.trim() !== "", "Name is required and cannot be empty"),
30
+ product: z.enum(MESSAGE_GROUP_PRODUCTS).optional(),
14
31
  brand_id: z.string().nullable().optional(),
15
32
  });
16
33
  export const createMessageGroupBodySchema = z.object({
17
34
  name: z.string(),
18
35
  category: z.enum(MESSAGE_GROUP_CATEGORIES),
36
+ product: z.enum(MESSAGE_GROUP_PRODUCTS),
19
37
  visibility: z.enum(MESSAGE_GROUP_VISIBILITY).optional(),
20
38
  brand_id: z.string().nullable().optional(),
21
39
  });
@@ -23,6 +41,7 @@ export const duplicateMessageGroupBodySchema = z
23
41
  .object({
24
42
  name: z.string().optional(),
25
43
  visibility: z.enum(MESSAGE_GROUP_VISIBILITY).optional(),
44
+ product: z.enum(MESSAGE_GROUP_PRODUCTS).optional(),
26
45
  brand_id: z.string().nullable().optional(),
27
46
  })
28
47
  .optional();
@@ -31,6 +50,7 @@ export const messageGroupResponseSchema = z.object({
31
50
  id: z.uuid(),
32
51
  name: z.string(),
33
52
  category: z.enum(MESSAGE_GROUP_CATEGORIES),
53
+ product: z.enum(MESSAGE_GROUP_PRODUCTS),
34
54
  visibility: z.enum(MESSAGE_GROUP_VISIBILITY).nullable().optional(),
35
55
  brand_id: z.string().nullable().optional(),
36
56
  organization_id: z.string().nullable().optional(),
@@ -55,7 +75,8 @@ export const messageGroupWithMessageSummaryResponseSchema = messageGroupWithTags
55
75
  render_type: z.enum(MESSAGE_RENDER_TYPES),
56
76
  })),
57
77
  });
58
- export const messageGroupSummaryListResponseSchema = z.array(messageGroupWithMessageSummaryResponseSchema);
78
+ /** Paginated envelope of list items (ADR 0012). */
79
+ export const messageGroupSummaryListResponseSchema = paginatedResponseSchema(messageGroupWithMessageSummaryResponseSchema);
59
80
  export const messageGroupValidationResponseSchema = z.object({
60
81
  valid: z.boolean(),
61
82
  errors: z.object({
@@ -1 +1 @@
1
- {"version":3,"file":"message-group.js","sourceRoot":"","sources":["../../src/schemas/message-group.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message-group.js","sourceRoot":"","sources":["../../src/schemas/message-group.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { brandDefaultEmailSettingsSchema, brandDefaultButtonSettingsSchema, brandSocialsSchema, } from "../types/index.js";
2
+ import { MESSAGE_GROUP_PRODUCTS, brandDefaultEmailSettingsSchema, brandDefaultButtonSettingsSchema, brandSocialsSchema, } from "../types/index.js";
3
3
  import { messageGroupWithMessagesResponseSchema } from "./message-group.js";
4
4
  export const onboardingResponseSchema = z.object({
5
5
  messageGroups: z.array(messageGroupWithMessagesResponseSchema),
@@ -15,6 +15,8 @@ export const onboardingBodySchema = z.object({
15
15
  userId: z
16
16
  .string({ error: "Both organizationId and userId are required" })
17
17
  .min(1, "Both organizationId and userId are required"),
18
+ // Required, no fallback to the sm-product header (ADR 0011)
19
+ product: z.enum(MESSAGE_GROUP_PRODUCTS),
18
20
  tags: z.array(z.string()).optional(),
19
21
  });
20
22
  export const createDefaultBrandBodySchema = z.object({
@@ -1 +1 @@
1
- {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/schemas/onboarding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,+BAA+B,EAC/B,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sCAAsC,EAAE,MAAM,oBAAoB,CAAC;AAE5E,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,sCAAsC,CAAC;IAC9D,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;KAC1B,CAAC;CACH,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,cAAc,EAAE,CAAC;SACd,MAAM,CAAC,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;SAChE,GAAG,CAAC,CAAC,EAAE,6CAA6C,CAAC;IACxD,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;SAChE,GAAG,CAAC,CAAC,EAAE,6CAA6C,CAAC;IACxD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,cAAc,EAAE,CAAC;SACd,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,IAAI,EAAE,CAAC;SACJ,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,OAAO,EAAE,CAAC;SACP,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACtC,oBAAoB,EAAE,+BAA+B,CAAC,QAAQ,EAAE;IAChE,qBAAqB,EAAE,gCAAgC,CAAC,QAAQ,EAAE;IAClE,cAAc,EAAE,kBAAkB;CACnC,CAAC,CAAC"}
1
+ {"version":3,"file":"onboarding.js","sourceRoot":"","sources":["../../src/schemas/onboarding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,sBAAsB,EACtB,+BAA+B,EAC/B,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sCAAsC,EAAE,MAAM,oBAAoB,CAAC;AAE5E,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,sCAAsC,CAAC;IAC9D,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE;KAC1B,CAAC;CACH,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,cAAc,EAAE,CAAC;SACd,MAAM,CAAC,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;SAChE,GAAG,CAAC,CAAC,EAAE,6CAA6C,CAAC;IACxD,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC;SAChE,GAAG,CAAC,CAAC,EAAE,6CAA6C,CAAC;IACxD,4DAA4D;IAC5D,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,cAAc,EAAE,CAAC;SACd,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,MAAM,EAAE,CAAC;SACN,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,IAAI,EAAE,CAAC;SACJ,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,OAAO,EAAE,CAAC;SACP,MAAM,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC;SAC3E,GAAG,CAAC,CAAC,EAAE,wDAAwD,CAAC;IACnE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACtC,oBAAoB,EAAE,+BAA+B,CAAC,QAAQ,EAAE;IAChE,qBAAqB,EAAE,gCAAgC,CAAC,QAAQ,EAAE;IAClE,cAAc,EAAE,kBAAkB;CACnC,CAAC,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ export const PAGINATION_DEFAULT_PER_PAGE = 25;
3
+ export const PAGINATION_MAX_PER_PAGE = 100;
4
+ /** Laravel-style offset pagination query params (ADR 0012). */
5
+ export const paginationQuerySchema = z.object({
6
+ page: z.coerce.number().int().min(1).default(1),
7
+ per_page: z.coerce
8
+ .number()
9
+ .int()
10
+ .min(1)
11
+ .max(PAGINATION_MAX_PER_PAGE)
12
+ .default(PAGINATION_DEFAULT_PER_PAGE),
13
+ });
14
+ export const paginationLinksSchema = z.object({
15
+ first: z.string(),
16
+ last: z.string(),
17
+ prev: z.string().nullable(),
18
+ next: z.string().nullable(),
19
+ });
20
+ export const paginationMetaSchema = z.object({
21
+ current_page: z.number().int(),
22
+ from: z.number().int().nullable(),
23
+ last_page: z.number().int(),
24
+ path: z.string(),
25
+ per_page: z.number().int(),
26
+ to: z.number().int().nullable(),
27
+ total: z.number().int(),
28
+ });
29
+ /**
30
+ * Laravel API resource-collection envelope around `item`:
31
+ * `{ data, links: { first, last, prev, next }, meta: { … } }`, no `meta.links`.
32
+ */
33
+ export function paginatedResponseSchema(item) {
34
+ return z.object({
35
+ data: z.array(item),
36
+ links: paginationLinksSchema,
37
+ meta: paginationMetaSchema,
38
+ });
39
+ }
40
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/schemas/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAE3C,+DAA+D;AAC/D,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM;SACf,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,uBAAuB,CAAC;SAC5B,OAAO,CAAC,2BAA2B,CAAC;CACxC,CAA+C,CAAC;AAEjD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAA+C,CAAC;AAEjD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACjC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IAC1B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;CACxB,CAA8C,CAAC;AAEhD;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAsB,IAAO;IAClE,OAAO,CAAC,CAAC,MAAM,CAAC;QACd,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QACnB,KAAK,EAAE,qBAAqB;QAC5B,IAAI,EAAE,oBAAoB;KAC3B,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { z } from "zod";
3
+ import { paginationQuerySchema, paginatedResponseSchema, } from "./pagination.js";
4
+ describe("paginationQuerySchema", () => {
5
+ it("defaults to page 1 and 25 per page", () => {
6
+ expect(paginationQuerySchema.parse({})).toEqual({ page: 1, per_page: 25 });
7
+ });
8
+ it("coerces query-string numbers", () => {
9
+ expect(paginationQuerySchema.parse({ page: "3", per_page: "50" })).toEqual({
10
+ page: 3,
11
+ per_page: 50,
12
+ });
13
+ });
14
+ it("accepts the bounds 1 and 100 for per_page", () => {
15
+ expect(paginationQuerySchema.parse({ per_page: "1" }).per_page).toBe(1);
16
+ expect(paginationQuerySchema.parse({ per_page: "100" }).per_page).toBe(100);
17
+ });
18
+ it("rejects page 0, per_page 0 and per_page 101", () => {
19
+ expect(paginationQuerySchema.safeParse({ page: "0" }).success).toBe(false);
20
+ expect(paginationQuerySchema.safeParse({ per_page: "0" }).success).toBe(false);
21
+ expect(paginationQuerySchema.safeParse({ per_page: "101" }).success).toBe(false);
22
+ });
23
+ it("rejects non-integer values", () => {
24
+ expect(paginationQuerySchema.safeParse({ page: "1.5" }).success).toBe(false);
25
+ expect(paginationQuerySchema.safeParse({ page: "abc" }).success).toBe(false);
26
+ });
27
+ });
28
+ describe("paginatedResponseSchema", () => {
29
+ const schema = paginatedResponseSchema(z.object({ id: z.string() }));
30
+ it("accepts the Laravel resource-collection envelope", () => {
31
+ const envelope = {
32
+ data: [{ id: "a" }],
33
+ links: { first: "/x?page=1", last: "/x?page=1", prev: null, next: null },
34
+ meta: {
35
+ current_page: 1,
36
+ from: 1,
37
+ last_page: 1,
38
+ path: "/x",
39
+ per_page: 25,
40
+ to: 1,
41
+ total: 1,
42
+ },
43
+ };
44
+ expect(schema.parse(envelope)).toEqual(envelope);
45
+ });
46
+ it("allows null from/to on an empty page and has no meta.links", () => {
47
+ const parsed = schema.parse({
48
+ data: [],
49
+ links: { first: "/x?page=1", last: "/x?page=1", prev: null, next: null },
50
+ meta: {
51
+ current_page: 1,
52
+ from: null,
53
+ last_page: 1,
54
+ path: "/x",
55
+ per_page: 25,
56
+ to: null,
57
+ total: 0,
58
+ links: [{ url: null, label: "x", active: false }],
59
+ },
60
+ });
61
+ expect(parsed.meta.from).toBeNull();
62
+ expect(parsed.meta.to).toBeNull();
63
+ expect("links" in parsed.meta).toBe(false);
64
+ });
65
+ it("validates items with the given schema", () => {
66
+ const result = schema.safeParse({
67
+ data: [{ id: 1 }],
68
+ links: { first: "/x?page=1", last: "/x?page=1", prev: null, next: null },
69
+ meta: {
70
+ current_page: 1,
71
+ from: 1,
72
+ last_page: 1,
73
+ path: "/x",
74
+ per_page: 25,
75
+ to: 1,
76
+ total: 1,
77
+ },
78
+ });
79
+ expect(result.success).toBe(false);
80
+ });
81
+ });
82
+ //# sourceMappingURL=pagination.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.test.js","sourceRoot":"","sources":["../../src/schemas/pagination.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/types/app.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,qBAAqB,EAAE,uBAAuB;IAC9C,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,mBAAmB,EAAE,qBAAqB;IAC1C,qBAAqB,EAAE,uBAAuB;IAC9C,+BAA+B,EAAE,iCAAiC;IAClE,6BAA6B,EAAE,+BAA+B;IAC9D,gCAAgC,EAAE,kCAAkC;IACpE,kCAAkC,EAAE,oCAAoC;IACxE,qBAAqB,EAAE,uBAAuB;IAC9C,kBAAkB,EAAE,oBAAoB;IACxC,qBAAqB,EAAE,uBAAuB;IAC9C,sBAAsB,EAAE,wBAAwB;IAChD,kBAAkB,EAAE,oBAAoB;IACxC,uBAAuB,EAAE,yBAAyB;IAClD,eAAe,EAAE,iBAAiB;IAClC,0BAA0B,EAAE,4BAA4B;CAChD,CAAC;AAuDX,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC"}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/types/app.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,qBAAqB,EAAE,uBAAuB;IAC9C,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,mBAAmB,EAAE,qBAAqB;IAC1C,qBAAqB,EAAE,uBAAuB;IAC9C,+BAA+B,EAAE,iCAAiC;IAClE,6BAA6B,EAAE,+BAA+B;IAC9D,gCAAgC,EAAE,kCAAkC;IACpE,kCAAkC,EAAE,oCAAoC;IACxE,qBAAqB,EAAE,uBAAuB;IAC9C,kBAAkB,EAAE,oBAAoB;IACxC,qBAAqB,EAAE,uBAAuB;IAC9C,sBAAsB,EAAE,wBAAwB;IAChD,kBAAkB,EAAE,oBAAoB;IACxC,uBAAuB,EAAE,yBAAyB;IAClD,eAAe,EAAE,iBAAiB;IAClC,0BAA0B,EAAE,4BAA4B;CAChD,CAAC;AA0DX,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC"}
@@ -13,4 +13,5 @@ export * from "./submission-job.js";
13
13
  export * from "./kafka-events.js";
14
14
  export * from "./dynamic-values.js";
15
15
  export * from "./brand.js";
16
+ export * from "./pagination.js";
16
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC"}
@@ -9,6 +9,28 @@ export const MESSAGE_STATUS = [
9
9
  ];
10
10
  export const MESSAGE_GROUP_CATEGORIES = ["marketing", "utility"];
11
11
  export const MESSAGE_GROUP_VISIBILITY = ["public", "internal"];
12
+ /** `visibility` filter of the list route; `internal` and `all` are admin-only. */
13
+ export const MESSAGE_GROUP_LIST_VISIBILITY = [
14
+ ...MESSAGE_GROUP_VISIBILITY,
15
+ "all",
16
+ ];
17
+ /** Sortable columns of the list route (ADR 0012); aggregates are not sortable. */
18
+ export const MESSAGE_GROUP_LIST_SORT = [
19
+ "name",
20
+ "category",
21
+ "created_at",
22
+ "updated_at",
23
+ ];
24
+ /**
25
+ * The Smartness application a Message Group is authored for (ADR 0011).
26
+ * Anything outside this list is not a Product and is rejected on write.
27
+ */
28
+ export const MESSAGE_GROUP_PRODUCTS = [
29
+ "connect",
30
+ "chat",
31
+ "pms",
32
+ "pricing",
33
+ ];
12
34
  /**
13
35
  * Brand cascade data schema for preview validation.
14
36
  * Subset of BrandFromDb used at compile time.
@@ -1 +1 @@
1
- {"version":3,"file":"message.js","sourceRoot":"","sources":["../../src/types/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,OAAO,EACL,+BAA+B,EAC/B,gCAAgC,EAChC,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,aAAa,EAAE,UAAU,CAAU,CAAC;AAGzE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,SAAS;IACT,OAAO;IACP,UAAU;IACV,UAAU;CACF,CAAC;AAmDX,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,WAAW,EAAE,SAAS,CAAU,CAAC;AAG1E,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAU,CAAC;AA8DxE;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,sBAAsB,EAAE,+BAA+B;IACvD,uBAAuB,EAAE,gCAAgC;IACzD,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC5D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,IAAI,EAAE,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC7C,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,OAAO,EAAE,CAAC;SACP,MAAM,CAAC;QACN,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;QAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YACb,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;YACnB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAClD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;SAC5C,CAAC;QACF,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;KACvD,CAAC;SACD,WAAW,EAAE;IAChB,KAAK,EAAE,sBAAsB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACpD,CAAC,CAAC"}
1
+ {"version":3,"file":"message.js","sourceRoot":"","sources":["../../src/types/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,OAAO,EACL,+BAA+B,EAC/B,gCAAgC,EAChC,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,aAAa,EAAE,UAAU,CAAU,CAAC;AAGzE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,SAAS;IACT,OAAO;IACP,UAAU;IACV,UAAU;CACF,CAAC;AAmDX,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,WAAW,EAAE,SAAS,CAAU,CAAC;AAG1E,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAU,CAAC;AAGxE,kFAAkF;AAClF,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,GAAG,wBAAwB;IAC3B,KAAK;CACG,CAAC;AAIX,kFAAkF;AAClF,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,MAAM;IACN,UAAU;IACV,YAAY;IACZ,YAAY;CACJ,CAAC;AAkBX;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,SAAS;IACT,MAAM;IACN,KAAK;IACL,SAAS;CACD,CAAC;AA+DX;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,sBAAsB,EAAE,+BAA+B;IACvD,uBAAuB,EAAE,gCAAgC;IACzD,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC5D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,IAAI,EAAE,eAAe,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC7C,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,OAAO,EAAE,CAAC;SACP,MAAM,CAAC;QACN,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;QAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;YACb,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;YACnB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAClD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;SAC5C,CAAC;QACF,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;KACvD,CAAC;SACD,WAAW,EAAE;IAChB,KAAK,EAAE,sBAAsB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACpD,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const SORT_ORDER = ["asc", "desc"];
2
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/types/pagination.ts"],"names":[],"mappings":"AAkCA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,MAAM,CAAU,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.smartpricing/message-composer-utils",
3
- "version": "4.3.1",
3
+ "version": "4.3.2-chat.0",
4
4
  "description": "Shared types and utilities for the message composer",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1,8 +1,13 @@
1
1
  import { z } from "zod";
2
- import { DynamicValuesResponse } from "../types/index.js";
2
+ import {
3
+ DynamicValuesResponse,
4
+ MESSAGE_GROUP_PRODUCTS,
5
+ } from "../types/index.js";
3
6
 
4
7
  export const dynamicValuesQuerySchema = z.object({
5
- product: z.string({ error: "product query parameter is required" }),
8
+ product: z.enum(MESSAGE_GROUP_PRODUCTS, {
9
+ error: "product query parameter is required and must be a Product",
10
+ }),
6
11
  context: z.string().optional(),
7
12
  language: z.string().default("en"),
8
13
  });
@@ -1,4 +1,5 @@
1
1
  export * from "./common.js";
2
+ export * from "./pagination.js";
2
3
  export * from "./ai-integration.js";
3
4
  export * from "./brand.js";
4
5
  export * from "./dynamic-values.js";
@@ -1,21 +1,46 @@
1
1
  import { z } from "zod";
2
2
  import {
3
3
  DuplicateMessageGroupBody,
4
+ ListMessageGroupsQuery,
4
5
  MessageGroupFromDb,
5
6
  MessageGroupWithMessageSummary,
6
7
  MESSAGE_GROUP_CATEGORIES,
8
+ MESSAGE_GROUP_LIST_SORT,
9
+ MESSAGE_GROUP_LIST_VISIBILITY,
10
+ MESSAGE_GROUP_PRODUCTS,
7
11
  MESSAGE_GROUP_VISIBILITY,
8
12
  MESSAGE_STATUS,
9
13
  MESSAGE_RENDER_TYPES,
14
+ SORT_ORDER,
10
15
  } from "../types/index.js";
11
16
  import { dbTimestampSchema } from "./common.js";
17
+ import {
18
+ paginatedResponseSchema,
19
+ paginationQuerySchema,
20
+ } from "./pagination.js";
12
21
  import { messageResponseSchema } from "./message.js";
13
22
 
14
- export const listMessageGroupsQuerySchema = z.object({
15
- render_type: z.string().optional(),
16
- tags: z.union([z.string(), z.array(z.string())]).optional(),
17
- tags_mode: z.enum(["and", "or"]).default("and"),
18
- });
23
+ /**
24
+ * GET /message-groups: server-side filter, sort and pagination (ADR 0012).
25
+ * `visibility` other than `public` is admin-only (403 otherwise, enforced by
26
+ * the route). An empty or whitespace `search` is treated as absent.
27
+ */
28
+ export const listMessageGroupsQuerySchema = paginationQuerySchema.extend({
29
+ search: z
30
+ .string()
31
+ .trim()
32
+ .max(200)
33
+ .optional()
34
+ .transform((value) => (value === "" ? undefined : value)),
35
+ category: z.enum(MESSAGE_GROUP_CATEGORIES).optional(),
36
+ status: z.enum(MESSAGE_STATUS).optional(),
37
+ product: z.enum(MESSAGE_GROUP_PRODUCTS).optional(),
38
+ visibility: z.enum(MESSAGE_GROUP_LIST_VISIBILITY).default("public"),
39
+ render_type: z.enum(MESSAGE_RENDER_TYPES).optional(),
40
+ sort: z.enum(MESSAGE_GROUP_LIST_SORT).default("updated_at"),
41
+ order: z.enum(SORT_ORDER).default("desc"),
42
+ }) satisfies z.ZodType<ListMessageGroupsQuery, unknown>;
43
+ export type { ListMessageGroupsQuery };
19
44
 
20
45
  export const updateMessageGroupBodySchema = z.object({
21
46
  name: z
@@ -24,12 +49,14 @@ export const updateMessageGroupBodySchema = z.object({
24
49
  (name) => name.trim() !== "",
25
50
  "Name is required and cannot be empty",
26
51
  ),
52
+ product: z.enum(MESSAGE_GROUP_PRODUCTS).optional(),
27
53
  brand_id: z.string().nullable().optional(),
28
54
  });
29
55
 
30
56
  export const createMessageGroupBodySchema = z.object({
31
57
  name: z.string(),
32
58
  category: z.enum(MESSAGE_GROUP_CATEGORIES),
59
+ product: z.enum(MESSAGE_GROUP_PRODUCTS),
33
60
  visibility: z.enum(MESSAGE_GROUP_VISIBILITY).optional(),
34
61
  brand_id: z.string().nullable().optional(),
35
62
  });
@@ -38,6 +65,7 @@ export const duplicateMessageGroupBodySchema = z
38
65
  .object({
39
66
  name: z.string().optional(),
40
67
  visibility: z.enum(MESSAGE_GROUP_VISIBILITY).optional(),
68
+ product: z.enum(MESSAGE_GROUP_PRODUCTS).optional(),
41
69
  brand_id: z.string().nullable().optional(),
42
70
  })
43
71
  .optional() satisfies z.ZodType<DuplicateMessageGroupBody | undefined>;
@@ -47,6 +75,7 @@ export const messageGroupResponseSchema = z.object({
47
75
  id: z.uuid(),
48
76
  name: z.string(),
49
77
  category: z.enum(MESSAGE_GROUP_CATEGORIES),
78
+ product: z.enum(MESSAGE_GROUP_PRODUCTS),
50
79
  visibility: z.enum(MESSAGE_GROUP_VISIBILITY).nullable().optional(),
51
80
  brand_id: z.string().nullable().optional(),
52
81
  organization_id: z.string().nullable().optional(),
@@ -80,7 +109,8 @@ export const messageGroupWithMessageSummaryResponseSchema =
80
109
  ),
81
110
  }) satisfies z.ZodType<unknown, MessageGroupWithMessageSummary>;
82
111
 
83
- export const messageGroupSummaryListResponseSchema = z.array(
112
+ /** Paginated envelope of list items (ADR 0012). */
113
+ export const messageGroupSummaryListResponseSchema = paginatedResponseSchema(
84
114
  messageGroupWithMessageSummaryResponseSchema,
85
115
  );
86
116
 
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import {
3
+ MESSAGE_GROUP_PRODUCTS,
3
4
  brandDefaultEmailSettingsSchema,
4
5
  brandDefaultButtonSettingsSchema,
5
6
  brandSocialsSchema,
@@ -21,6 +22,8 @@ export const onboardingBodySchema = z.object({
21
22
  userId: z
22
23
  .string({ error: "Both organizationId and userId are required" })
23
24
  .min(1, "Both organizationId and userId are required"),
25
+ // Required, no fallback to the sm-product header (ADR 0011)
26
+ product: z.enum(MESSAGE_GROUP_PRODUCTS),
24
27
  tags: z.array(z.string()).optional(),
25
28
  });
26
29
 
@@ -0,0 +1,101 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { z } from "zod";
3
+ import {
4
+ paginationQuerySchema,
5
+ paginatedResponseSchema,
6
+ } from "./pagination.js";
7
+
8
+ describe("paginationQuerySchema", () => {
9
+ it("defaults to page 1 and 25 per page", () => {
10
+ expect(paginationQuerySchema.parse({})).toEqual({ page: 1, per_page: 25 });
11
+ });
12
+
13
+ it("coerces query-string numbers", () => {
14
+ expect(paginationQuerySchema.parse({ page: "3", per_page: "50" })).toEqual({
15
+ page: 3,
16
+ per_page: 50,
17
+ });
18
+ });
19
+
20
+ it("accepts the bounds 1 and 100 for per_page", () => {
21
+ expect(paginationQuerySchema.parse({ per_page: "1" }).per_page).toBe(1);
22
+ expect(paginationQuerySchema.parse({ per_page: "100" }).per_page).toBe(100);
23
+ });
24
+
25
+ it("rejects page 0, per_page 0 and per_page 101", () => {
26
+ expect(paginationQuerySchema.safeParse({ page: "0" }).success).toBe(false);
27
+ expect(paginationQuerySchema.safeParse({ per_page: "0" }).success).toBe(
28
+ false,
29
+ );
30
+ expect(paginationQuerySchema.safeParse({ per_page: "101" }).success).toBe(
31
+ false,
32
+ );
33
+ });
34
+
35
+ it("rejects non-integer values", () => {
36
+ expect(paginationQuerySchema.safeParse({ page: "1.5" }).success).toBe(
37
+ false,
38
+ );
39
+ expect(paginationQuerySchema.safeParse({ page: "abc" }).success).toBe(
40
+ false,
41
+ );
42
+ });
43
+ });
44
+
45
+ describe("paginatedResponseSchema", () => {
46
+ const schema = paginatedResponseSchema(z.object({ id: z.string() }));
47
+
48
+ it("accepts the Laravel resource-collection envelope", () => {
49
+ const envelope = {
50
+ data: [{ id: "a" }],
51
+ links: { first: "/x?page=1", last: "/x?page=1", prev: null, next: null },
52
+ meta: {
53
+ current_page: 1,
54
+ from: 1,
55
+ last_page: 1,
56
+ path: "/x",
57
+ per_page: 25,
58
+ to: 1,
59
+ total: 1,
60
+ },
61
+ };
62
+ expect(schema.parse(envelope)).toEqual(envelope);
63
+ });
64
+
65
+ it("allows null from/to on an empty page and has no meta.links", () => {
66
+ const parsed = schema.parse({
67
+ data: [],
68
+ links: { first: "/x?page=1", last: "/x?page=1", prev: null, next: null },
69
+ meta: {
70
+ current_page: 1,
71
+ from: null,
72
+ last_page: 1,
73
+ path: "/x",
74
+ per_page: 25,
75
+ to: null,
76
+ total: 0,
77
+ links: [{ url: null, label: "x", active: false }],
78
+ },
79
+ });
80
+ expect(parsed.meta.from).toBeNull();
81
+ expect(parsed.meta.to).toBeNull();
82
+ expect("links" in parsed.meta).toBe(false);
83
+ });
84
+
85
+ it("validates items with the given schema", () => {
86
+ const result = schema.safeParse({
87
+ data: [{ id: 1 }],
88
+ links: { first: "/x?page=1", last: "/x?page=1", prev: null, next: null },
89
+ meta: {
90
+ current_page: 1,
91
+ from: 1,
92
+ last_page: 1,
93
+ path: "/x",
94
+ per_page: 25,
95
+ to: 1,
96
+ total: 1,
97
+ },
98
+ });
99
+ expect(result.success).toBe(false);
100
+ });
101
+ });
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ import type {
3
+ PaginationLinks,
4
+ PaginationMeta,
5
+ PaginationQuery,
6
+ } from "../types/index.js";
7
+
8
+ export const PAGINATION_DEFAULT_PER_PAGE = 25;
9
+ export const PAGINATION_MAX_PER_PAGE = 100;
10
+
11
+ /** Laravel-style offset pagination query params (ADR 0012). */
12
+ export const paginationQuerySchema = z.object({
13
+ page: z.coerce.number().int().min(1).default(1),
14
+ per_page: z.coerce
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(PAGINATION_MAX_PER_PAGE)
19
+ .default(PAGINATION_DEFAULT_PER_PAGE),
20
+ }) satisfies z.ZodType<PaginationQuery, unknown>;
21
+
22
+ export const paginationLinksSchema = z.object({
23
+ first: z.string(),
24
+ last: z.string(),
25
+ prev: z.string().nullable(),
26
+ next: z.string().nullable(),
27
+ }) satisfies z.ZodType<unknown, PaginationLinks>;
28
+
29
+ export const paginationMetaSchema = z.object({
30
+ current_page: z.number().int(),
31
+ from: z.number().int().nullable(),
32
+ last_page: z.number().int(),
33
+ path: z.string(),
34
+ per_page: z.number().int(),
35
+ to: z.number().int().nullable(),
36
+ total: z.number().int(),
37
+ }) satisfies z.ZodType<unknown, PaginationMeta>;
38
+
39
+ /**
40
+ * Laravel API resource-collection envelope around `item`:
41
+ * `{ data, links: { first, last, prev, next }, meta: { … } }`, no `meta.links`.
42
+ */
43
+ export function paginatedResponseSchema<T extends z.ZodType>(item: T) {
44
+ return z.object({
45
+ data: z.array(item),
46
+ links: paginationLinksSchema,
47
+ meta: paginationMetaSchema,
48
+ });
49
+ }
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  MessageFromDb,
3
3
  MessageGroupFromDb,
4
+ MessageGroupProduct,
4
5
  MessageGroupVisibility,
5
6
  MessageStatus,
6
7
  RenderType,
@@ -14,6 +15,8 @@ export type MessageGroupWithMessages = MessageGroupFromDb & {
14
15
  export interface DuplicateMessageGroupBody {
15
16
  name?: string;
16
17
  visibility?: MessageGroupVisibility;
18
+ /** Defaults to the source group's Product. */
19
+ product?: MessageGroupProduct;
17
20
  brand_id?: string | null;
18
21
  }
19
22
 
package/src/types/app.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { MessageGroupProduct } from "./message.js";
2
+
1
3
  export const errorCodes = {
2
4
  INTERNAL_SERVER_ERROR: "INTERNAL_SERVER_ERROR",
3
5
  UNAUTHORIZED: "UNAUTHORIZED",
@@ -34,7 +36,8 @@ export interface AppError {
34
36
  export interface AppContextConfig {
35
37
  /** @deprecated No longer used — auth is cookie-based via platform layer */
36
38
  authenticationToken?: string;
37
- product?: "connect" | "chat" | "pms" | "pricing" | "browser";
39
+ /** The Product the host app embeds the composer in (ADR 0011). Required. */
40
+ product: MessageGroupProduct;
38
41
  strategy?: "embed" | "native";
39
42
  channels?: Array<"email" | "whatsapp">;
40
43
  languages?: string[];
@@ -45,7 +48,9 @@ export interface AppContextConfig {
45
48
  default_tags?: string[];
46
49
  }
47
50
 
48
- export interface AppContextState extends AppContextConfig {
51
+ export interface AppContextState extends Omit<AppContextConfig, "product"> {
52
+ /** Undefined when the host gave no Product; surfaced as a validation error. */
53
+ product?: MessageGroupProduct;
49
54
  initializationSource:
50
55
  | "query"
51
56
  | "postMessage"
@@ -13,3 +13,4 @@ export * from "./submission-job.js";
13
13
  export * from "./kafka-events.js";
14
14
  export * from "./dynamic-values.js";
15
15
  export * from "./brand.js";
16
+ export * from "./pagination.js";
@@ -1,10 +1,16 @@
1
- import type { MessageStatus, MessageGroupCategory } from "./message.js";
1
+ import type {
2
+ MessageStatus,
3
+ MessageGroupCategory,
4
+ MessageGroupProduct,
5
+ } from "./message.js";
2
6
 
3
7
  export interface KafkaEventBase {
4
8
  event_id: string;
5
9
  event_type: string;
6
10
  timestamp: string;
7
11
  message_group_id: string;
12
+ /** Product of the Message Group the event is about (ADR 0011). */
13
+ product: MessageGroupProduct;
8
14
  organization_id?: string | null;
9
15
  user_id: string;
10
16
  }
@@ -8,6 +8,7 @@ import type {
8
8
  ButtonBlockV1,
9
9
  } from "./whatsappblocks.v1.js";
10
10
  import type { EmailBlock, EmailGlobalSettings } from "./emailblocks.v1.js";
11
+ import type { PaginationQuery, SortOrder } from "./pagination.js";
11
12
  import {
12
13
  brandDefaultEmailSettingsSchema,
13
14
  brandDefaultButtonSettingsSchema,
@@ -79,10 +80,55 @@ export type MessageGroupCategory = (typeof MESSAGE_GROUP_CATEGORIES)[number];
79
80
  export const MESSAGE_GROUP_VISIBILITY = ["public", "internal"] as const;
80
81
  export type MessageGroupVisibility = (typeof MESSAGE_GROUP_VISIBILITY)[number];
81
82
 
83
+ /** `visibility` filter of the list route; `internal` and `all` are admin-only. */
84
+ export const MESSAGE_GROUP_LIST_VISIBILITY = [
85
+ ...MESSAGE_GROUP_VISIBILITY,
86
+ "all",
87
+ ] as const;
88
+ export type MessageGroupListVisibility =
89
+ (typeof MESSAGE_GROUP_LIST_VISIBILITY)[number];
90
+
91
+ /** Sortable columns of the list route (ADR 0012); aggregates are not sortable. */
92
+ export const MESSAGE_GROUP_LIST_SORT = [
93
+ "name",
94
+ "category",
95
+ "created_at",
96
+ "updated_at",
97
+ ] as const;
98
+ export type MessageGroupListSort = (typeof MESSAGE_GROUP_LIST_SORT)[number];
99
+
100
+ /**
101
+ * Parsed query of `GET /message-groups` (ADR 0012): what the route handler
102
+ * receives after defaults. The layer sends any subset of these as query params.
103
+ */
104
+ export interface ListMessageGroupsQuery extends PaginationQuery {
105
+ search?: string;
106
+ category?: MessageGroupCategory;
107
+ status?: MessageStatus;
108
+ product?: MessageGroupProduct;
109
+ visibility: MessageGroupListVisibility;
110
+ render_type?: RenderType;
111
+ sort: MessageGroupListSort;
112
+ order: SortOrder;
113
+ }
114
+
115
+ /**
116
+ * The Smartness application a Message Group is authored for (ADR 0011).
117
+ * Anything outside this list is not a Product and is rejected on write.
118
+ */
119
+ export const MESSAGE_GROUP_PRODUCTS = [
120
+ "connect",
121
+ "chat",
122
+ "pms",
123
+ "pricing",
124
+ ] as const;
125
+ export type MessageGroupProduct = (typeof MESSAGE_GROUP_PRODUCTS)[number];
126
+
82
127
  export interface MessageGroup extends DbEntity {
83
128
  id: string;
84
129
  name: string;
85
130
  category: MessageGroupCategory;
131
+ product: MessageGroupProduct;
86
132
  visibility?: MessageGroupVisibility | null;
87
133
  brand_id?: string | null;
88
134
  organization_id?: string | null;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Laravel API resource-collection envelope for paginated lists (ADR 0012).
3
+ * Links are relative (path + query, no host); `meta.links` is omitted.
4
+ */
5
+ export interface PaginationLinks {
6
+ first: string;
7
+ last: string;
8
+ prev: string | null;
9
+ next: string | null;
10
+ }
11
+
12
+ export interface PaginationMeta {
13
+ current_page: number;
14
+ /** 1-based index of the first item on this page; null on an empty page. */
15
+ from: number | null;
16
+ last_page: number;
17
+ path: string;
18
+ per_page: number;
19
+ /** 1-based index of the last item on this page; null on an empty page. */
20
+ to: number | null;
21
+ total: number;
22
+ }
23
+
24
+ export interface PaginatedResponse<T> {
25
+ data: T[];
26
+ links: PaginationLinks;
27
+ meta: PaginationMeta;
28
+ }
29
+
30
+ export interface PaginationQuery {
31
+ page: number;
32
+ per_page: number;
33
+ }
34
+
35
+ export const SORT_ORDER = ["asc", "desc"] as const;
36
+ export type SortOrder = (typeof SORT_ORDER)[number];