|
| 1 | +// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. |
| 2 | + |
| 3 | +/** |
| 4 | + * System Object Names — Protocol Layer Constants |
| 5 | + * |
| 6 | + * These constants define the canonical, protocol-level names for system objects. |
| 7 | + * All API calls, SDK references, permissions checks, and metadata lookups MUST use |
| 8 | + * these names instead of hardcoded strings or physical table names. |
| 9 | + * |
| 10 | + * The actual storage table name may differ via `ObjectSchema.tableName`. |
| 11 | + * The mapping between protocol name and storage name is handled by the |
| 12 | + * ObjectQL Engine / Driver layer. |
| 13 | + * |
| 14 | + * @example |
| 15 | + * ```ts |
| 16 | + * import { SystemObjectName } from '@objectstack/spec/system'; |
| 17 | + * |
| 18 | + * // Always use the constant for API / SDK / permission references |
| 19 | + * const users = await engine.find(SystemObjectName.USER, { ... }); |
| 20 | + * ``` |
| 21 | + */ |
| 22 | +export const SystemObjectName = { |
| 23 | + /** Authentication: user identity */ |
| 24 | + USER: 'user', |
| 25 | + /** Authentication: active session */ |
| 26 | + SESSION: 'session', |
| 27 | + /** Authentication: OAuth / credential account */ |
| 28 | + ACCOUNT: 'account', |
| 29 | + /** Authentication: email / phone verification */ |
| 30 | + VERIFICATION: 'verification', |
| 31 | + /** System metadata storage */ |
| 32 | + METADATA: 'sys_metadata', |
| 33 | +} as const; |
| 34 | + |
| 35 | +/** Union type of all system object names */ |
| 36 | +export type SystemObjectName = typeof SystemObjectName[keyof typeof SystemObjectName]; |
| 37 | + |
| 38 | +/** |
| 39 | + * System Field Names — Protocol Layer Constants |
| 40 | + * |
| 41 | + * These constants define the canonical, protocol-level names for common system fields. |
| 42 | + * All API calls, SDK references, and permission checks MUST use these constants |
| 43 | + * instead of hardcoded strings or physical column names. |
| 44 | + * |
| 45 | + * The actual storage column name may differ via `FieldSchema.columnName`. |
| 46 | + * |
| 47 | + * @example |
| 48 | + * ```ts |
| 49 | + * import { SystemFieldName } from '@objectstack/spec/system'; |
| 50 | + * |
| 51 | + * // Use the constant to reference the owner field in queries |
| 52 | + * const myRecords = await engine.find('project', { |
| 53 | + * filters: [SystemFieldName.OWNER_ID, '=', currentUserId], |
| 54 | + * }); |
| 55 | + * ``` |
| 56 | + */ |
| 57 | +export const SystemFieldName = { |
| 58 | + /** Primary key */ |
| 59 | + ID: 'id', |
| 60 | + /** Record creation timestamp */ |
| 61 | + CREATED_AT: 'created_at', |
| 62 | + /** Record last-updated timestamp */ |
| 63 | + UPDATED_AT: 'updated_at', |
| 64 | + /** Record owner (lookup to user) */ |
| 65 | + OWNER_ID: 'owner_id', |
| 66 | + /** Tenant isolation key */ |
| 67 | + TENANT_ID: 'tenant_id', |
| 68 | + /** Foreign key to user on session / account objects */ |
| 69 | + USER_ID: 'user_id', |
| 70 | + /** Soft-delete timestamp */ |
| 71 | + DELETED_AT: 'deleted_at', |
| 72 | +} as const; |
| 73 | + |
| 74 | +/** Union type of all system field names */ |
| 75 | +export type SystemFieldName = typeof SystemFieldName[keyof typeof SystemFieldName]; |
| 76 | + |
| 77 | +/** |
| 78 | + * Storage Name Mapping — Protocol ↔ Physical Name Resolution |
| 79 | + * |
| 80 | + * Provides pure utility functions for resolving protocol-level names to |
| 81 | + * physical storage names and vice-versa. |
| 82 | + * |
| 83 | + * These helpers are intended for use inside the ObjectQL Engine and Driver layers. |
| 84 | + * They are intentionally stateless — they receive the object definition and return |
| 85 | + * the resolved name. |
| 86 | + */ |
| 87 | +export const StorageNameMapping = { |
| 88 | + /** |
| 89 | + * Resolve the physical table name for an object. |
| 90 | + * Falls back to `object.name` when `tableName` is not set. |
| 91 | + * |
| 92 | + * @param object - Object definition (at minimum `{ name: string; tableName?: string }`) |
| 93 | + * @returns The physical table / collection name to use in storage operations. |
| 94 | + */ |
| 95 | + resolveTableName(object: { name: string; tableName?: string }): string { |
| 96 | + return object.tableName ?? object.name; |
| 97 | + }, |
| 98 | + |
| 99 | + /** |
| 100 | + * Resolve the physical column name for a field. |
| 101 | + * Falls back to `fieldKey` when `columnName` is not set on the field. |
| 102 | + * |
| 103 | + * @param fieldKey - The protocol-level field key (snake_case identifier). |
| 104 | + * @param field - Field definition (at minimum `{ columnName?: string }`). |
| 105 | + * @returns The physical column name to use in storage operations. |
| 106 | + */ |
| 107 | + resolveColumnName(fieldKey: string, field: { columnName?: string }): string { |
| 108 | + return field.columnName ?? fieldKey; |
| 109 | + }, |
| 110 | + |
| 111 | + /** |
| 112 | + * Build a complete field-key → column-name map for an entire object. |
| 113 | + * |
| 114 | + * @param fields - The fields record from an ObjectSchema. |
| 115 | + * @returns A record mapping every protocol field key to its physical column name. |
| 116 | + */ |
| 117 | + buildColumnMap(fields: Record<string, { columnName?: string }>): Record<string, string> { |
| 118 | + const map: Record<string, string> = {}; |
| 119 | + for (const key of Object.keys(fields)) { |
| 120 | + map[key] = fields[key].columnName ?? key; |
| 121 | + } |
| 122 | + return map; |
| 123 | + }, |
| 124 | + |
| 125 | + /** |
| 126 | + * Build a reverse column-name → field-key map for an entire object. |
| 127 | + * Useful for translating storage-layer results back to protocol-level field keys. |
| 128 | + * |
| 129 | + * @param fields - The fields record from an ObjectSchema. |
| 130 | + * @returns A record mapping every physical column name back to its protocol field key. |
| 131 | + */ |
| 132 | + buildReverseColumnMap(fields: Record<string, { columnName?: string }>): Record<string, string> { |
| 133 | + const map: Record<string, string> = {}; |
| 134 | + for (const key of Object.keys(fields)) { |
| 135 | + const col = fields[key].columnName ?? key; |
| 136 | + map[col] = key; |
| 137 | + } |
| 138 | + return map; |
| 139 | + }, |
| 140 | +} as const; |
0 commit comments