@molecule/api-resource 1.0.0 → 1.0.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.
Files changed (2) hide show
  1. package/README.md +628 -0
  2. package/package.json +12 -11
package/README.md ADDED
@@ -0,0 +1,628 @@
1
+ <!--
2
+ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
+ Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
+ Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
+ To change this document, edit the module-level JSDoc in src/index.ts.
6
+ Generated: 2026-08-04T01:49:07.015Z
7
+ -->
8
+
9
+ # @molecule/api-resource
10
+
11
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
12
+ > It is written to be read by coding agents as much as by people, and is generated from this
13
+ > package's source — edit `src/index.ts` JSDoc, not this file.
14
+
15
+ Base resource patterns for molecule.dev.
16
+
17
+ Provides CRUD operation factories and request handler utilities for
18
+ building RESTful API resources. Every `@molecule/api-resource-*` package
19
+ (user, project, device, ...) is built on these factories.
20
+
21
+ ## Quick Start
22
+
23
+ ```typescript
24
+ import { z } from 'zod'
25
+
26
+ import {
27
+ createRequestHandler,
28
+ create,
29
+ read,
30
+ update,
31
+ del,
32
+ query,
33
+ type MoleculeRequest,
34
+ } from '@molecule/api-resource'
35
+ import { types } from '@molecule/api-resource'
36
+ import { basePropsSchema } from '@molecule/api-resource/schema'
37
+
38
+ // Every resource schema extends basePropsSchema (id/createdAt/updatedAt).
39
+ const userSchema = basePropsSchema.extend({ name: z.string(), email: z.string() })
40
+ type UserProps = z.infer<typeof userSchema>
41
+
42
+ // Each CRUD factory takes the resource descriptor and returns an async
43
+ // operation, e.g. ({ props }) or ({ id }) => { statusCode, body }
44
+ // One descriptor, typed as Resource, drives every CRUD factory:
45
+ const userResource: types.Resource = { name: 'User', tableName: 'users', schema: userSchema }
46
+
47
+ const createUser = create<UserProps>(userResource)
48
+ const updateUser = update<UserProps>(userResource)
49
+ // read/del type their descriptor with the concrete props — cast the shared one:
50
+ const readUser = read(userResource as types.Resource<UserProps>)
51
+ const deleteUser = del(userResource as types.Resource<UserProps>)
52
+
53
+ // Operations do NOT take (req, res) directly — adapt each one by mapping
54
+ // request fields (req.body, req.params.id) to its arguments, then wrap
55
+ // with createRequestHandler for Express
56
+ const requestHandlerMap = {
57
+ create: createRequestHandler(
58
+ async (req: MoleculeRequest) => await createUser({ props: req.body }),
59
+ ),
60
+ read: createRequestHandler(async (req: MoleculeRequest) => await readUser({ id: req.params.id })),
61
+ // ...
62
+ }
63
+ ```
64
+
65
+ ## Type
66
+
67
+ `infrastructure`
68
+
69
+ ## Installation
70
+
71
+ ```bash
72
+ npm install @molecule/api-resource @molecule/api-bond @molecule/api-database @molecule/api-i18n @molecule/api-locales-resource @molecule/api-utilities-validation uuid zod
73
+ ```
74
+
75
+ ## API
76
+
77
+ ### Interfaces
78
+
79
+ #### `ErrorFallback`
80
+
81
+ Fallback HTTP response used when a caught error is NOT a molecule-tagged error
82
+ (i.e. a genuinely-unexpected failure).
83
+
84
+ ```typescript
85
+ interface ErrorFallback {
86
+ /** HTTP status for the fallback (typically 500). */
87
+ status: number
88
+ /** Human-readable fallback message (already localized by the caller). */
89
+ message: string
90
+ /** Machine-readable key the frontend maps to a localized message. */
91
+ errorKey: string
92
+ }
93
+ ```
94
+
95
+ #### `MoleculeRequest`
96
+
97
+ Framework-agnostic HTTP request interface. Structurally compatible with Express's
98
+ `Request` — Express objects satisfy this interface without adapters, but the type
99
+ doesn't depend on Express, allowing use with Fastify, Hono, or Lambda.
100
+
101
+ ```typescript
102
+ interface MoleculeRequest {
103
+ body: any
104
+ params: Record<string, string>
105
+ query: Record<string, any>
106
+ headers: Record<string, string | string[] | undefined>
107
+ cookies?: Record<string, string>
108
+ /**
109
+ * Client IP address. Populated by Express (honoring `trust proxy`) and other
110
+ * frameworks; optional because it isn't guaranteed in every adapter. Used for
111
+ * IP-keyed rate limiting on auth endpoints.
112
+ */
113
+ ip?: string
114
+ on?(event: string, listener: (...args: unknown[]) => void): void
115
+ }
116
+ ```
117
+
118
+ #### `MoleculeResponse`
119
+
120
+ Framework-agnostic HTTP response interface. Structurally compatible with Express's
121
+ `Response` — includes `status()`, `json()`, `send()`, `set()`, cookie methods,
122
+ streaming (`write`), and `locals` for per-request data sharing.
123
+
124
+ ```typescript
125
+ interface MoleculeResponse {
126
+ status(code: number): this
127
+ json(body: unknown): void
128
+ send(body: unknown): void
129
+ end(): void
130
+ set(headers: Record<string, string>): void
131
+ setHeader(name: string, value: string): void
132
+ cookie(name: string, value: string, options?: Record<string, unknown>): void
133
+ write(chunk: string | Buffer): boolean
134
+ flushHeaders?(): void
135
+ locals: Record<string, any>
136
+ }
137
+ ```
138
+
139
+ #### `Query`
140
+
141
+ The available resource query options.
142
+
143
+ ```typescript
144
+ interface Query {
145
+ /**
146
+ * The maximum number of query results.
147
+ */
148
+ limit?: number
149
+ /**
150
+ * An indexed property to order the results by.
151
+ */
152
+ orderBy?: `createdAt` | `updatedAt`
153
+ /**
154
+ * The direction to order the results.
155
+ */
156
+ orderDirection?: `asc` | `desc`
157
+ /**
158
+ * Constrains the results to values less than these properties, when provided.
159
+ */
160
+ before?: {
161
+ /**
162
+ * Constrains the results to resources created before the provided timestamp.
163
+ *
164
+ * Usually an ISO 8601 timestamp.
165
+ */
166
+ createdAt?: string
167
+ /**
168
+ * Constrains the results to resources updated before the provided timestamp.
169
+ *
170
+ * Usually an ISO 8601 timestamp.
171
+ */
172
+ updatedAt?: string
173
+ }
174
+ /**
175
+ * Constrains the results to values greater than these properties, when provided.
176
+ */
177
+ after?: {
178
+ /**
179
+ * Constrains the results to resources created after the provided timestamp.
180
+ *
181
+ * Usually an ISO 8601 timestamp.
182
+ */
183
+ createdAt?: string
184
+ /**
185
+ * Constrains the results to resources updated after the provided timestamp.
186
+ *
187
+ * Usually an ISO 8601 timestamp.
188
+ */
189
+ updatedAt?: string
190
+ }
191
+ }
192
+ ```
193
+
194
+ #### `Response`
195
+
196
+ Structured response object returned by resource handlers. Contains the HTTP
197
+ status code, optional headers, and the response body payload.
198
+
199
+ ```typescript
200
+ interface Response {
201
+ /**
202
+ * HTTP status code to return.
203
+ */
204
+ statusCode: number
205
+ /**
206
+ * Optional map of HTTP header keys to values.
207
+ */
208
+ headers?: Record<string, string>
209
+ /**
210
+ * The response payload to return.
211
+ */
212
+ body: any
213
+ }
214
+ ```
215
+
216
+ ### Types
217
+
218
+ #### `Handler`
219
+
220
+ A resource handler function that processes a request and returns a structured
221
+ response object. If it returns `null` or `undefined`, the request is passed
222
+ to the next middleware via `next()`.
223
+
224
+ ```typescript
225
+ type Handler = (
226
+ req: MoleculeRequest,
227
+ res: MoleculeResponse,
228
+ ) => Promise<Response | null | undefined> | Response | null | undefined
229
+ ```
230
+
231
+ #### `MoleculeNextFunction`
232
+
233
+ Framework-agnostic next function. Call with no arguments to pass to the next
234
+ middleware, or with an error to trigger error-handling middleware.
235
+
236
+ ```typescript
237
+ type MoleculeNextFunction = (err?: unknown) => void
238
+ ```
239
+
240
+ #### `MoleculeRequestHandler`
241
+
242
+ Framework-agnostic middleware function signature matching Express's
243
+ `(req, res, next) => void` pattern.
244
+
245
+ ```typescript
246
+ type MoleculeRequestHandler = (
247
+ req: MoleculeRequest,
248
+ res: MoleculeResponse,
249
+ next: MoleculeNextFunction,
250
+ ) => void | Promise<void>
251
+ ```
252
+
253
+ ### Functions
254
+
255
+ #### `create(resource)`
256
+
257
+ Creates a handler to create a resource.
258
+
259
+ Uses the bonded DataStore from `@molecule/api-database` for database-agnostic operations.
260
+
261
+ Example usage:
262
+
263
+ ```ts
264
+ import { create as resourceCreate } from '@molecule/api-resource'
265
+ import type * as types from '../../types.js'
266
+
267
+ export const create = ({ name, tableName, schema }: types.Resource) => {
268
+ const create = resourceCreate({ name, tableName, schema })
269
+
270
+ return async (req: MoleculeRequest) => {
271
+ const props = req.body
272
+ return await create({ props })
273
+ }
274
+ }
275
+ ```
276
+
277
+ ```typescript
278
+ function create({
279
+ name,
280
+ tableName,
281
+ schema,
282
+ }: types.Resource<unknown>): ({
283
+ props: createProps,
284
+ id,
285
+ }: {
286
+ props: CreateProps
287
+ id?: CreatedProps['id']
288
+ }) => Promise<
289
+ | { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
290
+ | { statusCode: number; body: { props: CreatedProps; error?: undefined; errorKey?: undefined } }
291
+ >
292
+ ```
293
+
294
+ - `resource` — The resource descriptor.
295
+ - `resource.name` — Human-readable resource name used in error messages (e.g. `'User'`).
296
+ - `resource.tableName` — Database table to insert the new row into.
297
+ - `resource.schema` — Zod schema to validate the incoming props against.
298
+
299
+ **Returns:** A curried async function that accepts `{ props, id? }` and returns a `{ statusCode, body }` response.
300
+
301
+ #### `createRequestHandler(handler)`
302
+
303
+ Wraps a resource handler into Express-compatible middleware. Calls the handler,
304
+ then applies `statusCode`, `headers`, and `body` to the response. If the handler
305
+ returns null/undefined, calls `next()`. Catches errors and forwards them as
306
+ i18n-translated error messages.
307
+
308
+ ```typescript
309
+ function createRequestHandler(
310
+ handler: Handler,
311
+ ): (req: MoleculeRequest, res: MoleculeResponse, next: MoleculeNextFunction) => Promise<void>
312
+ ```
313
+
314
+ - `handler` — The resource handler function to wrap.
315
+
316
+ **Returns:** An Express-compatible middleware function `(req, res, next) => void`.
317
+
318
+ #### `del(resource)`
319
+
320
+ Creates a handler to delete a resource by its `id`.
321
+
322
+ > **Note:** We use `del` everywhere instead of `delete` since `delete` is a reserved keyword in JavaScript.
323
+
324
+ Uses the bonded DataStore from `@molecule/api-database` for database-agnostic operations.
325
+
326
+ Example usage:
327
+
328
+ ```ts
329
+ import { del as resourceDel } from '@molecule/api-resource'
330
+ import type * as types from '../../types.js'
331
+
332
+ export const del = ({ name, tableName, schema }: types.Resource) => {
333
+ const del = resourceDel({ name, tableName, schema })
334
+
335
+ return async (req: MoleculeRequest) => {
336
+ const id = req.params.id
337
+ return await del({ id })
338
+ }
339
+ }
340
+ ```
341
+
342
+ ```typescript
343
+ function del({ name, tableName }: DeletedResource): ({ id }: { id: Props['id'] }) => Promise<
344
+ | { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
345
+ | {
346
+ statusCode: number
347
+ body: { props: { id: Props['id'] }; error?: undefined; errorKey?: undefined }
348
+ }
349
+ >
350
+ ```
351
+
352
+ - `resource` — The resource descriptor.
353
+ - `resource.name` — Human-readable resource name used in error messages (e.g. `'User'`).
354
+ - `resource.tableName` — Database table to delete the row from.
355
+
356
+ **Returns:** A curried async function that accepts `{ id }` and returns a `{ statusCode, body }` response.
357
+
358
+ #### `query(resource)`
359
+
360
+ Creates a request handler to be used to query resources.
361
+
362
+ Uses the bonded DataStore from `@molecule/api-database` for database-agnostic operations.
363
+
364
+ ```typescript
365
+ function query({
366
+ tableName,
367
+ }: QueriedResource): (
368
+ req: MoleculeRequest,
369
+ ) => Promise<
370
+ | { statusCode: number; body: { error: string; errorKey: string } }
371
+ | { statusCode: number; body: Props[] }
372
+ >
373
+ ```
374
+
375
+ - `resource` — The resource descriptor.
376
+ - `resource.tableName` — Database table to query resources from.
377
+
378
+ **Returns:** A curried async handler that accepts a `MoleculeRequest` (with query params for pagination/sorting) and returns a `{ statusCode, body }` response.
379
+
380
+ #### `read(resource)`
381
+
382
+ Creates a handler to read a resource by its `id`.
383
+
384
+ Uses the bonded DataStore from `@molecule/api-database` for database-agnostic operations.
385
+
386
+ Example usage:
387
+
388
+ ```ts
389
+ import { read as resourceRead } from '@molecule/api-resource'
390
+ import type * as types from '../../types.js'
391
+
392
+ export const read = ({ name, tableName, schema }: types.Resource) => {
393
+ const read = resourceRead({ name, tableName, schema })
394
+
395
+ return async (req: MoleculeRequest) => {
396
+ const id = req.params.id
397
+ return await read({ id })
398
+ }
399
+ }
400
+ ```
401
+
402
+ ```typescript
403
+ function read({
404
+ tableName,
405
+ }: ReadResource): ({
406
+ id,
407
+ props,
408
+ }: {
409
+ id: ReadProps['id']
410
+ props?: ReadProps
411
+ }) => Promise<
412
+ | { statusCode: number; body: { props: ReadProps; error?: undefined; errorKey?: undefined } }
413
+ | { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
414
+ >
415
+ ```
416
+
417
+ - `resource` — The resource descriptor.
418
+ - `resource.tableName` — Database table to query the resource from.
419
+
420
+ **Returns:** A curried async function that accepts `{ id, props? }` and returns a `{ statusCode, body }` response.
421
+
422
+ #### `respondError(res, error, fallback)`
423
+
424
+ Respond to a caught handler error, honoring molecule's "tagged error" convention.
425
+
426
+ A tagged error carries BOTH a numeric `statusCode` AND a string `errorKey` — the same
427
+ contract `@molecule/api-server-default-express`'s error middleware (`classifyTaggedError`)
428
+ uses. Providers throw these for expected, user-actionable conditions — e.g. a
429
+ config-missing throw (`503` + `'config.notConfigured'`) when `STRIPE_SECRET_KEY` is unset.
430
+
431
+ Handlers that catch errors MUST funnel through this instead of a blanket
432
+ `res.status(500)`, so a tagged error surfaces its REAL status + errorKey rather than
433
+ being flattened to a generic 500. Otherwise the app looks broken at the exact moment the
434
+ user tries to use the feature — the worst possible time for a payment (the conversion
435
+ path). A swallowed tag is why the middleware-only fix was insufficient: ~185 handlers
436
+ catch-and-500, so the tag never reached the middleware.
437
+
438
+ Tagged errors are logged at `warn` (expected/actionable, not a server fault); everything
439
+ else is logged at `error` and returns the caller's `fallback`. Requiring BOTH fields is
440
+ deliberate: it keeps arbitrary library errors that merely carry a `.statusCode` (e.g. an
441
+ AWS SDK 403) from being surfaced with a status molecule never chose.
442
+
443
+ ```typescript
444
+ function respondError(res: MoleculeResponse, error: unknown, fallback: ErrorFallback): void
445
+ ```
446
+
447
+ - `res` — The response object.
448
+ - `error` — The caught error.
449
+ - `fallback` — Status/message/errorKey to use when `error` isn't a tagged error.
450
+
451
+ #### `update(resource)`
452
+
453
+ Creates a handler to update a resource by its `id` and some `props`.
454
+
455
+ Uses the bonded DataStore from `@molecule/api-database` for database-agnostic operations.
456
+
457
+ Example usage:
458
+
459
+ ```ts
460
+ import { update as resourceUpdate } from '@molecule/api-resource'
461
+ import type * as types from '../../types.js'
462
+
463
+ export const update = ({ name, tableName, schema }: types.Resource) => {
464
+ const update = resourceUpdate({ name, tableName, schema })
465
+
466
+ return async (req: MoleculeRequest) => {
467
+ const id = req.params.id
468
+ const props = req.body
469
+ return await update({ id, props })
470
+ }
471
+ }
472
+ ```
473
+
474
+ ```typescript
475
+ function update({
476
+ name,
477
+ tableName,
478
+ schema,
479
+ }: types.Resource<unknown>): ({
480
+ id,
481
+ props: updateProps,
482
+ }: {
483
+ id: UpdatedProps['id'] | types.Props['id']
484
+ props: UpdateProps
485
+ }) => Promise<
486
+ | { statusCode: number; body: { error: string; errorKey: string; props?: undefined } }
487
+ | { statusCode: number; body: { props: UpdatedProps; error?: undefined; errorKey?: undefined } }
488
+ >
489
+ ```
490
+
491
+ - `resource` — The resource descriptor.
492
+ - `resource.name` — Human-readable resource name used in error messages (e.g. `'User'`).
493
+ - `resource.tableName` — Database table to update the row in.
494
+ - `resource.schema` — Zod schema to validate the incoming props against.
495
+
496
+ **Returns:** A curried async function that accepts `{ id, props }` and returns a `{ statusCode, body }` response.
497
+
498
+ ### Constants
499
+
500
+ #### `i18nRegistered`
501
+
502
+ Sentinel value confirming that locale translations for the resource package
503
+ have been registered (or attempted). Always `true` after this module loads.
504
+
505
+ ```typescript
506
+ const i18nRegistered: true
507
+ ```
508
+
509
+ #### `querySchema`
510
+
511
+ The resource query Zod schema.
512
+
513
+ ```typescript
514
+ const querySchema: z.ZodObject<
515
+ {
516
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>
517
+ orderBy: z.ZodDefault<z.ZodEnum<{ createdAt: 'createdAt'; updatedAt: 'updatedAt' }>>
518
+ orderDirection: z.ZodDefault<z.ZodEnum<{ asc: 'asc'; desc: 'desc' }>>
519
+ before: z.ZodOptional<
520
+ z.ZodObject<
521
+ { createdAt: z.ZodOptional<z.ZodString>; updatedAt: z.ZodOptional<z.ZodString> },
522
+ z.core.$strip
523
+ >
524
+ >
525
+ after: z.ZodOptional<
526
+ z.ZodObject<
527
+ { createdAt: z.ZodOptional<z.ZodString>; updatedAt: z.ZodOptional<z.ZodString> },
528
+ z.core.$strip
529
+ >
530
+ >
531
+ },
532
+ z.core.$strip
533
+ >
534
+ ```
535
+
536
+ ### Namespaces
537
+
538
+ #### `schema`
539
+
540
+ Members:
541
+
542
+ - `schema.basePropsSchema` — const: Base Zod schema with the three fields every resource row must have:
543
+ - `schema.BaseProps` — type: TypeScript type inferred from `basePropsSchema`, containing `id`, `createdAt`, and `updatedAt`.
544
+ - `schema.responseSchema` — function: Creates a Zod schema for the standard `{ statusCode, body }` response
545
+ - `schema.resourceSchema` — function: Creates a Zod schema that validates a resource descriptor object
546
+ - `schema.validate` — function: Validates data against a Zod schema, throwing an `Error` with a
547
+ - `schema.safeParse` — function: Validates data against a Zod schema without throwing. Returns a discriminated
548
+ - `schema.partial` — function: Creates a schema where all fields are optional, suitable for validating
549
+ - `schema.pick` — function: Creates a schema with only the specified fields, useful for create payloads
550
+
551
+ #### `types`
552
+
553
+ Members:
554
+
555
+ - `types.BaseProps` — type: TypeScript type inferred from `basePropsSchema`, containing `id`, `createdAt`, and `updatedAt`.
556
+ - `types.Props` — interface: The resource's properties to be stored in the database.
557
+ - `types.Resource` — interface: An object describing the resource.
558
+ - `types.ZodResource` — interface: An object describing the resource with a Zod schema.
559
+ - `types.Response` — interface: Standard response from resource operations.
560
+
561
+ #### `utilities`
562
+
563
+ Members:
564
+
565
+ - `utilities.getValidProps` — const: Validates resource props against a Zod schema, returning typed valid props or throwing
566
+ - `utilities.safeParse` — const: Validates data against a Zod schema without throwing, returning a discriminated union.
567
+ - `utilities.validate` — const: Validates data against a Zod schema, throwing a ZodError on failure.
568
+ - `utilities.ZodError` — interface: An Error-like class used to store Zod validation issues.
569
+ - `utilities.ZodSchema` — interface
570
+ - `utilities.isEmail` — const: Basic regex for determining whether or not a string is an email address.
571
+ - `utilities.isUuid` — const: Basic regex for determining whether or not a string is a UUID.
572
+
573
+ ## Injection Notes
574
+
575
+ ### Requirements
576
+
577
+ Peer dependencies:
578
+
579
+ - `@molecule/api-bond` ^1.0.1
580
+ - `@molecule/api-database` ^1.0.1
581
+ - `@molecule/api-i18n` ^1.0.1
582
+ - `@molecule/api-locales-resource` ^1.0.1
583
+ - `@molecule/api-utilities-validation` ^1.0.1
584
+
585
+ ### Runtime Dependencies
586
+
587
+ - `@molecule/api-bond`
588
+ - `@molecule/api-database`
589
+ - `@molecule/api-i18n`
590
+ - `@molecule/api-locales-resource`
591
+ - `@molecule/api-utilities-validation`
592
+ - `uuid`
593
+ - `zod`
594
+
595
+ Working WITH installed `@molecule/api-resource-*` packages (auth `getUserId`,
596
+ table ownership, migrations) — what generated code most often breaks:
597
+
598
+ - **Auth inside a handler is `const userId = getUserId(res)`, imported from
599
+ `@molecule/api-bonds-default-express`** — NOT from this package (this
600
+ package's `utilities` namespace holds only validation helpers). Return 401
601
+ when it is missing. There is NO `requireAuth` middleware or file to import
602
+ or create; auth is applied at the router level. A route's authorizer may
603
+ also stash the already-loaded row on the response context and hand it to
604
+ `read({ id, props })` to skip a second database fetch.
605
+ - **A selected `@molecule/api-resource-*` package OWNS its table AND its
606
+ already-wired routes with a FIXED schema** (e.g. `api-resource-project` owns
607
+ `projects` keyed by `user_id` with a live `/projects` handler). Do NOT write
608
+ a migration that re-creates such a table with a different schema — your
609
+ `CREATE TABLE` shadows theirs (or vice-versa) and the package's still-wired
610
+ handler then 500s (`column "user_id" does not exist`). To model a different
611
+ shape, use a DIFFERENT table name (e.g. `workspace_projects`) + your own
612
+ handler. Check `api/migrations/*` + `api/__setup__/` for tables packages
613
+ already provide.
614
+ - **Adding a resource package AFTER scaffold? Copy its migration.** Such a
615
+ package ships its table DDL at `node_modules/@molecule/<pkg>/src/__setup__/*.sql`
616
+ — copy it into `api/migrations/<next-number>_<name>.sql`. Wiring its
617
+ routes/handler alone does NOT create the table: every request 500s with
618
+ "relation does not exist" even though the build type-checks clean. (Only
619
+ packages present at scaffold time get their migration auto-created.)
620
+ - **Zod/validation helpers are NAMESPACED exports**, not top-level:
621
+ `schema.basePropsSchema` / `schema.validate` / `schema.safeParse` /
622
+ `schema.partial` / `schema.pick`, and `utilities.getValidProps`
623
+ (re-exported from `@molecule/api-utilities-validation`). Import the
624
+ namespace: `import { schema, utilities } from '@molecule/api-resource'`.
625
+
626
+ ## Translations
627
+
628
+ Translation strings are provided by `@molecule/api-locales-resource`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-resource",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Resource CRUD operations and utilities for molecule.dev.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,7 +25,8 @@
25
25
  }
