@modainteract/moda-interact-shared 0.1.0 → 0.2.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.
- package/README.md +438 -58
- package/dist/chunk-T6DNWLWW.js +303 -0
- package/dist/chunk-T6DNWLWW.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +43 -7
- package/dist/logging/index.d.ts +45 -0
- package/dist/logging/index.js +199 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/shopify/index.d.ts +266 -10
- package/dist/shopify/index.js +43 -7
- package/dist/shopify/node.d.ts +6 -3
- package/dist/shopify/node.js +22 -7
- package/dist/shopify/node.js.map +1 -1
- package/package.json +5 -1
- package/dist/chunk-NE5RMNXU.js +0 -142
- package/dist/chunk-NE5RMNXU.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,67 +1,447 @@
|
|
|
1
1
|
# @modainteract/moda-interact-shared
|
|
2
2
|
|
|
3
|
-
Shared
|
|
4
|
-
Interact
|
|
5
|
-
|
|
3
|
+
Shared TypeScript contracts and reusable platform primitives used by multiple
|
|
4
|
+
Moda Interact services.
|
|
5
|
+
|
|
6
|
+
This package is a **library**, not a deployable service. It does not own
|
|
7
|
+
transport, persistence, background workers, provider SDK integration, or
|
|
8
|
+
application business workflows.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @modainteract/moda-interact-shared
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Production services should use the exact package version selected by the
|
|
17
|
+
corresponding Moda Interact architecture/release task rather than relying on a
|
|
18
|
+
floating version.
|
|
6
19
|
|
|
7
20
|
## What this package owns
|
|
8
21
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
22
|
+
The package contains code that genuinely belongs at a cross-service boundary,
|
|
23
|
+
including:
|
|
24
|
+
|
|
25
|
+
- runtime-validated cross-service event contracts;
|
|
26
|
+
- TypeScript types shared by producers and consumers;
|
|
27
|
+
- schema and event-version constants;
|
|
28
|
+
- canonical queue/job-name constants where multiple repositories must agree;
|
|
29
|
+
- deterministic correlation/job identifier helpers;
|
|
30
|
+
- small pure utilities used by more than one service;
|
|
31
|
+
- reusable structured application logging primitives.
|
|
32
|
+
|
|
33
|
+
The package deliberately keeps framework and runtime dependencies small.
|
|
34
|
+
|
|
35
|
+
## What this package does not own
|
|
36
|
+
|
|
37
|
+
This package does **not** own:
|
|
38
|
+
|
|
39
|
+
- Prisma models or database migrations;
|
|
40
|
+
- PostgreSQL persistence;
|
|
41
|
+
- BullMQ/Redis clients or worker processes;
|
|
42
|
+
- Shopify or Meta webhook HTTP handlers;
|
|
43
|
+
- Shopify or Meta SDK integration;
|
|
44
|
+
- application recovery/business logic;
|
|
45
|
+
- OpenTelemetry spans or application-specific metrics;
|
|
46
|
+
- OTLP transport or observability backends;
|
|
47
|
+
- Render deployment configuration.
|
|
48
|
+
|
|
49
|
+
Those capabilities remain in the repository that owns the corresponding
|
|
50
|
+
runtime concern.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
# Shopify contracts
|
|
55
|
+
|
|
56
|
+
Moda Interact uses the shared Shopify package boundary so webhook producers and
|
|
57
|
+
background consumers do not independently invent compatible-looking event
|
|
58
|
+
types.
|
|
59
|
+
|
|
60
|
+
Import Shopify contracts from:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import {
|
|
64
|
+
// schemas, parsers, constants and types
|
|
65
|
+
} from "@modainteract/moda-interact-shared/shopify";
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Node-only Shopify helpers are isolated under:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import {
|
|
72
|
+
createShopifyWebhookJobId,
|
|
73
|
+
} from "@modainteract/moda-interact-shared/shopify/node";
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The Node-only subpath prevents `node:` built-ins from being accidentally pulled
|
|
77
|
+
into consumers that only need the browser-safe Shopify contract entry point.
|
|
78
|
+
|
|
79
|
+
## Recovery-focused events
|
|
80
|
+
|
|
81
|
+
The current recovery architecture defines distinct event meanings for:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
checkout.created
|
|
85
|
+
checkout.updated
|
|
86
|
+
order.completed
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Pre-recovery checkout events deliberately avoid transporting customer identity,
|
|
90
|
+
line-item, pricing and address data merely because those values existed in the
|
|
91
|
+
provider webhook.
|
|
92
|
+
|
|
93
|
+
Consumers must parse/validate cross-service data with the shared runtime
|
|
94
|
+
contracts before acting on it. TypeScript types alone are not a trust boundary.
|
|
95
|
+
|
|
96
|
+
## Contract versioning
|
|
97
|
+
|
|
98
|
+
Serialized event schema versions and the npm package version are separate
|
|
99
|
+
concepts.
|
|
100
|
+
|
|
101
|
+
A breaking change to an existing serialized event contract requires an
|
|
102
|
+
appropriate event-schema version change and coordinated producer/consumer
|
|
103
|
+
rollout.
|
|
104
|
+
|
|
105
|
+
An additive npm package capability, such as a new independent package export,
|
|
106
|
+
does not by itself require changing an unrelated serialized event schema.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
# Structured logging
|
|
111
|
+
|
|
112
|
+
Moda Interact runtime services use one reusable structured logging primitive:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import {
|
|
116
|
+
createLogger,
|
|
117
|
+
type StructuredLogger,
|
|
118
|
+
type LogFields,
|
|
119
|
+
} from "@modainteract/moda-interact-shared/logging";
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Create a logger
|
|
123
|
+
|
|
124
|
+
Each deployable service/process supplies its own identity and environment:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
const logger = createLogger({
|
|
128
|
+
serviceName: "moda-interact",
|
|
129
|
+
environment: "test",
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The default namespace is:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
moda-interact
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Logs contain the canonical identity:
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
service.namespace=moda-interact
|
|
143
|
+
service.name=<service/process name>
|
|
144
|
+
deployment.environment.name=<environment>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
For deployed Moda Interact environments, the expected environment values are:
|
|
148
|
+
|
|
149
|
+
```text
|
|
150
|
+
test
|
|
151
|
+
production
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The shared library does not read a service's environment variables for it.
|
|
155
|
+
The owning service resolves its configuration and passes the resulting
|
|
156
|
+
environment to `createLogger`.
|
|
157
|
+
|
|
158
|
+
## Write logs
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
logger.info("shopify.webhook.outcome", {
|
|
162
|
+
topic: "checkouts/create",
|
|
163
|
+
outcome: "ENQUEUED",
|
|
164
|
+
ackMs: 18,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
logger.warn("queue.job.retry", {
|
|
168
|
+
queue: "recovery",
|
|
169
|
+
attempt: 2,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
logger.error("recovery.failed", {
|
|
173
|
+
recoveryId,
|
|
174
|
+
error,
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The default sink emits one structured JSON record per console call.
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"timestamp": "2026-08-30T12:00:00.000Z",
|
|
185
|
+
"level": "info",
|
|
186
|
+
"event": "shopify.webhook.outcome",
|
|
187
|
+
"service.namespace": "moda-interact",
|
|
188
|
+
"service.name": "moda-interact",
|
|
189
|
+
"deployment.environment.name": "test",
|
|
190
|
+
"data": {
|
|
191
|
+
"topic": "checkouts/create",
|
|
192
|
+
"outcome": "ENQUEUED",
|
|
193
|
+
"ackMs": 18
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Log levels
|
|
199
|
+
|
|
200
|
+
Available levels are:
|
|
201
|
+
|
|
202
|
+
```text
|
|
203
|
+
debug
|
|
204
|
+
info
|
|
205
|
+
warn
|
|
206
|
+
error
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Use stable, machine-readable event names such as:
|
|
210
|
+
|
|
211
|
+
```text
|
|
212
|
+
shopify.webhook.outcome
|
|
213
|
+
meta.webhook.outcome
|
|
214
|
+
queue.job.started
|
|
215
|
+
queue.job.completed
|
|
216
|
+
queue.job.failed
|
|
217
|
+
recovery.materialized
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Child loggers
|
|
221
|
+
|
|
222
|
+
`child()` is optional.
|
|
223
|
+
|
|
224
|
+
It creates another logger with some repeated fields already attached. It does
|
|
225
|
+
**not** create a worker, queue, process, connection, or logging service.
|
|
226
|
+
|
|
227
|
+
Instead of repeatedly writing:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
logger.info("queue.job.started", {
|
|
231
|
+
queue: "recovery",
|
|
232
|
+
jobId,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
logger.info("queue.job.completed", {
|
|
236
|
+
queue: "recovery",
|
|
237
|
+
jobId,
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
you may write:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const recoveryLogger = logger.child({
|
|
245
|
+
queue: "recovery",
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
recoveryLogger.info("queue.job.started", {
|
|
249
|
+
jobId,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
recoveryLogger.info("queue.job.completed", {
|
|
253
|
+
jobId,
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Both records automatically contain:
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"queue": "recovery"
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Use child context only for stable, safe operational fields.
|
|
266
|
+
|
|
267
|
+
## Sensitive data
|
|
268
|
+
|
|
269
|
+
The logger provides defense-in-depth redaction and bounded serialization, but
|
|
270
|
+
callers remain responsible for constructing safe, explicit log fields.
|
|
271
|
+
|
|
272
|
+
Do not intentionally log:
|
|
273
|
+
|
|
274
|
+
- access or refresh tokens;
|
|
275
|
+
- authorization headers;
|
|
276
|
+
- cookies;
|
|
277
|
+
- OAuth codes;
|
|
278
|
+
- passwords or API keys;
|
|
279
|
+
- private keys;
|
|
280
|
+
- webhook verification secrets;
|
|
281
|
+
- complete webhook/request/response payloads;
|
|
282
|
+
- customer names, email addresses, phone numbers or postal addresses;
|
|
283
|
+
- payment/card data.
|
|
284
|
+
|
|
285
|
+
Do not pass a complete provider/customer object to the logger and rely on
|
|
286
|
+
redaction to make it safe.
|
|
287
|
+
|
|
288
|
+
## Errors
|
|
289
|
+
|
|
290
|
+
Errors can be logged as values:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
logger.error("queue.job.failed", {
|
|
294
|
+
jobId,
|
|
295
|
+
error,
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
The shared logger safely serializes `Error` objects without emitting stack
|
|
300
|
+
traces by default.
|
|
301
|
+
|
|
302
|
+
Logging is best-effort: a logger sink or serialization failure must not alter
|
|
303
|
+
the success/failure semantics of the business operation being logged.
|
|
304
|
+
|
|
305
|
+
## Logging and OpenTelemetry are separate
|
|
306
|
+
|
|
307
|
+
The shared logger does **not** automatically create:
|
|
308
|
+
|
|
309
|
+
- spans;
|
|
310
|
+
- counters;
|
|
311
|
+
- histograms;
|
|
312
|
+
- OTLP exports.
|
|
313
|
+
|
|
314
|
+
Service-specific OpenTelemetry remains in the owning runtime repository.
|
|
315
|
+
|
|
316
|
+
For example:
|
|
317
|
+
|
|
318
|
+
```text
|
|
319
|
+
Shopify operation
|
|
320
|
+
|
|
|
321
|
+
+--> shared structured logger
|
|
322
|
+
|
|
|
323
|
+
+--> Shopify-specific OpenTelemetry
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
This separation is intentional. Fields that are useful in diagnostic logs can
|
|
327
|
+
have very different cardinality constraints from metric attributes.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
# Package exports
|
|
332
|
+
|
|
333
|
+
The public package exposes independent entry points.
|
|
334
|
+
|
|
335
|
+
```text
|
|
336
|
+
@modainteract/moda-interact-shared
|
|
337
|
+
existing root exports
|
|
338
|
+
|
|
339
|
+
@modainteract/moda-interact-shared/shopify
|
|
340
|
+
Shopify cross-service contracts, schemas, constants and pure helpers
|
|
341
|
+
|
|
342
|
+
@modainteract/moda-interact-shared/shopify/node
|
|
343
|
+
Node-only Shopify helpers
|
|
344
|
+
|
|
345
|
+
@modainteract/moda-interact-shared/logging
|
|
346
|
+
reusable structured logging API
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Consumers should prefer the narrowest appropriate subpath.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
# Ownership boundary
|
|
354
|
+
|
|
355
|
+
Examples of repository ownership:
|
|
356
|
+
|
|
357
|
+
**moda-interact**
|
|
358
|
+
|
|
359
|
+
- authenticates Shopify ingress;
|
|
360
|
+
- normalizes/validates Shopify events;
|
|
361
|
+
- publishes the appropriate queue jobs;
|
|
362
|
+
- uses the shared structured logger for generic log mechanics;
|
|
363
|
+
- owns Shopify-specific observability semantics and OpenTelemetry.
|
|
364
|
+
|
|
365
|
+
**moda-interact-background**
|
|
366
|
+
|
|
367
|
+
- parses shared cross-service contracts before acting;
|
|
368
|
+
- runs BullMQ workers and recovery workflows;
|
|
369
|
+
- uses the shared logger for generic structured logging;
|
|
370
|
+
- owns worker/recovery OpenTelemetry.
|
|
371
|
+
|
|
372
|
+
**moda-interact-messaging**
|
|
373
|
+
|
|
374
|
+
- owns Meta/WhatsApp ingress;
|
|
375
|
+
- uses the shared logger for generic structured logging;
|
|
376
|
+
- owns Meta/WhatsApp-specific observability and OpenTelemetry.
|
|
377
|
+
|
|
378
|
+
**moda-interact-admin**
|
|
379
|
+
|
|
380
|
+
- owns the internal admin application;
|
|
381
|
+
- may use the same shared logger where server-side operational logging is
|
|
382
|
+
required;
|
|
383
|
+
- owns admin-specific application telemetry.
|
|
384
|
+
|
|
385
|
+
**moda-interact-database**
|
|
386
|
+
|
|
387
|
+
- owns Prisma schema/migrations and database artifacts;
|
|
388
|
+
- does not duplicate shared TypeScript transport contracts.
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
# Development
|
|
393
|
+
|
|
394
|
+
## Typecheck
|
|
395
|
+
|
|
396
|
+
```bash
|
|
64
397
|
npm run typecheck
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Build
|
|
401
|
+
|
|
402
|
+
```bash
|
|
65
403
|
npm run build
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Test
|
|
407
|
+
|
|
408
|
+
```bash
|
|
66
409
|
npm test
|
|
67
410
|
```
|
|
411
|
+
|
|
412
|
+
## Inspect the publish artifact
|
|
413
|
+
|
|
414
|
+
Before publishing:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
npm pack --dry-run
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Verify that the intended JavaScript and type declarations for every public
|
|
421
|
+
subpath are included.
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
# Release policy
|
|
426
|
+
|
|
427
|
+
The npm package version follows semantic versioning.
|
|
428
|
+
|
|
429
|
+
Examples:
|
|
430
|
+
|
|
431
|
+
```text
|
|
432
|
+
patch
|
|
433
|
+
backwards-compatible fix to an existing package capability
|
|
434
|
+
|
|
435
|
+
minor
|
|
436
|
+
backwards-compatible new public capability/export
|
|
437
|
+
|
|
438
|
+
major
|
|
439
|
+
breaking public API/package compatibility change
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
The addition of the independent `./logging` public API is an additive package
|
|
443
|
+
capability and should therefore be released as a **minor** version increment.
|
|
444
|
+
|
|
445
|
+
Publishing the package and changing consuming services are coordinated tasks;
|
|
446
|
+
a consuming service must not assume an unpublished local shared export exists
|
|
447
|
+
in the npm artifact it installs.
|