@qbobjx/plugins
Advanced tools
+5
-0
@@ -8,2 +8,7 @@ import { type ObjxPlugin } from '@qbobjx/core'; | ||
| export declare function createTimestampsPlugin(options?: TimestampsPluginOptions): Readonly<ObjxPlugin>; | ||
| export interface SnakeCaseNamingPluginOptions { | ||
| readonly exclude?: readonly string[]; | ||
| readonly overrides?: Readonly<Record<string, string>>; | ||
| } | ||
| export declare function createSnakeCaseNamingPlugin(options?: SnakeCaseNamingPluginOptions): Readonly<ObjxPlugin>; | ||
| export declare const SOFT_DELETE_METADATA_KEY = "softDelete"; | ||
@@ -10,0 +15,0 @@ export interface SoftDeletePluginMetadata { |
+24
-0
@@ -18,2 +18,26 @@ import { definePlugin } from '@qbobjx/core'; | ||
| } | ||
| function toSnakeCase(value) { | ||
| return value | ||
| .replace(/([A-Z]+)([A-Z][a-z0-9])/g, '$1_$2') | ||
| .replace(/([a-z0-9])([A-Z])/g, '$1_$2') | ||
| .replace(/[-\s]+/g, '_') | ||
| .toLowerCase(); | ||
| } | ||
| export function createSnakeCaseNamingPlugin(options = {}) { | ||
| const excluded = new Set(options.exclude ?? []); | ||
| const overrides = options.overrides ?? {}; | ||
| return definePlugin({ | ||
| name: 'snake-case-naming', | ||
| hooks: { | ||
| onModelDefine(context) { | ||
| for (const columnKey of Object.keys(context.columnDefinitions)) { | ||
| if (excluded.has(columnKey)) { | ||
| continue; | ||
| } | ||
| context.setColumnDbName(columnKey, overrides[columnKey] ?? toSnakeCase(columnKey)); | ||
| } | ||
| }, | ||
| }, | ||
| }); | ||
| } | ||
| export const SOFT_DELETE_METADATA_KEY = 'softDelete'; | ||
@@ -20,0 +44,0 @@ export function createSoftDeletePlugin(options = {}) { |
+10
-2
| { | ||
| "name": "@qbobjx/plugins", | ||
| "version": "0.1.2", | ||
| "version": "0.2.0", | ||
| "private": false, | ||
@@ -12,2 +12,10 @@ "type": "module", | ||
| "types": "./dist/index.d.ts", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/QB-Objx/Objx" | ||
| }, | ||
| "homepage": "https://github.com/QB-Objx/Objx", | ||
| "bugs": { | ||
| "url": "https://github.com/QB-Objx/Objx/issues" | ||
| }, | ||
| "publishConfig": { | ||
@@ -23,4 +31,4 @@ "access": "public" | ||
| "dependencies": { | ||
| "@qbobjx/core": "0.1.2" | ||
| "@qbobjx/core": "0.2.0" | ||
| } | ||
| } |
+65
-3
| # @qbobjx/plugins | ||
| Official OBJX plugins, including timestamps, soft delete, tenant scope, and audit trail. | ||
| Official OBJX plugins, including timestamps, snake case naming, soft delete, tenant scope, and audit trail. | ||
@@ -14,5 +14,67 @@ ## Install | ||
| ```ts | ||
| import { createSoftDeletePlugin } from '@qbobjx/plugins'; | ||
| import { col, defineModel } from '@qbobjx/core'; | ||
| import { | ||
| createSnakeCaseNamingPlugin, | ||
| createSoftDeletePlugin, | ||
| createTenantScopePlugin, | ||
| } from '@qbobjx/plugins'; | ||
| const plugins = [createSoftDeletePlugin()]; | ||
| export const Account = defineModel({ | ||
| table: 'accounts', | ||
| columns: { | ||
| id: col.int().primary(), | ||
| tenantId: col.text(), | ||
| deletedAt: col.timestamp().nullable(), | ||
| }, | ||
| plugins: [ | ||
| createSnakeCaseNamingPlugin(), | ||
| createTenantScopePlugin(), | ||
| createSoftDeletePlugin(), | ||
| ], | ||
| }); | ||
| ``` | ||
| ## Included Plugins | ||
| - `createTimestampsPlugin()` | ||
| - `createSnakeCaseNamingPlugin()` | ||
| - `createSoftDeletePlugin()` | ||
| - `createTenantScopePlugin()` | ||
| - `createAuditTrailPlugin()` | ||
| ## Snake Case Naming | ||
| Use `createSnakeCaseNamingPlugin()` when your model keys are camelCase but your physical database columns are snake_case. | ||
| ```ts | ||
| import { col, defineModel } from '@qbobjx/core'; | ||
| import { createSnakeCaseNamingPlugin } from '@qbobjx/plugins'; | ||
| export const Account = defineModel({ | ||
| table: 'accounts', | ||
| columns: { | ||
| id: col.int().primary(), | ||
| tenantId: col.text(), | ||
| createdAt: col.timestamp(), | ||
| }, | ||
| plugins: [ | ||
| createSnakeCaseNamingPlugin({ | ||
| exclude: ['id'], | ||
| overrides: { | ||
| createdAt: 'created_on', | ||
| }, | ||
| }), | ||
| ], | ||
| }); | ||
| ``` | ||
| The compiler emits physical names like `tenant_id`, while hydrated rows continue to use model keys like `tenantId`. | ||
| Important: this plugin should be attached at model definition time. It updates column metadata in | ||
| the `onModelDefine` hook, so adding it only as a session-global plugin is too late for remapping. | ||
| Repository examples using this pattern: | ||
| - `examples/complex-runtime` | ||
| - `examples/express-api` | ||
| - `examples/nestjs-api` |
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
11884
31.2%239
13.81%1
-50%1
-50%80
344.44%0
-100%+ Added
- Removed
Updated