diff --git a/app/__test__/data/data.test.ts b/app/__test__/data/data.test.ts index 886f2aa..79b4301 100644 --- a/app/__test__/data/data.test.ts +++ b/app/__test__/data/data.test.ts @@ -110,4 +110,18 @@ describe("some tests", async () => { new EntityManager([entity, entity2], connection); }).toThrow(); }); + + test("primary uuid", async () => { + const entity = new Entity("users", [ + new PrimaryField("id", { format: "uuid" }), + new TextField("username"), + ]); + const em = new EntityManager([entity], getDummyConnection().dummyConnection); + await em.schema().sync({ force: true }); + + const mutator = em.mutator(entity); + const data = await mutator.insertOne({ username: "test" }); + expect(data.data.id).toBeDefined(); + expect(data.data.id).toBeString(); + }); }); diff --git a/app/package.json b/app/package.json index 350340f..febba33 100644 --- a/app/package.json +++ b/app/package.json @@ -70,7 +70,8 @@ "oauth4webapi": "^2.11.1", "object-path-immutable": "^4.1.2", "radix-ui": "^1.1.3", - "swr": "^2.3.3" + "swr": "^2.3.3", + "uuid": "^11.1.0" }, "devDependencies": { "@aws-sdk/client-s3": "^3.758.0", @@ -99,7 +100,7 @@ "dotenv": "^16.4.7", "jotai": "^2.12.2", "jsdom": "^26.0.0", - "jsonv-ts": "^0.0.14-alpha.6", + "jsonv-ts": "^0.1.0", "kysely-d1": "^0.3.0", "open": "^10.1.0", "openapi-types": "^12.1.3", diff --git a/app/src/core/config.ts b/app/src/core/config.ts index 189b6c9..99a9013 100644 --- a/app/src/core/config.ts +++ b/app/src/core/config.ts @@ -3,9 +3,9 @@ */ import type { Generated } from "kysely"; -export type PrimaryFieldType = IdType | Generated; +export type PrimaryFieldType = IdType | Generated; -export interface AppEntity { +export interface AppEntity { id: PrimaryFieldType; } diff --git a/app/src/core/utils/uuid.ts b/app/src/core/utils/uuid.ts index 32397e8..e3112e2 100644 --- a/app/src/core/utils/uuid.ts +++ b/app/src/core/utils/uuid.ts @@ -1,4 +1,10 @@ +import { v4, v7 } from "uuid"; + // generates v4 export function uuid(): string { - return crypto.randomUUID(); + return v4(); +} + +export function uuidv7(): string { + return v7(); } diff --git a/app/src/data/api/DataController.ts b/app/src/data/api/DataController.ts index 8c3ab45..e0f6c03 100644 --- a/app/src/data/api/DataController.ts +++ b/app/src/data/api/DataController.ts @@ -233,6 +233,8 @@ export class DataController extends Controller { const hono = this.create(); const entitiesEnum = this.getEntitiesEnum(this.em); + // @todo: make dynamic based on entity + const idType = s.anyOf([s.number(), s.string()], { coerce: (v) => v as any }); /** * Function endpoints @@ -333,7 +335,7 @@ export class DataController extends Controller { "param", s.object({ entity: entitiesEnum, - id: s.string(), + id: idType, }), ), jsc("query", repoQuery, { skipOpenAPI: true }), @@ -342,8 +344,9 @@ export class DataController extends Controller { if (!this.entityExists(entity)) { return this.notFound(c); } + console.log("id", id); const options = c.req.valid("query") as RepoQuery; - const result = await this.em.repository(entity).findId(Number(id), options); + const result = await this.em.repository(entity).findId(id, options); return c.json(this.repoResult(result), { status: result.data ? 200 : 404 }); }, @@ -362,7 +365,7 @@ export class DataController extends Controller { "param", s.object({ entity: entitiesEnum, - id: s.string(), + id: idType, reference: s.string(), }), ), @@ -376,7 +379,7 @@ export class DataController extends Controller { const options = c.req.valid("query") as RepoQuery; const result = await this.em .repository(entity) - .findManyByReference(Number(id), reference, options); + .findManyByReference(id, reference, options); return c.json(this.repoResult(result), { status: result.data ? 200 : 404 }); }, @@ -485,7 +488,7 @@ export class DataController extends Controller { tags: ["data"], }), permission(DataPermissions.entityUpdate), - jsc("param", s.object({ entity: entitiesEnum, id: s.number() })), + jsc("param", s.object({ entity: entitiesEnum, id: idType })), jsc("json", s.object({})), async (c) => { const { entity, id } = c.req.valid("param"); @@ -493,7 +496,7 @@ export class DataController extends Controller { return this.notFound(c); } const body = (await c.req.json()) as EntityData; - const result = await this.em.mutator(entity).updateOne(Number(id), body); + const result = await this.em.mutator(entity).updateOne(id, body); return c.json(this.mutatorResult(result)); }, @@ -507,13 +510,13 @@ export class DataController extends Controller { tags: ["data"], }), permission(DataPermissions.entityDelete), - jsc("param", s.object({ entity: entitiesEnum, id: s.number() })), + jsc("param", s.object({ entity: entitiesEnum, id: idType })), async (c) => { const { entity, id } = c.req.valid("param"); if (!this.entityExists(entity)) { return this.notFound(c); } - const result = await this.em.mutator(entity).deleteOne(Number(id)); + const result = await this.em.mutator(entity).deleteOne(id); return c.json(this.mutatorResult(result)); }, diff --git a/app/src/data/connection/sqlite/SqliteConnection.ts b/app/src/data/connection/sqlite/SqliteConnection.ts index a63d49b..33a3fd8 100644 --- a/app/src/data/connection/sqlite/SqliteConnection.ts +++ b/app/src/data/connection/sqlite/SqliteConnection.ts @@ -31,7 +31,11 @@ export class SqliteConnection extends Connection { type, (col: ColumnDefinitionBuilder) => { if (spec.primary) { - return col.primaryKey().notNull().autoIncrement(); + if (spec.type === "integer") { + return col.primaryKey().notNull().autoIncrement(); + } + + return col.primaryKey().notNull(); } if (spec.references) { let relCol = col.references(spec.references); diff --git a/app/src/data/data-schema.ts b/app/src/data/data-schema.ts index 38f272f..c594117 100644 --- a/app/src/data/data-schema.ts +++ b/app/src/data/data-schema.ts @@ -1,4 +1,4 @@ -import { type Static, StringRecord, objectTransform } from "core/utils"; +import { type Static, StringEnum, StringRecord, objectTransform } from "core/utils"; import * as tb from "@sinclair/typebox"; import { FieldClassMap, @@ -8,6 +8,7 @@ import { entityTypes, } from "data"; import { MediaField, mediaFieldConfigSchema } from "../media/MediaField"; +import { primaryFieldTypes } from "./fields"; export const FIELDS = { ...FieldClassMap, @@ -72,6 +73,9 @@ export const indicesSchema = tb.Type.Object( export const dataConfigSchema = tb.Type.Object( { basepath: tb.Type.Optional(tb.Type.String({ default: "/api/data" })), + default_primary_format: tb.Type.Optional( + StringEnum(primaryFieldTypes, { default: "integer" }), + ), entities: tb.Type.Optional(StringRecord(entitiesSchema, { default: {} })), relations: tb.Type.Optional(StringRecord(tb.Type.Union(relationsSchema), { default: {} })), indices: tb.Type.Optional(StringRecord(indicesSchema, { default: {} })), diff --git a/app/src/data/entities/Entity.ts b/app/src/data/entities/Entity.ts index b097557..cdcbce6 100644 --- a/app/src/data/entities/Entity.ts +++ b/app/src/data/entities/Entity.ts @@ -6,7 +6,13 @@ import { snakeToPascalWithSpaces, transformObject, } from "core/utils"; -import { type Field, PrimaryField, type TActionContext, type TRenderContext } from "../fields"; +import { + type Field, + PrimaryField, + primaryFieldTypes, + type TActionContext, + type TRenderContext, +} from "../fields"; import * as tbbox from "@sinclair/typebox"; const { Type } = tbbox; @@ -18,6 +24,7 @@ export const entityConfigSchema = Type.Object( description: Type.Optional(Type.String()), sort_field: Type.Optional(Type.String({ default: config.data.default_primary_field })), sort_dir: Type.Optional(StringEnum(["asc", "desc"], { default: "asc" })), + primary_format: Type.Optional(StringEnum(primaryFieldTypes)), }, { additionalProperties: false, @@ -68,7 +75,14 @@ export class Entity< if (primary_count > 1) { throw new Error(`Entity "${name}" has more than one primary field`); } - this.fields = primary_count === 1 ? [] : [new PrimaryField()]; + this.fields = + primary_count === 1 + ? [] + : [ + new PrimaryField(undefined, { + format: this.config.primary_format, + }), + ]; if (fields) { fields.forEach((field) => this.addField(field)); diff --git a/app/src/data/entities/Mutator.ts b/app/src/data/entities/Mutator.ts index 104ca33..7dcb2dd 100644 --- a/app/src/data/entities/Mutator.ts +++ b/app/src/data/entities/Mutator.ts @@ -143,7 +143,7 @@ export class Mutator< // if listener returned, take what's returned const _data = result.returned ? result.params.data : data; - const validatedData = { + let validatedData = { ...entity.getDefaultObject(), ...(await this.getValidatedData(_data, "create")), }; @@ -159,6 +159,16 @@ export class Mutator< } } + // primary + const primary = entity.getPrimaryField(); + const primary_value = primary.getNewValue(); + if (primary_value) { + validatedData = { + [primary.name]: primary_value, + ...validatedData, + }; + } + const query = this.conn .insertInto(entity.name) .values(validatedData) @@ -175,7 +185,7 @@ export class Mutator< async updateOne(id: PrimaryFieldType, data: Partial): Promise> { const entity = this.entity; - if (!Number.isInteger(id)) { + if (!id) { throw new Error("ID must be provided for update"); } @@ -212,7 +222,7 @@ export class Mutator< async deleteOne(id: PrimaryFieldType): Promise> { const entity = this.entity; - if (!Number.isInteger(id)) { + if (!id) { throw new Error("ID must be provided for deletion"); } diff --git a/app/src/data/fields/PrimaryField.ts b/app/src/data/fields/PrimaryField.ts index 2cc2983..efcfd6f 100644 --- a/app/src/data/fields/PrimaryField.ts +++ b/app/src/data/fields/PrimaryField.ts @@ -1,13 +1,17 @@ import { config } from "core"; -import type { Static } from "core/utils"; +import { StringEnum, uuidv7, type Static } from "core/utils"; import { Field, baseFieldConfigSchema } from "./Field"; import * as tbbox from "@sinclair/typebox"; import type { TFieldTSType } from "data/entities/EntityTypescript"; const { Type } = tbbox; +export const primaryFieldTypes = ["integer", "uuid"] as const; +export type TPrimaryFieldFormat = (typeof primaryFieldTypes)[number]; + export const primaryFieldConfigSchema = Type.Composite([ Type.Omit(baseFieldConfigSchema, ["required"]), Type.Object({ + format: Type.Optional(StringEnum(primaryFieldTypes, { default: "integer" })), required: Type.Optional(Type.Literal(false)), }), ]); @@ -21,8 +25,8 @@ export class PrimaryField extends Field< > { override readonly type = "primary"; - constructor(name: string = config.data.default_primary_field) { - super(name, { fillable: false, required: false }); + constructor(name: string = config.data.default_primary_field, cfg?: PrimaryFieldConfig) { + super(name, { fillable: false, required: false, ...cfg }); } override isRequired(): boolean { @@ -30,18 +34,34 @@ export class PrimaryField extends Field< } protected getSchema() { - return baseFieldConfigSchema; + return primaryFieldConfigSchema; + } + + get format() { + return this.config.format ?? "integer"; + } + + get fieldType() { + return this.format === "integer" ? "integer" : "text"; } override schema() { return Object.freeze({ - type: "integer", + type: this.fieldType, name: this.name, primary: true, nullable: false, }); } + getNewValue(): any { + if (this.format === "uuid") { + return uuidv7(); + } + + return undefined; + } + override async transformPersist(value: any): Promise { throw new Error("PrimaryField: This function should not be called"); } @@ -51,11 +71,12 @@ export class PrimaryField extends Field< } override toType(): TFieldTSType { + const type = this.format === "integer" ? "number" : "string"; return { ...super.toType(), required: true, import: [{ package: "kysely", name: "Generated" }], - type: "Generated", + type: `Generated<${type}>`, }; } } diff --git a/app/src/data/relations/EntityRelation.ts b/app/src/data/relations/EntityRelation.ts index bf0d8e6..35cef59 100644 --- a/app/src/data/relations/EntityRelation.ts +++ b/app/src/data/relations/EntityRelation.ts @@ -9,6 +9,7 @@ import { import type { RepoQuery } from "../server/query"; import type { RelationType } from "./relation-types"; import * as tbbox from "@sinclair/typebox"; +import type { PrimaryFieldType } from "core"; const { Type } = tbbox; const directions = ["source", "target"] as const; @@ -72,7 +73,7 @@ export abstract class EntityRelation< reference: string, ): KyselyQueryBuilder; - getReferenceQuery(entity: Entity, id: number, reference: string): Partial { + getReferenceQuery(entity: Entity, id: PrimaryFieldType, reference: string): Partial { return {}; } diff --git a/app/src/data/relations/RelationField.ts b/app/src/data/relations/RelationField.ts index 0623987..67be187 100644 --- a/app/src/data/relations/RelationField.ts +++ b/app/src/data/relations/RelationField.ts @@ -1,6 +1,6 @@ import { type Static, StringEnum } from "core/utils"; import type { EntityManager } from "../entities"; -import { Field, baseFieldConfigSchema } from "../fields"; +import { Field, baseFieldConfigSchema, primaryFieldTypes } from "../fields"; import type { EntityRelation } from "./EntityRelation"; import type { EntityRelationAnchor } from "./EntityRelationAnchor"; import * as tbbox from "@sinclair/typebox"; @@ -15,6 +15,7 @@ export const relationFieldConfigSchema = Type.Composite([ reference: Type.String(), target: Type.String(), // @todo: potentially has to be an instance! target_field: Type.Optional(Type.String({ default: "id" })), + target_field_type: Type.Optional(StringEnum(["integer", "text"], { default: "integer" })), on_delete: Type.Optional(StringEnum(CASCADES, { default: "set null" })), }), ]); @@ -45,6 +46,7 @@ export class RelationField extends Field { reference: target.reference, target: target.entity.name, target_field: target.entity.getPrimaryField().name, + target_field_type: target.entity.getPrimaryField().fieldType, }); } @@ -63,7 +65,7 @@ export class RelationField extends Field { override schema() { return Object.freeze({ ...super.schema()!, - type: "integer", + type: this.config.target_field_type ?? "integer", references: `${this.config.target}.${this.config.target_field}`, onDelete: this.config.on_delete ?? "set null", }); diff --git a/app/src/data/schema/SchemaManager.ts b/app/src/data/schema/SchemaManager.ts index 7ad1ba1..42c071d 100644 --- a/app/src/data/schema/SchemaManager.ts +++ b/app/src/data/schema/SchemaManager.ts @@ -2,6 +2,7 @@ import type { CompiledQuery, TableMetadata } from "kysely"; import type { IndexMetadata, SchemaResponse } from "../connection/Connection"; import type { Entity, EntityManager } from "../entities"; import { PrimaryField } from "../fields"; +import { $console } from "core"; type IntrospectedTable = TableMetadata & { indices: IndexMetadata[]; @@ -332,6 +333,7 @@ export class SchemaManager { if (config.force) { try { + $console.info("[SchemaManager]", sql, parameters); await qb.execute(); } catch (e) { throw new Error(`Failed to execute query: ${sql}: ${(e as any).message}`); diff --git a/app/src/ui/elements/media/DropzoneContainer.tsx b/app/src/ui/elements/media/DropzoneContainer.tsx index d31f641..1ecd4f2 100644 --- a/app/src/ui/elements/media/DropzoneContainer.tsx +++ b/app/src/ui/elements/media/DropzoneContainer.tsx @@ -1,9 +1,10 @@ import type { Api } from "bknd/client"; +import type { PrimaryFieldType } from "core"; import type { RepoQueryIn } from "data"; import type { MediaFieldSchema } from "media/AppMedia"; import type { TAppMediaConfig } from "media/media-schema"; import { useId, useEffect, useRef, useState } from "react"; -import { useApi, useApiInfiniteQuery, useApiQuery, useInvalidate } from "ui/client"; +import { useApi, useApiInfiniteQuery, useApiQuery, useInvalidate } from "bknd/client"; import { useEvent } from "ui/hooks/use-event"; import { Dropzone, type DropzoneProps } from "./Dropzone"; import { mediaItemsToFileStates } from "./helper"; @@ -14,7 +15,7 @@ export type DropzoneContainerProps = { infinite?: boolean; entity?: { name: string; - id: number; + id: PrimaryFieldType; field: string; }; media?: Pick; diff --git a/app/src/ui/modules/data/components/EntityForm.tsx b/app/src/ui/modules/data/components/EntityForm.tsx index 2c7c6a3..50c9feb 100644 --- a/app/src/ui/modules/data/components/EntityForm.tsx +++ b/app/src/ui/modules/data/components/EntityForm.tsx @@ -22,6 +22,7 @@ import { EntityRelationalFormField } from "./fields/EntityRelationalFormField"; import ErrorBoundary from "ui/components/display/ErrorBoundary"; import { Alert } from "ui/components/display/Alert"; import { bkndModals } from "ui/modals"; +import type { PrimaryFieldType } from "core"; // simplify react form types 🤦 export type FormApi = ReactFormExtendedApi; @@ -30,7 +31,7 @@ export type TFieldApi = FieldApi) => void; fieldsDisabled: boolean; @@ -225,7 +226,7 @@ function EntityMediaFormField({ formApi: FormApi; field: MediaField; entity: Entity; - entityId?: number; + entityId?: PrimaryFieldType; disabled?: boolean; }) { if (!entityId) return; diff --git a/app/src/ui/modules/data/components/schema/create-modal/step.entity.fields.tsx b/app/src/ui/modules/data/components/schema/create-modal/step.entity.fields.tsx index a9a90e0..141c9ec 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/step.entity.fields.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/step.entity.fields.tsx @@ -11,12 +11,14 @@ import { type EntityFieldsFormRef, } from "ui/routes/data/forms/entity.fields.form"; import { ModalBody, ModalFooter, type TCreateModalSchema, useStepContext } from "./CreateModal"; +import { useBkndData } from "ui/client/schema/data/use-bknd-data"; const schema = entitiesSchema; type Schema = Static; export function StepEntityFields() { const { nextStep, stepBack, state, setState } = useStepContext(); + const { config } = useBkndData(); const entity = state.entities?.create?.[0]!; const defaultFields = { id: { type: "primary", name: "id" } } as const; const ref = useRef(null); @@ -82,6 +84,8 @@ export function StepEntityFields() { ref={ref} fields={initial.fields as any} onChange={updateListener} + defaultPrimaryFormat={config?.default_primary_format} + isNew={true} /> diff --git a/app/src/ui/modules/data/components/schema/create-modal/step.entity.tsx b/app/src/ui/modules/data/components/schema/create-modal/step.entity.tsx index 7824fb1..253fc4f 100644 --- a/app/src/ui/modules/data/components/schema/create-modal/step.entity.tsx +++ b/app/src/ui/modules/data/components/schema/create-modal/step.entity.tsx @@ -10,12 +10,13 @@ import { entitySchema, useStepContext, } from "./CreateModal"; +import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect"; export function StepEntity() { const focusTrapRef = useFocusTrap(); const { nextStep, stepBack, state, setState } = useStepContext(); - const { register, handleSubmit, formState, watch } = useForm({ + const { register, handleSubmit, formState, watch, control } = useForm({ mode: "onTouched", resolver: typeboxResolver(entitySchema), defaultValues: state.entities?.create?.[0] ?? {}, @@ -56,7 +57,6 @@ export function StepEntity() { label="What's the name of the entity?" description="Use plural form, and all lowercase. It will be used as the database table." /> - {/**/} ; } - const entityId = Number.parseInt(params.id as string); + const entityId = params.id as PrimaryFieldType; const [error, setError] = useState(null); const [navigate] = useNavigate(); useBrowserTitle(["Data", entity.label, `#${entityId}`]); @@ -202,7 +203,7 @@ function EntityDetailRelations({ entity, relations, }: { - id: number; + id: PrimaryFieldType; entity: Entity; relations: EntityRelation[]; }) { @@ -250,7 +251,7 @@ function EntityDetailInner({ entity, relation, }: { - id: number; + id: PrimaryFieldType; entity: Entity; relation: EntityRelation; }) { diff --git a/app/src/ui/routes/data/data.schema.$entity.tsx b/app/src/ui/routes/data/data.schema.$entity.tsx index 0831284..3125a34 100644 --- a/app/src/ui/routes/data/data.schema.$entity.tsx +++ b/app/src/ui/routes/data/data.schema.$entity.tsx @@ -148,7 +148,7 @@ export function DataSchemaEntity({ params }) { const Fields = ({ entity }: { entity: Entity }) => { const [submitting, setSubmitting] = useState(false); const [updates, setUpdates] = useState(0); - const { actions, $data } = useBkndData(); + const { actions, $data, config } = useBkndData(); const [res, setRes] = useState(); const ref = useRef(null); async function handleUpdate() { @@ -201,6 +201,8 @@ const Fields = ({ entity }: { entity: Entity }) => { } }, }))} + defaultPrimaryFormat={config?.default_primary_format} + isNew={false} /> {isDebug() && ( diff --git a/app/src/ui/routes/data/forms/entity.fields.form.tsx b/app/src/ui/routes/data/forms/entity.fields.form.tsx index a0ca16c..b0186a6 100644 --- a/app/src/ui/routes/data/forms/entity.fields.form.tsx +++ b/app/src/ui/routes/data/forms/entity.fields.form.tsx @@ -28,6 +28,8 @@ import { type TFieldSpec, fieldSpecs } from "ui/modules/data/components/fields-s import { dataFieldsUiSchema } from "../../settings/routes/data.settings"; import * as tbbox from "@sinclair/typebox"; import { useRoutePathState } from "ui/hooks/use-route-path-state"; +import { MantineSelect } from "ui/components/form/hook-form-mantine/MantineSelect"; +import type { TPrimaryFieldFormat } from "data/fields/PrimaryField"; const { Type } = tbbox; const fieldsSchemaObject = originalFieldsSchemaObject; @@ -65,6 +67,8 @@ export type EntityFieldsFormProps = { sortable?: boolean; additionalFieldTypes?: (TFieldSpec & { onClick: () => void })[]; routePattern?: string; + defaultPrimaryFormat?: TPrimaryFieldFormat; + isNew?: boolean; }; export type EntityFieldsFormRef = { @@ -77,7 +81,7 @@ export type EntityFieldsFormRef = { export const EntityFieldsForm = forwardRef( function EntityFieldsForm( - { fields: _fields, sortable, additionalFieldTypes, routePattern, ...props }, + { fields: _fields, sortable, additionalFieldTypes, routePattern, isNew, ...props }, ref, ) { const entityFields = Object.entries(_fields).map(([name, field]) => ({ @@ -172,6 +176,10 @@ export const EntityFieldsForm = forwardRef )} /> @@ -186,6 +194,10 @@ export const EntityFieldsForm = forwardRef ))} @@ -281,6 +293,7 @@ function EntityField({ errors, dnd, routePattern, + primary, }: { field: FieldArrayWithId; index: number; @@ -292,6 +305,10 @@ function EntityField({ errors: any; dnd?: SortableItemProps; routePattern?: string; + primary?: { + defaultFormat?: TPrimaryFieldFormat; + editable?: boolean; + }; }) { const prefix = `fields.${index}.field` as const; const type = field.field.type; @@ -363,15 +380,29 @@ function EntityField({ )}
- Required {is_primary ? ( - + <> + + ) : ( - + <> + Required + + )}
diff --git a/app/tsconfig.json b/app/tsconfig.json index 8b845ec..c4e2739 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -30,7 +30,14 @@ "baseUrl": ".", "outDir": "./dist/types", "paths": { - "*": ["./src/*"] + "*": ["./src/*"], + "bknd": ["./src/index.ts"], + "bknd/core": ["./src/core/index.ts"], + "bknd/adapter": ["./src/adapter/index.ts"], + "bknd/client": ["./src/ui/client/index.ts"], + "bknd/data": ["./src/data/index.ts"], + "bknd/media": ["./src/media/index.ts"], + "bknd/auth": ["./src/auth/index.ts"] } }, "include": [ diff --git a/bun.lock b/bun.lock index 9744812..e0e754d 100644 --- a/bun.lock +++ b/bun.lock @@ -27,7 +27,7 @@ }, "app": { "name": "bknd", - "version": "0.12.0", + "version": "0.13.0", "bin": "./dist/cli/index.js", "dependencies": { "@cfworker/json-schema": "^4.1.1", @@ -56,6 +56,7 @@ "object-path-immutable": "^4.1.2", "radix-ui": "^1.1.3", "swr": "^2.3.3", + "uuid": "^11.1.0", }, "devDependencies": { "@aws-sdk/client-s3": "^3.758.0", @@ -84,7 +85,7 @@ "dotenv": "^16.4.7", "jotai": "^2.12.2", "jsdom": "^26.0.0", - "jsonv-ts": "^0.0.14-alpha.6", + "jsonv-ts": "^0.1.0", "kysely-d1": "^0.3.0", "open": "^10.1.0", "openapi-types": "^12.1.3", @@ -2521,7 +2522,7 @@ "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], - "jsonv-ts": ["jsonv-ts@0.0.14-alpha.6", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-pwMpjEbNtyq8Xi6QBXuQ8dOZm7WQAEwvCPu3vVf9b3aU2KRHW+cfTPqO53U01YYdjWSSRkqaTKcLSiYdfwBYRA=="], + "jsonv-ts": ["jsonv-ts@0.1.0", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-wJ+79o49MNie2Xk9w1hPN8ozjqemVWXOfWUTdioLui/SeGDC7C+QKXTDxsmUaIay86lorkjb3CCGo6JDKbyTZQ=="], "jsonwebtoken": ["jsonwebtoken@9.0.2", "", { "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ=="], @@ -3603,7 +3604,7 @@ "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], - "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], "v8-compile-cache": ["v8-compile-cache@2.4.0", "", {}, "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw=="], @@ -3853,6 +3854,8 @@ "@bundled-es-modules/tough-cookie/tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="], + "@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + "@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "@inquirer/core/cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="],