@voltom/contracts 1.0.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/dist/index.js ADDED
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ERROR_MESSAGES: () => ERROR_MESSAGES,
24
+ ERROR_STATUS_MAP: () => ERROR_STATUS_MAP,
25
+ FieldSensitivity: () => FieldSensitivity,
26
+ ResponseHeaders: () => ResponseHeaders,
27
+ StandardStatus: () => StandardStatus,
28
+ auditFields: () => auditFields,
29
+ defineContract: () => defineContract,
30
+ listQueryFilters: () => listQueryFilters,
31
+ makeUpdateFields: () => makeUpdateFields,
32
+ paginationQuery: () => paginationQuery,
33
+ softDeleteFields: () => softDeleteFields,
34
+ t: () => t,
35
+ tenantFields: () => tenantFields,
36
+ timestampFields: () => timestampFields
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/primitives.ts
41
+ var import_zod = require("zod");
42
+ var t = {
43
+ /** UUID string (v4 or v5) */
44
+ uuid: () => import_zod.z.string().uuid(),
45
+ /** String with optional min/max length and pattern constraints */
46
+ string: (opts) => {
47
+ let s = import_zod.z.string().trim();
48
+ if (opts?.min !== void 0) s = s.min(opts.min);
49
+ if (opts?.max !== void 0) s = s.max(opts.max);
50
+ if (opts?.pattern) {
51
+ const pattern = typeof opts.pattern === "string" ? new RegExp(opts.pattern) : opts.pattern;
52
+ s = s.regex(pattern, opts.description || `must match pattern: ${pattern}`);
53
+ }
54
+ return s;
55
+ },
56
+ /** Email address (RFC 5322 compliant) */
57
+ email: () => import_zod.z.string().email().toLowerCase(),
58
+ /** Phone number (international format: +[country code][number], or 7+ digits) */
59
+ phone: () => import_zod.z.string().regex(/^\+[1-9]\d{1,14}$|^[0-9]{7,15}$/, "Invalid phone number format (use +CC followed by digits, or 7+ digits)"),
60
+ /** URL (HTTP, HTTPS, or custom protocol) */
61
+ url: () => import_zod.z.string().url(),
62
+ /** URL slug (lowercase alphanumeric, hyphens, underscores) */
63
+ slug: (opts) => {
64
+ let s = import_zod.z.string().regex(/^[a-z0-9]+(?:[_-][a-z0-9]+)*$/, "Invalid slug format");
65
+ if (opts?.min !== void 0) s = s.min(opts.min);
66
+ if (opts?.max !== void 0) s = s.max(opts.max);
67
+ return s;
68
+ },
69
+ /** Strong password (min 8 chars, mixed case, number, special char) */
70
+ password: () => import_zod.z.string().min(8, "Password must be at least 8 characters").regex(/[a-z]/, "Password must contain lowercase letter").regex(/[A-Z]/, "Password must contain uppercase letter").regex(/\d/, "Password must contain number").regex(/[!@#$%^&*(),.?":{}|<>]/, "Password must contain special character"),
71
+ /** IPv4 or IPv6 address */
72
+ ipAddress: () => import_zod.z.string().ip({ version: "v4" }).or(import_zod.z.string().ip({ version: "v6" })),
73
+ /** Hexadecimal string (colors, hashes) */
74
+ hex: (opts) => {
75
+ let s = import_zod.z.string().regex(/^[0-9a-f]*$/i, "Must be valid hexadecimal");
76
+ if (opts?.length) s = s.length(opts.length);
77
+ return s;
78
+ },
79
+ /** Alphanumeric identifier (no special chars) */
80
+ id: () => import_zod.z.string().regex(/^[a-zA-Z0-9_-]+$/, "Invalid ID format"),
81
+ /** Country code (ISO 3166-1 alpha-2) */
82
+ countryCode: () => import_zod.z.string().length(2).regex(/^[A-Z]{2}$/, "Must be ISO 3166-1 alpha-2"),
83
+ /** Currency code (ISO 4217) */
84
+ currencyCode: () => import_zod.z.string().length(3).regex(/^[A-Z]{3}$/, "Must be ISO 4217 code"),
85
+ /** Number with optional min/max bounds */
86
+ number: (opts) => {
87
+ let n = import_zod.z.number().finite();
88
+ if (opts?.min !== void 0) n = n.min(opts.min);
89
+ if (opts?.max !== void 0) n = n.max(opts.max);
90
+ if (opts?.multipleOf !== void 0) n = n.multipleOf(opts.multipleOf);
91
+ return n;
92
+ },
93
+ /** Integer with optional min/max bounds */
94
+ int: (opts) => {
95
+ let n = import_zod.z.number().int();
96
+ if (opts?.min !== void 0) n = n.min(opts.min);
97
+ if (opts?.max !== void 0) n = n.max(opts.max);
98
+ if (opts?.positive) n = n.positive();
99
+ if (opts?.negative) n = n.negative();
100
+ return n;
101
+ },
102
+ /** Percentage (0-100) */
103
+ percent: () => import_zod.z.number().min(0).max(100),
104
+ /** Boolean */
105
+ boolean: () => import_zod.z.boolean(),
106
+ /** ISO 8601 datetime (e.g., "2024-01-15T10:30:00Z") */
107
+ datetime: () => import_zod.z.string().datetime({ offset: true }),
108
+ /** ISO 8601 date (e.g., "2024-01-15") */
109
+ date: () => import_zod.z.string().date(),
110
+ /** ISO 8601 time (e.g., "14:30:00") */
111
+ time: () => import_zod.z.string().regex(/^\d{2}:\d{2}:\d{2}(.\d{3})?$/, "Invalid time format"),
112
+ /** Unix timestamp (seconds since epoch) */
113
+ timestamp: () => import_zod.z.number().int().positive(),
114
+ /** Enum with fixed string values */
115
+ enum: (values) => import_zod.z.enum(values),
116
+ /** Arbitrary JSON value (object, array, primitive) */
117
+ json: () => import_zod.z.any(),
118
+ /** Make a schema optional (allows undefined) */
119
+ optional: (schema) => schema.optional(),
120
+ /** Make a schema nullable (allows null) */
121
+ nullable: (schema) => schema.nullable(),
122
+ /** Array of a schema with optional length constraints */
123
+ array: (schema, opts) => {
124
+ let arr = import_zod.z.array(schema);
125
+ if (opts?.min !== void 0) arr = arr.min(opts.min);
126
+ if (opts?.max !== void 0) arr = arr.max(opts.max);
127
+ if (opts?.nonempty) arr = arr.nonempty("Array cannot be empty");
128
+ return arr;
129
+ }
130
+ };
131
+
132
+ // src/errors.ts
133
+ var ERROR_STATUS_MAP = {
134
+ VALIDATION_ERROR: 400,
135
+ UNAUTHORIZED: 401,
136
+ FORBIDDEN: 403,
137
+ NOT_FOUND: 404,
138
+ CONFLICT: 409,
139
+ RATE_LIMITED: 429,
140
+ UNPROCESSABLE: 422,
141
+ INTERNAL_ERROR: 500
142
+ };
143
+ var ERROR_MESSAGES = {
144
+ VALIDATION_ERROR: "Invalid input. Check error details for specific field issues.",
145
+ UNAUTHORIZED: "Authentication required. Please log in or provide valid credentials.",
146
+ FORBIDDEN: "You do not have permission to access this resource.",
147
+ NOT_FOUND: "The requested resource was not found.",
148
+ CONFLICT: "This operation conflicts with existing data or system state.",
149
+ RATE_LIMITED: "Too many requests. Please wait before trying again.",
150
+ UNPROCESSABLE: "Your request is valid but cannot be processed at this time.",
151
+ INTERNAL_ERROR: "An unexpected error occurred. Our team has been notified."
152
+ };
153
+
154
+ // src/contract.ts
155
+ function defineContract(config) {
156
+ return config;
157
+ }
158
+
159
+ // src/helpers.ts
160
+ var import_zod2 = require("zod");
161
+ var auditFields = {
162
+ created_at: t.datetime(),
163
+ created_by: t.uuid(),
164
+ updated_at: t.datetime(),
165
+ updated_by: t.uuid()
166
+ };
167
+ var softDeleteFields = {
168
+ is_active: t.boolean(),
169
+ deleted_at: t.nullable(t.datetime()),
170
+ deleted_by: t.nullable(t.uuid())
171
+ };
172
+ var timestampFields = {
173
+ created_at: t.datetime(),
174
+ updated_at: t.datetime()
175
+ };
176
+ var tenantFields = {
177
+ tenant_id: t.uuid(),
178
+ organization_id: t.uuid()
179
+ };
180
+ var StandardStatus = {
181
+ DRAFT: "draft",
182
+ ACTIVE: "active",
183
+ INACTIVE: "inactive",
184
+ ARCHIVED: "archived",
185
+ DELETED: "deleted"
186
+ };
187
+ var listQueryFilters = {
188
+ search: import_zod2.z.string().optional(),
189
+ status: import_zod2.z.string().optional(),
190
+ sort_by: import_zod2.z.string().optional(),
191
+ sort_direction: import_zod2.z.enum(["asc", "desc"]).optional(),
192
+ limit: import_zod2.z.number().int().min(1).max(100).optional().default(20),
193
+ offset: import_zod2.z.number().int().min(0).optional().default(0)
194
+ };
195
+ var paginationQuery = {
196
+ page: import_zod2.z.number().int().min(1).optional().default(1),
197
+ per_page: import_zod2.z.number().int().min(1).max(100).optional().default(20)
198
+ };
199
+ var makeUpdateFields = (entity, excludeFields = ["id"]) => {
200
+ const result = {};
201
+ for (const [key, schema] of Object.entries(entity)) {
202
+ if (!excludeFields.includes(key)) {
203
+ result[key] = schema.optional();
204
+ }
205
+ }
206
+ return result;
207
+ };
208
+ var ResponseHeaders = {
209
+ /** Cache-Control header value */
210
+ CACHE: {
211
+ NO_CACHE: "no-cache, no-store, must-revalidate",
212
+ SHORT: "public, max-age=300",
213
+ // 5 minutes
214
+ MEDIUM: "public, max-age=3600",
215
+ // 1 hour
216
+ LONG: "public, max-age=86400",
217
+ // 24 hours
218
+ NEVER: "no-store"
219
+ }
220
+ };
221
+ var FieldSensitivity = {
222
+ PUBLIC: "public",
223
+ INTERNAL: "internal",
224
+ CONFIDENTIAL: "confidential",
225
+ // PII, payment, credentials
226
+ SECRET: "secret"
227
+ // Never log, hash, or transmit unencrypted
228
+ };
229
+ // Annotate the CommonJS export names for ESM import in node:
230
+ 0 && (module.exports = {
231
+ ERROR_MESSAGES,
232
+ ERROR_STATUS_MAP,
233
+ FieldSensitivity,
234
+ ResponseHeaders,
235
+ StandardStatus,
236
+ auditFields,
237
+ defineContract,
238
+ listQueryFilters,
239
+ makeUpdateFields,
240
+ paginationQuery,
241
+ softDeleteFields,
242
+ t,
243
+ tenantFields,
244
+ timestampFields
245
+ });
246
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/primitives.ts","../src/errors.ts","../src/contract.ts","../src/helpers.ts"],"sourcesContent":["/**\n * @voltom/contracts\n *\n * Universal contract language for voltom products.\n * Primitive types, response shapes, error codes, and contract engine.\n *\n * Usage:\n * - import { defineContract, t } from '@voltom/contracts'\n * - import type { SingleResponse, ApiError } from '@voltom/contracts'\n * - import { auditFields, softDeleteFields } from '@voltom/contracts'\n */\n\n// Primitive type builders\nexport { t } from './primitives'\n\n// Response shapes (canonical envelopes)\nexport type {\n PaginationMeta,\n CursorPaginationMeta,\n FieldError,\n SingleResponse,\n PaginatedResponse,\n CursorPaginatedResponse,\n DeletedResponse,\n BulkResponse,\n ApiError,\n} from './responses'\n\n// Error codes\nexport type { ErrorCode } from './errors'\nexport { ERROR_STATUS_MAP, ERROR_MESSAGES } from './errors'\n\n// Contract engine\nexport { defineContract } from './contract'\nexport type {\n ContractConfig,\n Contract,\n EndpointDef,\n ActionDef,\n} from './types/contract.types'\n\n// Production helpers\nexport {\n auditFields,\n softDeleteFields,\n timestampFields,\n tenantFields,\n StandardStatus,\n listQueryFilters,\n paginationQuery,\n makeUpdateFields,\n ResponseHeaders,\n FieldSensitivity,\n} from './helpers'\n\nexport type { SortDirection } from './helpers'\n","import { z } from 'zod'\n\n/**\n * Universal primitive type builders for voltom contracts.\n * Every field in every contract is built using these.\n * They wrap Zod schemas with cleaner names and consistent validation.\n *\n * All validators are designed for production use:\n * - Security (no injection vectors)\n * - Performance (fast validation)\n * - Clarity (error messages are useful)\n */\nexport const t = {\n /** UUID string (v4 or v5) */\n uuid: () => z.string().uuid(),\n\n /** String with optional min/max length and pattern constraints */\n string: (opts?: {\n min?: number\n max?: number\n pattern?: RegExp | string\n description?: string\n }) => {\n let s = z.string().trim()\n if (opts?.min !== undefined) s = s.min(opts.min)\n if (opts?.max !== undefined) s = s.max(opts.max)\n if (opts?.pattern) {\n const pattern = typeof opts.pattern === 'string' ? new RegExp(opts.pattern) : opts.pattern\n s = s.regex(pattern, opts.description || `must match pattern: ${pattern}`)\n }\n return s\n },\n\n /** Email address (RFC 5322 compliant) */\n email: () => z.string().email().toLowerCase(),\n\n /** Phone number (international format: +[country code][number], or 7+ digits) */\n phone: () =>\n z.string().regex(/^\\+[1-9]\\d{1,14}$|^[0-9]{7,15}$/, 'Invalid phone number format (use +CC followed by digits, or 7+ digits)'),\n\n /** URL (HTTP, HTTPS, or custom protocol) */\n url: () => z.string().url(),\n\n /** URL slug (lowercase alphanumeric, hyphens, underscores) */\n slug: (opts?: { min?: number; max?: number }) => {\n let s = z.string().regex(/^[a-z0-9]+(?:[_-][a-z0-9]+)*$/, 'Invalid slug format')\n if (opts?.min !== undefined) s = s.min(opts.min)\n if (opts?.max !== undefined) s = s.max(opts.max)\n return s\n },\n\n /** Strong password (min 8 chars, mixed case, number, special char) */\n password: () =>\n z\n .string()\n .min(8, 'Password must be at least 8 characters')\n .regex(/[a-z]/, 'Password must contain lowercase letter')\n .regex(/[A-Z]/, 'Password must contain uppercase letter')\n .regex(/\\d/, 'Password must contain number')\n .regex(/[!@#$%^&*(),.?\":{}|<>]/, 'Password must contain special character'),\n\n /** IPv4 or IPv6 address */\n ipAddress: () =>\n z.string().ip({ version: 'v4' }).or(z.string().ip({ version: 'v6' })),\n\n /** Hexadecimal string (colors, hashes) */\n hex: (opts?: { length?: number }) => {\n let s = z.string().regex(/^[0-9a-f]*$/i, 'Must be valid hexadecimal')\n if (opts?.length) s = s.length(opts.length)\n return s\n },\n\n /** Alphanumeric identifier (no special chars) */\n id: () => z.string().regex(/^[a-zA-Z0-9_-]+$/, 'Invalid ID format'),\n\n /** Country code (ISO 3166-1 alpha-2) */\n countryCode: () =>\n z.string().length(2).regex(/^[A-Z]{2}$/, 'Must be ISO 3166-1 alpha-2'),\n\n /** Currency code (ISO 4217) */\n currencyCode: () =>\n z.string().length(3).regex(/^[A-Z]{3}$/, 'Must be ISO 4217 code'),\n\n /** Number with optional min/max bounds */\n number: (opts?: { min?: number; max?: number; multipleOf?: number }) => {\n let n = z.number().finite()\n if (opts?.min !== undefined) n = n.min(opts.min)\n if (opts?.max !== undefined) n = n.max(opts.max)\n if (opts?.multipleOf !== undefined) n = n.multipleOf(opts.multipleOf)\n return n\n },\n\n /** Integer with optional min/max bounds */\n int: (opts?: { min?: number; max?: number; positive?: boolean; negative?: boolean }) => {\n let n = z.number().int()\n if (opts?.min !== undefined) n = n.min(opts.min)\n if (opts?.max !== undefined) n = n.max(opts.max)\n if (opts?.positive) n = n.positive()\n if (opts?.negative) n = n.negative()\n return n\n },\n\n /** Percentage (0-100) */\n percent: () => z.number().min(0).max(100),\n\n /** Boolean */\n boolean: () => z.boolean(),\n\n /** ISO 8601 datetime (e.g., \"2024-01-15T10:30:00Z\") */\n datetime: () => z.string().datetime({ offset: true }),\n\n /** ISO 8601 date (e.g., \"2024-01-15\") */\n date: () => z.string().date(),\n\n /** ISO 8601 time (e.g., \"14:30:00\") */\n time: () => z.string().regex(/^\\d{2}:\\d{2}:\\d{2}(.\\d{3})?$/, 'Invalid time format'),\n\n /** Unix timestamp (seconds since epoch) */\n timestamp: () => z.number().int().positive(),\n\n /** Enum with fixed string values */\n enum: <T extends readonly [string, ...string[]]>(values: T) =>\n z.enum(values),\n\n /** Arbitrary JSON value (object, array, primitive) */\n json: () => z.any(),\n\n /** Make a schema optional (allows undefined) */\n optional: <S extends z.ZodTypeAny>(schema: S) => schema.optional(),\n\n /** Make a schema nullable (allows null) */\n nullable: <S extends z.ZodTypeAny>(schema: S) => schema.nullable(),\n\n /** Array of a schema with optional length constraints */\n array: <S extends z.ZodTypeAny>(\n schema: S,\n opts?: { min?: number; max?: number; nonempty?: boolean }\n ) => {\n // Type is 'any' because Zod's chain methods return different ZodArray variants\n // nonempty() returns ZodArray<S, \"atleastone\">, while min/max return ZodArray<S, \"many\">\n // These types are incompatible in TypeScript, so we use 'any' for the builder pattern\n let arr: any = z.array(schema)\n if (opts?.min !== undefined) arr = arr.min(opts.min)\n if (opts?.max !== undefined) arr = arr.max(opts.max)\n if (opts?.nonempty) arr = arr.nonempty('Array cannot be empty')\n return arr\n },\n} as const\n","/**\n * Canonical error codes for voltom APIs.\n * These are the ONLY error codes that should be used across all products.\n * Maps to HTTP status codes via ERROR_STATUS_MAP.\n *\n * Do NOT add product-specific codes. Keep these universal.\n * If you need product logic, handle it in the backend with additional data.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR' // 400 — invalid input fields (include field-level errors)\n | 'UNAUTHORIZED' // 401 — no session, invalid token, or API key\n | 'FORBIDDEN' // 403 — authenticated but not allowed (no permission)\n | 'NOT_FOUND' // 404 — resource does not exist\n | 'CONFLICT' // 409 — duplicate key, invalid state transition, or race condition\n | 'UNPROCESSABLE' // 422 — valid input but business logic rejected it\n | 'RATE_LIMITED' // 429 — too many requests (include retry-after)\n | 'INTERNAL_ERROR' // 500 — unexpected server error (log details server-side)\n\n/**\n * Map each ErrorCode to its HTTP status code.\n * Use this in backend exception filters and middleware.\n */\nexport const ERROR_STATUS_MAP: Record<ErrorCode, number> = {\n VALIDATION_ERROR: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n RATE_LIMITED: 429,\n UNPROCESSABLE: 422,\n INTERNAL_ERROR: 500,\n} as const\n\n/**\n * Default error messages for each code.\n * Use these as fallback or customize with domain-specific context.\n */\nexport const ERROR_MESSAGES: Record<ErrorCode, string> = {\n VALIDATION_ERROR: 'Invalid input. Check error details for specific field issues.',\n UNAUTHORIZED: 'Authentication required. Please log in or provide valid credentials.',\n FORBIDDEN: 'You do not have permission to access this resource.',\n NOT_FOUND: 'The requested resource was not found.',\n CONFLICT: 'This operation conflicts with existing data or system state.',\n RATE_LIMITED: 'Too many requests. Please wait before trying again.',\n UNPROCESSABLE: 'Your request is valid but cannot be processed at this time.',\n INTERNAL_ERROR: 'An unexpected error occurred. Our team has been notified.',\n} as const\n","import type { ContractConfig, Contract } from './types/contract.types'\n\n/**\n * Define a contract for a resource in your product.\n *\n * This function validates the contract shape and returns it for use throughout\n * your product (backend, frontend, codegen). The real power is in TypeScript's\n * type inference — once you call defineContract(), every property is typed\n * and all downstream consumers know exactly what to expect.\n *\n * @param config The contract configuration\n * @returns The same contract config (validated)\n *\n * @example\n * ```typescript\n * const usersContract = defineContract({\n * resource: 'users',\n * basePath: '/api/v1/users',\n * entity: {\n * id: t.uuid(),\n * email: t.email(),\n * name: t.string({ min: 1, max: 255 }),\n * },\n * endpoints: {\n * list: { method: 'GET', path: '/', response: 'paginated' },\n * show: { method: 'GET', path: '/:id', response: 'single' },\n * create: { method: 'POST', path: '/', body: 'CreateUser' },\n * },\n * bodies: {\n * CreateUser: {\n * email: t.email(),\n * name: t.string({ min: 1, max: 255 }),\n * },\n * },\n * })\n * ```\n */\nexport function defineContract<T extends ContractConfig>(config: T): T & Contract {\n // In the future, add validation logic here if needed.\n // For now, this function serves as a type guard and entry point.\n return config as T & Contract\n}\n","import { z } from 'zod'\nimport { t } from './primitives'\n\n/**\n * Standard fields that most entities should have.\n * Use these to ensure consistency across products.\n */\n\n/**\n * Audit trail fields (who, when)\n * Include on entities that track ownership or modification history.\n */\nexport const auditFields = {\n created_at: t.datetime(),\n created_by: t.uuid(),\n updated_at: t.datetime(),\n updated_by: t.uuid(),\n} as const\n\n/**\n * Soft delete fields (mark as deleted without removing data)\n * Use when you need to preserve data for compliance or recovery.\n */\nexport const softDeleteFields = {\n is_active: t.boolean(),\n deleted_at: t.nullable(t.datetime()),\n deleted_by: t.nullable(t.uuid()),\n} as const\n\n/**\n * Timestamp fields only (no user tracking)\n * Minimum audit trail for systems that don't track users.\n */\nexport const timestampFields = {\n created_at: t.datetime(),\n updated_at: t.datetime(),\n} as const\n\n/**\n * Standard metadata for multi-tenant systems\n * Use when entities belong to organizations or accounts.\n */\nexport const tenantFields = {\n tenant_id: t.uuid(),\n organization_id: t.uuid(),\n} as const\n\n/**\n * Status enum commonly used across products\n * Extend in product-specific contracts if needed.\n */\nexport const StandardStatus = {\n DRAFT: 'draft',\n ACTIVE: 'active',\n INACTIVE: 'inactive',\n ARCHIVED: 'archived',\n DELETED: 'deleted',\n} as const\n\nexport type StandardStatus = (typeof StandardStatus)[keyof typeof StandardStatus]\n\n/**\n * Sort direction for list queries\n */\nexport type SortDirection = 'asc' | 'desc'\n\n/**\n * Standard query filters for list endpoints\n * Use when your contract needs search/filter capabilities.\n */\nexport const listQueryFilters = {\n search: z.string().optional(),\n status: z.string().optional(),\n sort_by: z.string().optional(),\n sort_direction: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().min(1).max(100).optional().default(20),\n offset: z.number().int().min(0).optional().default(0),\n} as const\n\n/**\n * Standard pagination query parameters\n */\nexport const paginationQuery = {\n page: z.number().int().min(1).optional().default(1),\n per_page: z.number().int().min(1).max(100).optional().default(20),\n} as const\n\n/**\n * Create a consistent 'create' body type from an entity\n *\n * @example\n * ```typescript\n * const userEntity = { id: t.uuid(), email: t.email(), name: t.string() }\n * type CreateUserBody = OmitFields<typeof userEntity, 'id'> // email, name\n * ```\n *\n * Note: In your contract, you can also just define bodies explicitly:\n * bodies: { CreateUser: { email: t.email(), name: t.string() } }\n */\n\n/**\n * Create a consistent 'update' body type from an entity\n * (all fields optional except id)\n */\nexport const makeUpdateFields = <T extends Record<string, z.ZodTypeAny>>(\n entity: T,\n excludeFields: (keyof T)[] = ['id']\n): Record<string, z.ZodTypeAny> => {\n const result: Record<string, z.ZodTypeAny> = {}\n\n for (const [key, schema] of Object.entries(entity)) {\n if (!excludeFields.includes(key as keyof T)) {\n result[key] = schema.optional()\n }\n }\n\n return result\n}\n\n/**\n * Standard HTTP header hints for backend/frontend coordination\n * Not part of contract, but useful metadata for documentation\n */\nexport const ResponseHeaders = {\n /** Cache-Control header value */\n CACHE: {\n NO_CACHE: 'no-cache, no-store, must-revalidate',\n SHORT: 'public, max-age=300', // 5 minutes\n MEDIUM: 'public, max-age=3600', // 1 hour\n LONG: 'public, max-age=86400', // 24 hours\n NEVER: 'no-store',\n },\n} as const\n\n/**\n * Security hints for contract fields\n * Mark sensitive fields so frontend/backend handle them carefully\n */\nexport const FieldSensitivity = {\n PUBLIC: 'public',\n INTERNAL: 'internal',\n CONFIDENTIAL: 'confidential', // PII, payment, credentials\n SECRET: 'secret', // Never log, hash, or transmit unencrypted\n} as const\n\nexport type FieldSensitivity = (typeof FieldSensitivity)[keyof typeof FieldSensitivity]\n"],"mappings":"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","names":["import_zod"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,206 @@
1
+ // src/primitives.ts
2
+ import { z } from "zod";
3
+ var t = {
4
+ /** UUID string (v4 or v5) */
5
+ uuid: () => z.string().uuid(),
6
+ /** String with optional min/max length and pattern constraints */
7
+ string: (opts) => {
8
+ let s = z.string().trim();
9
+ if (opts?.min !== void 0) s = s.min(opts.min);
10
+ if (opts?.max !== void 0) s = s.max(opts.max);
11
+ if (opts?.pattern) {
12
+ const pattern = typeof opts.pattern === "string" ? new RegExp(opts.pattern) : opts.pattern;
13
+ s = s.regex(pattern, opts.description || `must match pattern: ${pattern}`);
14
+ }
15
+ return s;
16
+ },
17
+ /** Email address (RFC 5322 compliant) */
18
+ email: () => z.string().email().toLowerCase(),
19
+ /** Phone number (international format: +[country code][number], or 7+ digits) */
20
+ phone: () => z.string().regex(/^\+[1-9]\d{1,14}$|^[0-9]{7,15}$/, "Invalid phone number format (use +CC followed by digits, or 7+ digits)"),
21
+ /** URL (HTTP, HTTPS, or custom protocol) */
22
+ url: () => z.string().url(),
23
+ /** URL slug (lowercase alphanumeric, hyphens, underscores) */
24
+ slug: (opts) => {
25
+ let s = z.string().regex(/^[a-z0-9]+(?:[_-][a-z0-9]+)*$/, "Invalid slug format");
26
+ if (opts?.min !== void 0) s = s.min(opts.min);
27
+ if (opts?.max !== void 0) s = s.max(opts.max);
28
+ return s;
29
+ },
30
+ /** Strong password (min 8 chars, mixed case, number, special char) */
31
+ password: () => z.string().min(8, "Password must be at least 8 characters").regex(/[a-z]/, "Password must contain lowercase letter").regex(/[A-Z]/, "Password must contain uppercase letter").regex(/\d/, "Password must contain number").regex(/[!@#$%^&*(),.?":{}|<>]/, "Password must contain special character"),
32
+ /** IPv4 or IPv6 address */
33
+ ipAddress: () => z.string().ip({ version: "v4" }).or(z.string().ip({ version: "v6" })),
34
+ /** Hexadecimal string (colors, hashes) */
35
+ hex: (opts) => {
36
+ let s = z.string().regex(/^[0-9a-f]*$/i, "Must be valid hexadecimal");
37
+ if (opts?.length) s = s.length(opts.length);
38
+ return s;
39
+ },
40
+ /** Alphanumeric identifier (no special chars) */
41
+ id: () => z.string().regex(/^[a-zA-Z0-9_-]+$/, "Invalid ID format"),
42
+ /** Country code (ISO 3166-1 alpha-2) */
43
+ countryCode: () => z.string().length(2).regex(/^[A-Z]{2}$/, "Must be ISO 3166-1 alpha-2"),
44
+ /** Currency code (ISO 4217) */
45
+ currencyCode: () => z.string().length(3).regex(/^[A-Z]{3}$/, "Must be ISO 4217 code"),
46
+ /** Number with optional min/max bounds */
47
+ number: (opts) => {
48
+ let n = z.number().finite();
49
+ if (opts?.min !== void 0) n = n.min(opts.min);
50
+ if (opts?.max !== void 0) n = n.max(opts.max);
51
+ if (opts?.multipleOf !== void 0) n = n.multipleOf(opts.multipleOf);
52
+ return n;
53
+ },
54
+ /** Integer with optional min/max bounds */
55
+ int: (opts) => {
56
+ let n = z.number().int();
57
+ if (opts?.min !== void 0) n = n.min(opts.min);
58
+ if (opts?.max !== void 0) n = n.max(opts.max);
59
+ if (opts?.positive) n = n.positive();
60
+ if (opts?.negative) n = n.negative();
61
+ return n;
62
+ },
63
+ /** Percentage (0-100) */
64
+ percent: () => z.number().min(0).max(100),
65
+ /** Boolean */
66
+ boolean: () => z.boolean(),
67
+ /** ISO 8601 datetime (e.g., "2024-01-15T10:30:00Z") */
68
+ datetime: () => z.string().datetime({ offset: true }),
69
+ /** ISO 8601 date (e.g., "2024-01-15") */
70
+ date: () => z.string().date(),
71
+ /** ISO 8601 time (e.g., "14:30:00") */
72
+ time: () => z.string().regex(/^\d{2}:\d{2}:\d{2}(.\d{3})?$/, "Invalid time format"),
73
+ /** Unix timestamp (seconds since epoch) */
74
+ timestamp: () => z.number().int().positive(),
75
+ /** Enum with fixed string values */
76
+ enum: (values) => z.enum(values),
77
+ /** Arbitrary JSON value (object, array, primitive) */
78
+ json: () => z.any(),
79
+ /** Make a schema optional (allows undefined) */
80
+ optional: (schema) => schema.optional(),
81
+ /** Make a schema nullable (allows null) */
82
+ nullable: (schema) => schema.nullable(),
83
+ /** Array of a schema with optional length constraints */
84
+ array: (schema, opts) => {
85
+ let arr = z.array(schema);
86
+ if (opts?.min !== void 0) arr = arr.min(opts.min);
87
+ if (opts?.max !== void 0) arr = arr.max(opts.max);
88
+ if (opts?.nonempty) arr = arr.nonempty("Array cannot be empty");
89
+ return arr;
90
+ }
91
+ };
92
+
93
+ // src/errors.ts
94
+ var ERROR_STATUS_MAP = {
95
+ VALIDATION_ERROR: 400,
96
+ UNAUTHORIZED: 401,
97
+ FORBIDDEN: 403,
98
+ NOT_FOUND: 404,
99
+ CONFLICT: 409,
100
+ RATE_LIMITED: 429,
101
+ UNPROCESSABLE: 422,
102
+ INTERNAL_ERROR: 500
103
+ };
104
+ var ERROR_MESSAGES = {
105
+ VALIDATION_ERROR: "Invalid input. Check error details for specific field issues.",
106
+ UNAUTHORIZED: "Authentication required. Please log in or provide valid credentials.",
107
+ FORBIDDEN: "You do not have permission to access this resource.",
108
+ NOT_FOUND: "The requested resource was not found.",
109
+ CONFLICT: "This operation conflicts with existing data or system state.",
110
+ RATE_LIMITED: "Too many requests. Please wait before trying again.",
111
+ UNPROCESSABLE: "Your request is valid but cannot be processed at this time.",
112
+ INTERNAL_ERROR: "An unexpected error occurred. Our team has been notified."
113
+ };
114
+
115
+ // src/contract.ts
116
+ function defineContract(config) {
117
+ return config;
118
+ }
119
+
120
+ // src/helpers.ts
121
+ import { z as z2 } from "zod";
122
+ var auditFields = {
123
+ created_at: t.datetime(),
124
+ created_by: t.uuid(),
125
+ updated_at: t.datetime(),
126
+ updated_by: t.uuid()
127
+ };
128
+ var softDeleteFields = {
129
+ is_active: t.boolean(),
130
+ deleted_at: t.nullable(t.datetime()),
131
+ deleted_by: t.nullable(t.uuid())
132
+ };
133
+ var timestampFields = {
134
+ created_at: t.datetime(),
135
+ updated_at: t.datetime()
136
+ };
137
+ var tenantFields = {
138
+ tenant_id: t.uuid(),
139
+ organization_id: t.uuid()
140
+ };
141
+ var StandardStatus = {
142
+ DRAFT: "draft",
143
+ ACTIVE: "active",
144
+ INACTIVE: "inactive",
145
+ ARCHIVED: "archived",
146
+ DELETED: "deleted"
147
+ };
148
+ var listQueryFilters = {
149
+ search: z2.string().optional(),
150
+ status: z2.string().optional(),
151
+ sort_by: z2.string().optional(),
152
+ sort_direction: z2.enum(["asc", "desc"]).optional(),
153
+ limit: z2.number().int().min(1).max(100).optional().default(20),
154
+ offset: z2.number().int().min(0).optional().default(0)
155
+ };
156
+ var paginationQuery = {
157
+ page: z2.number().int().min(1).optional().default(1),
158
+ per_page: z2.number().int().min(1).max(100).optional().default(20)
159
+ };
160
+ var makeUpdateFields = (entity, excludeFields = ["id"]) => {
161
+ const result = {};
162
+ for (const [key, schema] of Object.entries(entity)) {
163
+ if (!excludeFields.includes(key)) {
164
+ result[key] = schema.optional();
165
+ }
166
+ }
167
+ return result;
168
+ };
169
+ var ResponseHeaders = {
170
+ /** Cache-Control header value */
171
+ CACHE: {
172
+ NO_CACHE: "no-cache, no-store, must-revalidate",
173
+ SHORT: "public, max-age=300",
174
+ // 5 minutes
175
+ MEDIUM: "public, max-age=3600",
176
+ // 1 hour
177
+ LONG: "public, max-age=86400",
178
+ // 24 hours
179
+ NEVER: "no-store"
180
+ }
181
+ };
182
+ var FieldSensitivity = {
183
+ PUBLIC: "public",
184
+ INTERNAL: "internal",
185
+ CONFIDENTIAL: "confidential",
186
+ // PII, payment, credentials
187
+ SECRET: "secret"
188
+ // Never log, hash, or transmit unencrypted
189
+ };
190
+ export {
191
+ ERROR_MESSAGES,
192
+ ERROR_STATUS_MAP,
193
+ FieldSensitivity,
194
+ ResponseHeaders,
195
+ StandardStatus,
196
+ auditFields,
197
+ defineContract,
198
+ listQueryFilters,
199
+ makeUpdateFields,
200
+ paginationQuery,
201
+ softDeleteFields,
202
+ t,
203
+ tenantFields,
204
+ timestampFields
205
+ };
206
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/primitives.ts","../src/errors.ts","../src/contract.ts","../src/helpers.ts"],"sourcesContent":["import { z } from 'zod'\n\n/**\n * Universal primitive type builders for voltom contracts.\n * Every field in every contract is built using these.\n * They wrap Zod schemas with cleaner names and consistent validation.\n *\n * All validators are designed for production use:\n * - Security (no injection vectors)\n * - Performance (fast validation)\n * - Clarity (error messages are useful)\n */\nexport const t = {\n /** UUID string (v4 or v5) */\n uuid: () => z.string().uuid(),\n\n /** String with optional min/max length and pattern constraints */\n string: (opts?: {\n min?: number\n max?: number\n pattern?: RegExp | string\n description?: string\n }) => {\n let s = z.string().trim()\n if (opts?.min !== undefined) s = s.min(opts.min)\n if (opts?.max !== undefined) s = s.max(opts.max)\n if (opts?.pattern) {\n const pattern = typeof opts.pattern === 'string' ? new RegExp(opts.pattern) : opts.pattern\n s = s.regex(pattern, opts.description || `must match pattern: ${pattern}`)\n }\n return s\n },\n\n /** Email address (RFC 5322 compliant) */\n email: () => z.string().email().toLowerCase(),\n\n /** Phone number (international format: +[country code][number], or 7+ digits) */\n phone: () =>\n z.string().regex(/^\\+[1-9]\\d{1,14}$|^[0-9]{7,15}$/, 'Invalid phone number format (use +CC followed by digits, or 7+ digits)'),\n\n /** URL (HTTP, HTTPS, or custom protocol) */\n url: () => z.string().url(),\n\n /** URL slug (lowercase alphanumeric, hyphens, underscores) */\n slug: (opts?: { min?: number; max?: number }) => {\n let s = z.string().regex(/^[a-z0-9]+(?:[_-][a-z0-9]+)*$/, 'Invalid slug format')\n if (opts?.min !== undefined) s = s.min(opts.min)\n if (opts?.max !== undefined) s = s.max(opts.max)\n return s\n },\n\n /** Strong password (min 8 chars, mixed case, number, special char) */\n password: () =>\n z\n .string()\n .min(8, 'Password must be at least 8 characters')\n .regex(/[a-z]/, 'Password must contain lowercase letter')\n .regex(/[A-Z]/, 'Password must contain uppercase letter')\n .regex(/\\d/, 'Password must contain number')\n .regex(/[!@#$%^&*(),.?\":{}|<>]/, 'Password must contain special character'),\n\n /** IPv4 or IPv6 address */\n ipAddress: () =>\n z.string().ip({ version: 'v4' }).or(z.string().ip({ version: 'v6' })),\n\n /** Hexadecimal string (colors, hashes) */\n hex: (opts?: { length?: number }) => {\n let s = z.string().regex(/^[0-9a-f]*$/i, 'Must be valid hexadecimal')\n if (opts?.length) s = s.length(opts.length)\n return s\n },\n\n /** Alphanumeric identifier (no special chars) */\n id: () => z.string().regex(/^[a-zA-Z0-9_-]+$/, 'Invalid ID format'),\n\n /** Country code (ISO 3166-1 alpha-2) */\n countryCode: () =>\n z.string().length(2).regex(/^[A-Z]{2}$/, 'Must be ISO 3166-1 alpha-2'),\n\n /** Currency code (ISO 4217) */\n currencyCode: () =>\n z.string().length(3).regex(/^[A-Z]{3}$/, 'Must be ISO 4217 code'),\n\n /** Number with optional min/max bounds */\n number: (opts?: { min?: number; max?: number; multipleOf?: number }) => {\n let n = z.number().finite()\n if (opts?.min !== undefined) n = n.min(opts.min)\n if (opts?.max !== undefined) n = n.max(opts.max)\n if (opts?.multipleOf !== undefined) n = n.multipleOf(opts.multipleOf)\n return n\n },\n\n /** Integer with optional min/max bounds */\n int: (opts?: { min?: number; max?: number; positive?: boolean; negative?: boolean }) => {\n let n = z.number().int()\n if (opts?.min !== undefined) n = n.min(opts.min)\n if (opts?.max !== undefined) n = n.max(opts.max)\n if (opts?.positive) n = n.positive()\n if (opts?.negative) n = n.negative()\n return n\n },\n\n /** Percentage (0-100) */\n percent: () => z.number().min(0).max(100),\n\n /** Boolean */\n boolean: () => z.boolean(),\n\n /** ISO 8601 datetime (e.g., \"2024-01-15T10:30:00Z\") */\n datetime: () => z.string().datetime({ offset: true }),\n\n /** ISO 8601 date (e.g., \"2024-01-15\") */\n date: () => z.string().date(),\n\n /** ISO 8601 time (e.g., \"14:30:00\") */\n time: () => z.string().regex(/^\\d{2}:\\d{2}:\\d{2}(.\\d{3})?$/, 'Invalid time format'),\n\n /** Unix timestamp (seconds since epoch) */\n timestamp: () => z.number().int().positive(),\n\n /** Enum with fixed string values */\n enum: <T extends readonly [string, ...string[]]>(values: T) =>\n z.enum(values),\n\n /** Arbitrary JSON value (object, array, primitive) */\n json: () => z.any(),\n\n /** Make a schema optional (allows undefined) */\n optional: <S extends z.ZodTypeAny>(schema: S) => schema.optional(),\n\n /** Make a schema nullable (allows null) */\n nullable: <S extends z.ZodTypeAny>(schema: S) => schema.nullable(),\n\n /** Array of a schema with optional length constraints */\n array: <S extends z.ZodTypeAny>(\n schema: S,\n opts?: { min?: number; max?: number; nonempty?: boolean }\n ) => {\n // Type is 'any' because Zod's chain methods return different ZodArray variants\n // nonempty() returns ZodArray<S, \"atleastone\">, while min/max return ZodArray<S, \"many\">\n // These types are incompatible in TypeScript, so we use 'any' for the builder pattern\n let arr: any = z.array(schema)\n if (opts?.min !== undefined) arr = arr.min(opts.min)\n if (opts?.max !== undefined) arr = arr.max(opts.max)\n if (opts?.nonempty) arr = arr.nonempty('Array cannot be empty')\n return arr\n },\n} as const\n","/**\n * Canonical error codes for voltom APIs.\n * These are the ONLY error codes that should be used across all products.\n * Maps to HTTP status codes via ERROR_STATUS_MAP.\n *\n * Do NOT add product-specific codes. Keep these universal.\n * If you need product logic, handle it in the backend with additional data.\n */\nexport type ErrorCode =\n | 'VALIDATION_ERROR' // 400 — invalid input fields (include field-level errors)\n | 'UNAUTHORIZED' // 401 — no session, invalid token, or API key\n | 'FORBIDDEN' // 403 — authenticated but not allowed (no permission)\n | 'NOT_FOUND' // 404 — resource does not exist\n | 'CONFLICT' // 409 — duplicate key, invalid state transition, or race condition\n | 'UNPROCESSABLE' // 422 — valid input but business logic rejected it\n | 'RATE_LIMITED' // 429 — too many requests (include retry-after)\n | 'INTERNAL_ERROR' // 500 — unexpected server error (log details server-side)\n\n/**\n * Map each ErrorCode to its HTTP status code.\n * Use this in backend exception filters and middleware.\n */\nexport const ERROR_STATUS_MAP: Record<ErrorCode, number> = {\n VALIDATION_ERROR: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n RATE_LIMITED: 429,\n UNPROCESSABLE: 422,\n INTERNAL_ERROR: 500,\n} as const\n\n/**\n * Default error messages for each code.\n * Use these as fallback or customize with domain-specific context.\n */\nexport const ERROR_MESSAGES: Record<ErrorCode, string> = {\n VALIDATION_ERROR: 'Invalid input. Check error details for specific field issues.',\n UNAUTHORIZED: 'Authentication required. Please log in or provide valid credentials.',\n FORBIDDEN: 'You do not have permission to access this resource.',\n NOT_FOUND: 'The requested resource was not found.',\n CONFLICT: 'This operation conflicts with existing data or system state.',\n RATE_LIMITED: 'Too many requests. Please wait before trying again.',\n UNPROCESSABLE: 'Your request is valid but cannot be processed at this time.',\n INTERNAL_ERROR: 'An unexpected error occurred. Our team has been notified.',\n} as const\n","import type { ContractConfig, Contract } from './types/contract.types'\n\n/**\n * Define a contract for a resource in your product.\n *\n * This function validates the contract shape and returns it for use throughout\n * your product (backend, frontend, codegen). The real power is in TypeScript's\n * type inference — once you call defineContract(), every property is typed\n * and all downstream consumers know exactly what to expect.\n *\n * @param config The contract configuration\n * @returns The same contract config (validated)\n *\n * @example\n * ```typescript\n * const usersContract = defineContract({\n * resource: 'users',\n * basePath: '/api/v1/users',\n * entity: {\n * id: t.uuid(),\n * email: t.email(),\n * name: t.string({ min: 1, max: 255 }),\n * },\n * endpoints: {\n * list: { method: 'GET', path: '/', response: 'paginated' },\n * show: { method: 'GET', path: '/:id', response: 'single' },\n * create: { method: 'POST', path: '/', body: 'CreateUser' },\n * },\n * bodies: {\n * CreateUser: {\n * email: t.email(),\n * name: t.string({ min: 1, max: 255 }),\n * },\n * },\n * })\n * ```\n */\nexport function defineContract<T extends ContractConfig>(config: T): T & Contract {\n // In the future, add validation logic here if needed.\n // For now, this function serves as a type guard and entry point.\n return config as T & Contract\n}\n","import { z } from 'zod'\nimport { t } from './primitives'\n\n/**\n * Standard fields that most entities should have.\n * Use these to ensure consistency across products.\n */\n\n/**\n * Audit trail fields (who, when)\n * Include on entities that track ownership or modification history.\n */\nexport const auditFields = {\n created_at: t.datetime(),\n created_by: t.uuid(),\n updated_at: t.datetime(),\n updated_by: t.uuid(),\n} as const\n\n/**\n * Soft delete fields (mark as deleted without removing data)\n * Use when you need to preserve data for compliance or recovery.\n */\nexport const softDeleteFields = {\n is_active: t.boolean(),\n deleted_at: t.nullable(t.datetime()),\n deleted_by: t.nullable(t.uuid()),\n} as const\n\n/**\n * Timestamp fields only (no user tracking)\n * Minimum audit trail for systems that don't track users.\n */\nexport const timestampFields = {\n created_at: t.datetime(),\n updated_at: t.datetime(),\n} as const\n\n/**\n * Standard metadata for multi-tenant systems\n * Use when entities belong to organizations or accounts.\n */\nexport const tenantFields = {\n tenant_id: t.uuid(),\n organization_id: t.uuid(),\n} as const\n\n/**\n * Status enum commonly used across products\n * Extend in product-specific contracts if needed.\n */\nexport const StandardStatus = {\n DRAFT: 'draft',\n ACTIVE: 'active',\n INACTIVE: 'inactive',\n ARCHIVED: 'archived',\n DELETED: 'deleted',\n} as const\n\nexport type StandardStatus = (typeof StandardStatus)[keyof typeof StandardStatus]\n\n/**\n * Sort direction for list queries\n */\nexport type SortDirection = 'asc' | 'desc'\n\n/**\n * Standard query filters for list endpoints\n * Use when your contract needs search/filter capabilities.\n */\nexport const listQueryFilters = {\n search: z.string().optional(),\n status: z.string().optional(),\n sort_by: z.string().optional(),\n sort_direction: z.enum(['asc', 'desc']).optional(),\n limit: z.number().int().min(1).max(100).optional().default(20),\n offset: z.number().int().min(0).optional().default(0),\n} as const\n\n/**\n * Standard pagination query parameters\n */\nexport const paginationQuery = {\n page: z.number().int().min(1).optional().default(1),\n per_page: z.number().int().min(1).max(100).optional().default(20),\n} as const\n\n/**\n * Create a consistent 'create' body type from an entity\n *\n * @example\n * ```typescript\n * const userEntity = { id: t.uuid(), email: t.email(), name: t.string() }\n * type CreateUserBody = OmitFields<typeof userEntity, 'id'> // email, name\n * ```\n *\n * Note: In your contract, you can also just define bodies explicitly:\n * bodies: { CreateUser: { email: t.email(), name: t.string() } }\n */\n\n/**\n * Create a consistent 'update' body type from an entity\n * (all fields optional except id)\n */\nexport const makeUpdateFields = <T extends Record<string, z.ZodTypeAny>>(\n entity: T,\n excludeFields: (keyof T)[] = ['id']\n): Record<string, z.ZodTypeAny> => {\n const result: Record<string, z.ZodTypeAny> = {}\n\n for (const [key, schema] of Object.entries(entity)) {\n if (!excludeFields.includes(key as keyof T)) {\n result[key] = schema.optional()\n }\n }\n\n return result\n}\n\n/**\n * Standard HTTP header hints for backend/frontend coordination\n * Not part of contract, but useful metadata for documentation\n */\nexport const ResponseHeaders = {\n /** Cache-Control header value */\n CACHE: {\n NO_CACHE: 'no-cache, no-store, must-revalidate',\n SHORT: 'public, max-age=300', // 5 minutes\n MEDIUM: 'public, max-age=3600', // 1 hour\n LONG: 'public, max-age=86400', // 24 hours\n NEVER: 'no-store',\n },\n} as const\n\n/**\n * Security hints for contract fields\n * Mark sensitive fields so frontend/backend handle them carefully\n */\nexport const FieldSensitivity = {\n PUBLIC: 'public',\n INTERNAL: 'internal',\n CONFIDENTIAL: 'confidential', // PII, payment, credentials\n SECRET: 'secret', // Never log, hash, or transmit unencrypted\n} as const\n\nexport type FieldSensitivity = (typeof FieldSensitivity)[keyof typeof FieldSensitivity]\n"],"mappings":"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","names":["z","z"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@voltom/contracts",
3
+ "version": "1.0.0",
4
+ "description": "Universal contract language for voltom products — primitive types, response shapes, error codes, defineContract() engine",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format cjs,esm --dts",
21
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
22
+ "type-check": "tsc --noEmit",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest watch",
25
+ "release": "changeset publish"
26
+ },
27
+ "keywords": [
28
+ "voltom",
29
+ "contracts",
30
+ "types",
31
+ "zod",
32
+ "validation"
33
+ ],
34
+ "author": "voltom-team@proton.me",
35
+ "license": "MIT",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "peerDependencies": {
40
+ "zod": "^3.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@changesets/cli": "^2.26.0",
44
+ "@types/node": "^20.0.0",
45
+ "typescript": "^5.3.0",
46
+ "tsup": "^8.0.0",
47
+ "vitest": "^1.0.0",
48
+ "zod": "^3.22.0"
49
+ }
50
+ }