26
26
  },
27
27
  "files": [
28
- "dist"
28
+ "dist",
29
+ "README.md"
29
30
  ],
30
31
  "keywords": [
31
32
  "molecule",
@@ -38,20 +39,20 @@
38
39
  "zod": "4.4.3"
39
40
  },
40
41
  "devDependencies": {
41
- "@molecule/api-bond": "1.0.0",
42
- "@molecule/api-database": "1.0.0",
43
- "@molecule/api-locales-resource": "1.0.0",
44
- "@molecule/api-utilities-validation": "1.0.0",
42
+ "@molecule/api-bond": "1.0.1",
43
+ "@molecule/api-database": "1.0.1",
44
+ "@molecule/api-locales-resource": "1.0.1",
45
+ "@molecule/api-utilities-validation": "1.0.1",
45
46
  "@types/node": "26.1.2",
46
47
  "typescript": "6.0.3",
47
48
  "vitest": "4.1.10"
48
49
  },
49
50
  "peerDependencies": {
50
- "@molecule/api-bond": "^1.0.0",
51
- "@molecule/api-database": "^1.0.0",
52
- "@molecule/api-i18n": "^1.0.0",
53
- "@molecule/api-locales-resource": "^1.0.0",
54
- "@molecule/api-utilities-validation": "^1.0.0"
51
+ "@molecule/api-bond": "^1.0.1",
52
+ "@molecule/api-database": "^1.0.1",
53
+ "@molecule/api-i18n": "^1.0.1",
54
+ "@molecule/api-locales-resource": "^1.0.1",
55
+ "@molecule/api-utilities-validation": "^1.0.1"
55
56
  },
56
57
  "repository": {
57
58
  "type": "git",