Skip to content

Commit 1cabdf9

Browse files
author
=
committed
feat: customId function
1 parent 5def947 commit 1cabdf9

3 files changed

Lines changed: 39 additions & 2 deletions

File tree

packages/language/res/stdlib.zmodel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ function nanoid(length: Int?, format: String?): String {
104104
function ulid(format: String?): String {
105105
} @@@expressionContext([DefaultValue])
106106

107+
/**
108+
* Generates a custom identifier. The ORM client must be initialized with an
109+
* implementation of this function.
110+
*/
111+
function customId(length: Int?): String {
112+
} @@@expressionContext([DefaultValue])
113+
107114
/**
108115
* Creates a sequence of integers in the underlying database and assign the incremented
109116
* values to the ID values of the created records based on the sequence.

packages/orm/src/client/crud/operations/base.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
10441044
}
10451045
if (!(field in data)) {
10461046
if (typeof fieldDef?.default === 'object' && 'kind' in fieldDef.default) {
1047-
const generated = this.evalGenerator(fieldDef.default);
1047+
const generated = this.evalGenerator(fieldDef.default, modelDef.name);
10481048
if (generated !== undefined) {
10491049
values[field] = this.dialect.transformInput(
10501050
generated,
@@ -1072,7 +1072,7 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
10721072
return values;
10731073
}
10741074

1075-
private evalGenerator(defaultValue: Expression) {
1075+
private evalGenerator(defaultValue: Expression, model: string) {
10761076
if (ExpressionUtils.isCall(defaultValue)) {
10771077
const firstArgVal =
10781078
defaultValue.args?.[0] && ExpressionUtils.isLiteral(defaultValue.args[0])
@@ -1095,6 +1095,16 @@ export abstract class BaseOperationHandler<Schema extends SchemaDef> {
10951095
return this.formatGeneratedValue(generated, defaultValue.args?.[1]);
10961096
})
10971097
.with('ulid', () => this.formatGeneratedValue(ulid(), defaultValue.args?.[0]))
1098+
.with('customId', () => {
1099+
invariant(this.client.$options.customId, '"customId" implementation not provided');
1100+
const length = typeof firstArgVal === 'number' ? firstArgVal : undefined;
1101+
const generated = this.client.$options.customId({
1102+
model,
1103+
length,
1104+
});
1105+
invariant(generated && typeof generated === 'string', '"customId" must return a non-empty string');
1106+
return generated;
1107+
})
10981108
.otherwise(() => undefined);
10991109
} else if (
11001110
ExpressionUtils.isMember(defaultValue) &&

packages/orm/src/client/options.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ export type ZModelFunction<Schema extends SchemaDef> = (
4040
context: ZModelFunctionContext<Schema>,
4141
) => Expression<unknown>;
4242

43+
export type CustomIdFunctionContext = {
44+
/**
45+
* The model for which the ID should be generated for.
46+
*/
47+
model: string;
48+
49+
/**
50+
* The requested length of the ID.
51+
*/
52+
length?: number;
53+
};
54+
55+
export type CustomIdFunction = (ctx: CustomIdFunctionContext) => string;
56+
4357
/**
4458
* ZenStack client options.
4559
*/
@@ -82,6 +96,12 @@ export type ClientOptions<Schema extends SchemaDef> = {
8296
*/
8397
validateInput?: boolean;
8498

99+
/**
100+
* Implementation of a custom ID generation function, which is called from ZModel as
101+
* `@default(customId())`.
102+
*/
103+
customId?: CustomIdFunction;
104+
85105
/**
86106
* Options for omitting fields in ORM query results.
87107
*/

0 commit comments

Comments
 (0)