diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..2999acac8
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,8 @@
+{
+ "json.schemas": [
+ {
+ "fileMatch": ["packages/template/src/templates/*.base.json"],
+ "url": "http://localhost:3721/api/template/base/schema.json"
+ }
+ ]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ee7ce5f5..65bcea585 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## v1.0.0-84
+
+
+### 🏡 Chore
+
+- Fix ts issue ([68c5291](https://github.com/undb-io/undb/commit/68c5291))
+
+### ❤️ Contributors
+
+- Nichenqin ([@nichenqin](http://github.com/nichenqin))
+
## v1.0.0-83
diff --git a/apps/backend/package.json b/apps/backend/package.json
index c0507500a..9a9f176e3 100644
--- a/apps/backend/package.json
+++ b/apps/backend/package.json
@@ -36,6 +36,7 @@
"@undb/realtime": "workspace:*",
"@undb/share": "workspace:*",
"@undb/space": "workspace:*",
+ "@undb/template": "workspace:*",
"@undb/trpc": "workspace:*",
"@undb/webhook": "workspace:*",
"arctic": "^1.9.2",
diff --git a/apps/backend/src/app.ts b/apps/backend/src/app.ts
index 09317b95e..546fa5dc5 100644
--- a/apps/backend/src/app.ts
+++ b/apps/backend/src/app.ts
@@ -26,12 +26,14 @@ import * as pkg from "../../../package.json"
import { Auth, OpenAPI, Realtime, SpaceModule, TableModule, Web } from "./modules"
import { FileService } from "./modules/file/file"
import { OpenTelemetryModule } from "./modules/opentelemetry/opentelemetry.module"
+import { TemplateModule } from "./modules/template/template.module"
import { loggerPlugin } from "./plugins/logging"
const auth = container.resolve(Auth)
const web = container.resolve(Web)
const openapi = container.resolve(OpenAPI)
const opentelemetry = container.resolve(OpenTelemetryModule)
+const template = container.resolve(TemplateModule)
export const app = new Elysia()
.onStart(async () => {
@@ -132,6 +134,7 @@ export const app = new Elysia()
.use(auth.route())
.use(web.route())
.use(openapi.route())
+ .use(template.route())
.use(trpc(route))
.guard(
{
diff --git a/apps/backend/src/modules/template/template.module.ts b/apps/backend/src/modules/template/template.module.ts
new file mode 100644
index 000000000..f16390717
--- /dev/null
+++ b/apps/backend/src/modules/template/template.module.ts
@@ -0,0 +1,12 @@
+import { singleton } from "@undb/di"
+import { baseTemplateSchema } from "@undb/template"
+import Elysia from "elysia"
+
+@singleton()
+export class TemplateModule {
+ route() {
+ return new Elysia().get("/api/template/base/schema.json", () => {
+ return baseTemplateSchema
+ })
+ }
+}
diff --git a/apps/frontend/src/lib/components/blocks/nav/nav-tools.svelte b/apps/frontend/src/lib/components/blocks/nav/nav-tools.svelte
index ec025fbdf..9cd2cbc24 100644
--- a/apps/frontend/src/lib/components/blocks/nav/nav-tools.svelte
+++ b/apps/frontend/src/lib/components/blocks/nav/nav-tools.svelte
@@ -8,9 +8,24 @@
import SpaceDropdown from "../space/space-dropdown.svelte"
import type { ISpaceDTO } from "@undb/space"
import { preferences } from "$lib/store/persisted.store"
+ import { createMutation } from "@tanstack/svelte-query"
+ import { trpc } from "$lib/trpc/client"
+ import { toast } from "svelte-sonner"
+ import { goto, invalidateAll } from "$app/navigation"
export let space: ISpaceDTO | undefined | null
export let me: any
+
+ const createFromTemplateMutation = createMutation({
+ mutationFn: trpc.template.createFromTemplate.mutate,
+ onSuccess: async (data) => {
+ await invalidateAll()
+ toast.success("Base created successfully")
+ if (data.baseIds.length > 0) {
+ goto(`/bases/${data.baseIds[0]}`)
+ }
+ },
+ })
@@ -67,5 +82,15 @@
Create New Base
+
+
{/if}
diff --git a/apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.gql b/apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.gql
similarity index 75%
rename from apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.gql
rename to apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.gql
index d562fc602..fc5e8196e 100644
--- a/apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.gql
+++ b/apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.gql
@@ -1,4 +1,4 @@
-query GetCreateFromTemplateData($shareId: ID!) {
+query GetCreateFromShareData($shareId: ID!) {
space {
id
name
diff --git a/apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.svelte b/apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.svelte
similarity index 100%
rename from apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.svelte
rename to apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.svelte
diff --git a/apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.ts b/apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.ts
similarity index 71%
rename from apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.ts
rename to apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.ts
index 0c07ee103..102e215b6 100644
--- a/apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+layout.ts
+++ b/apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+layout.ts
@@ -1,4 +1,4 @@
-import { GetCreateFromTemplateDataStore } from "$houdini"
+import { GetCreateFromShareDataStore } from "$houdini"
import type { LayoutLoad } from "./$types"
export const ssr = false
@@ -7,7 +7,7 @@ export const prerender = "auto"
export const load: LayoutLoad = async (event) => {
const { shareId } = event.params
- const store = new GetCreateFromTemplateDataStore()
+ const store = new GetCreateFromShareDataStore()
await store.fetch({
event,
diff --git a/apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+page.svelte b/apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+page.svelte
similarity index 93%
rename from apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+page.svelte
rename to apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+page.svelte
index 6a8b0ed27..ac81bfe87 100644
--- a/apps/frontend/src/routes/(authed)/(template)/create-from-template/[shareId]/+page.svelte
+++ b/apps/frontend/src/routes/(authed)/(template)/create-from-share/[shareId]/+page.svelte
@@ -9,7 +9,7 @@
import * as Form from "$lib/components/ui/form"
import * as Alert from "$lib/components/ui/alert/index.js"
import { LoaderCircleIcon, SirenIcon, Store } from "lucide-svelte"
- import { createFromTemplateCommand } from "@undb/commands"
+ import { createFromShareCommand } from "@undb/commands"
import { Checkbox } from "$lib/components/ui/checkbox"
import { trpc } from "$lib/trpc/client"
import { page } from "$app/stores"
@@ -39,8 +39,8 @@
})
}
- const createFromTemplateMutation = createMutation({
- mutationFn: trpc.base.createFromTemplate.mutate,
+ const createFromShareMutation = createMutation({
+ mutationFn: trpc.base.createFromShare.mutate,
onError(error, variables, context) {
toast.error(error.message)
},
@@ -60,12 +60,12 @@
name: template?.name,
includeData: true,
},
- zodClient(createFromTemplateCommand),
+ zodClient(createFromShareCommand),
),
{
SPA: true,
dataType: "json",
- validators: zodClient(createFromTemplateCommand),
+ validators: zodClient(createFromShareCommand),
resetForm: false,
invalidateAll: false,
onSubmit(input) {
@@ -77,7 +77,7 @@
return
}
- await $createFromTemplateMutation.mutateAsync(event.form.data)
+ await $createFromShareMutation.mutateAsync(event.form.data)
},
},
)
@@ -176,8 +176,8 @@
System fields will be updated to the current user and timestamp.
-
- {#if $createFromTemplateMutation.isPending}
+
+ {#if $createFromShareMutation.isPending}
{/if}
Create
diff --git a/bun.lockb b/bun.lockb
index 889d213c6..c0e652b0e 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 1f64171f0..49f8107f7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "undb",
- "version": "1.0.0-83",
+ "version": "1.0.0-84",
"private": true,
"scripts": {
"build": "NODE_ENV=production bun --bun turbo build",
diff --git a/packages/base/src/dto/index.ts b/packages/base/src/dto/index.ts
index 185736b43..ff94ea2ba 100644
--- a/packages/base/src/dto/index.ts
+++ b/packages/base/src/dto/index.ts
@@ -2,4 +2,5 @@ export * from "./base.dto"
export * from "./create-base.dto"
export * from "./delete-base.dto"
export * from "./duplicate-base.dto"
+export * from "./unique-base.dto"
export * from "./update-base.dto"
diff --git a/packages/base/src/dto/unique-base.dto.ts b/packages/base/src/dto/unique-base.dto.ts
new file mode 100644
index 000000000..87d482ce9
--- /dev/null
+++ b/packages/base/src/dto/unique-base.dto.ts
@@ -0,0 +1,13 @@
+import { spaceIdSchema } from "@undb/space"
+import { z } from "@undb/zod"
+import { baseIdSchema, baseNameSchema } from "../value-objects"
+
+export const uniqueBaseDTO = z
+ .object({
+ baseId: baseIdSchema,
+ baseName: baseNameSchema,
+ spaceId: spaceIdSchema,
+ })
+ .partial()
+
+export type IUniqueBaseDTO = z.infer
diff --git a/packages/base/src/specifications/index.ts b/packages/base/src/specifications/index.ts
index d883b1ee4..fee4e50c1 100644
--- a/packages/base/src/specifications/index.ts
+++ b/packages/base/src/specifications/index.ts
@@ -2,3 +2,23 @@ export * from "./base-id.specification.js"
export * from "./base-name.specification.js"
export * from "./base-q.specification.js"
export * from "./base-space-id.specification.js"
+
+import { CompositeSpecification, Err, Ok, Result } from "@undb/domain"
+import type { Base } from "../base.js"
+import type { IUniqueBaseDTO } from "../dto/unique-base.dto.js"
+import type { IBaseSpecVisitor } from "../interface.js"
+import { BaseId } from "../value-objects/base-id.vo.js"
+import { WithBaseId } from "./base-id.specification.js"
+import { WithBaseName } from "./base-name.specification.js"
+
+type BaseComositeSpecification = CompositeSpecification
+
+export const withUniqueBase = (dto: IUniqueBaseDTO): Result => {
+ if (dto.baseId) {
+ return Ok(new WithBaseId(new BaseId(dto.baseId)))
+ }
+ if (dto.baseName && dto.spaceId) {
+ return Ok(WithBaseName.fromString(dto.baseName))
+ }
+ return Err("Invalid base specification")
+}
diff --git a/packages/base/src/value-objects/base-name.vo.ts b/packages/base/src/value-objects/base-name.vo.ts
index e135c98eb..b81117aa6 100644
--- a/packages/base/src/value-objects/base-name.vo.ts
+++ b/packages/base/src/value-objects/base-name.vo.ts
@@ -1,7 +1,7 @@
import { ValueObject } from "@undb/domain"
import * as z from "@undb/zod"
-export const baseNameSchema = z.string().min(1)
+export const baseNameSchema = z.string().min(1, { message: "Base name must be at least 1 character" })
export class BaseName extends ValueObject> {
static from(name: string): BaseName {
diff --git a/packages/command-handlers/package.json b/packages/command-handlers/package.json
index bd9085882..3aae4ceed 100644
--- a/packages/command-handlers/package.json
+++ b/packages/command-handlers/package.json
@@ -16,6 +16,7 @@
"@undb/cqrs": "workspace:*",
"@undb/di": "workspace:*",
"@undb/logger": "workspace:*",
+ "@undb/template": "workspace:*",
"@undb/openapi": "workspace:*",
"@undb/user": "workspace:*",
"ts-pattern": "^5.3.1"
diff --git a/packages/command-handlers/src/handlers/create-from-share.command-handler.ts b/packages/command-handlers/src/handlers/create-from-share.command-handler.ts
new file mode 100644
index 000000000..272e0cc3b
--- /dev/null
+++ b/packages/command-handlers/src/handlers/create-from-share.command-handler.ts
@@ -0,0 +1,58 @@
+import { checkPermission, injectSpaceMemberService, type ISpaceMemberService } from "@undb/authz"
+import { BaseId, injectBaseRepository, WithBaseId, WithBaseSpaceId, type IBaseRepository } from "@undb/base"
+import { CreateFromShareCommand } from "@undb/commands"
+import { getCurrentUserId, mustGetCurrentSpaceId } from "@undb/context/server"
+import { commandHandler } from "@undb/cqrs"
+import { singleton } from "@undb/di"
+import { type ICommandHandler } from "@undb/domain"
+import { createLogger } from "@undb/logger"
+import { injectShareRepository, WithShareId, type IShareRepository } from "@undb/share"
+import { injectTableService, type ITableService } from "@undb/table"
+
+@commandHandler(CreateFromShareCommand)
+@singleton()
+export class CreateFromShareCommandHandler implements ICommandHandler {
+ private readonly logger = createLogger(CreateFromShareCommandHandler.name)
+
+ constructor(
+ @injectBaseRepository()
+ private readonly baseRepository: IBaseRepository,
+ @injectTableService()
+ private readonly tableService: ITableService,
+ @injectSpaceMemberService()
+ private readonly spaceMemberService: ISpaceMemberService,
+ @injectShareRepository()
+ private readonly shareRepository: IShareRepository,
+ ) {}
+
+ async execute(command: CreateFromShareCommand): Promise {
+ this.logger.debug("CreateFromShareCommandHandler execute command", command)
+ const share = (await this.shareRepository.findOne(WithShareId.fromString(command.shareId))).expect(
+ "Share not found",
+ )
+
+ if (share.target.type !== "base") {
+ throw new Error("Share target is not base")
+ }
+
+ const baseId = share.target.id
+ const spaceId = share.spaceId
+
+ const userId = getCurrentUserId()
+ const targetSpaceId = command.targetSpaceId ?? mustGetCurrentSpaceId()
+
+ const member = (await this.spaceMemberService.getSpaceMember(userId, targetSpaceId)).expect("Member not found")
+ checkPermission(member.props.role, ["base:create"])
+
+ const spec = new WithBaseId(new BaseId(baseId)).and(new WithBaseSpaceId(spaceId))
+ const base = (await this.baseRepository.findOne(spec)).expect("Base not found")
+
+ const duplicatedBase = await this.tableService.duplicateBase(base, spaceId, targetSpaceId, {
+ id: baseId,
+ name: command.name,
+ includeData: command.includeData,
+ })
+
+ return duplicatedBase.id.value
+ }
+}
diff --git a/packages/command-handlers/src/handlers/create-from-template.command-handler.ts b/packages/command-handlers/src/handlers/create-from-template.command-handler.ts
index 376ffb96b..9f93951f6 100644
--- a/packages/command-handlers/src/handlers/create-from-template.command-handler.ts
+++ b/packages/command-handlers/src/handlers/create-from-template.command-handler.ts
@@ -1,58 +1,31 @@
-import { checkPermission, injectSpaceMemberService, type ISpaceMemberService } from "@undb/authz"
-import { BaseId, injectBaseRepository, WithBaseId, WithBaseSpaceId, type IBaseRepository } from "@undb/base"
-import { CreateFromTemplateCommand } from "@undb/commands"
-import { getCurrentUserId, mustGetCurrentSpaceId } from "@undb/context/server"
+import { CreateFromTemplateCommand, type ICreateFromTemplateCommandOutput } from "@undb/commands"
+import { mustGetCurrentSpaceId } from "@undb/context/server"
import { commandHandler } from "@undb/cqrs"
import { singleton } from "@undb/di"
import { type ICommandHandler } from "@undb/domain"
import { createLogger } from "@undb/logger"
-import { injectShareRepository, WithShareId, type IShareRepository } from "@undb/share"
-import { injectTableService, type ITableService } from "@undb/table"
+import { injectTemplateService, type ITemplateService, templates } from "@undb/template"
@commandHandler(CreateFromTemplateCommand)
@singleton()
-export class CreateFromTemplateCommandHandler implements ICommandHandler {
+export class CreateFromTemplateCommandHandler
+ implements ICommandHandler
+{
private readonly logger = createLogger(CreateFromTemplateCommandHandler.name)
constructor(
- @injectBaseRepository()
- private readonly baseRepository: IBaseRepository,
- @injectTableService()
- private readonly tableService: ITableService,
- @injectSpaceMemberService()
- private readonly spaceMemberService: ISpaceMemberService,
- @injectShareRepository()
- private readonly shareRepository: IShareRepository,
+ @injectTemplateService()
+ private readonly templateService: ITemplateService,
) {}
- async execute(command: CreateFromTemplateCommand): Promise {
- this.logger.debug("CreateFromTemplateCommandHandler execute command", command)
- const share = (await this.shareRepository.findOne(WithShareId.fromString(command.shareId))).expect(
- "Share not found",
- )
+ async execute(command: CreateFromTemplateCommand): Promise {
+ this.logger.info(`create from template command received: ${command.templateName}`)
- if (share.target.type !== "base") {
- throw new Error("Share target is not base")
- }
+ const template = templates["test"]
- const baseId = share.target.id
- const spaceId = share.spaceId
+ const spaceId = mustGetCurrentSpaceId()
+ const result = await this.templateService.createBase(template, spaceId)
- const userId = getCurrentUserId()
- const targetSpaceId = command.targetSpaceId ?? mustGetCurrentSpaceId()
-
- const member = (await this.spaceMemberService.getSpaceMember(userId, targetSpaceId)).expect("Member not found")
- checkPermission(member.props.role, ["base:create"])
-
- const spec = new WithBaseId(new BaseId(baseId)).and(new WithBaseSpaceId(spaceId))
- const base = (await this.baseRepository.findOne(spec)).expect("Base not found")
-
- const duplicatedBase = await this.tableService.duplicateBase(base, spaceId, targetSpaceId, {
- id: baseId,
- name: command.name,
- includeData: command.includeData,
- })
-
- return duplicatedBase.id.value
+ return { baseIds: result.map(({ base }) => base.id.value) }
}
}
diff --git a/packages/command-handlers/src/handlers/index.ts b/packages/command-handlers/src/handlers/index.ts
index 103e70821..0be613430 100644
--- a/packages/command-handlers/src/handlers/index.ts
+++ b/packages/command-handlers/src/handlers/index.ts
@@ -4,6 +4,7 @@ import { BulkDuplicateRecordsCommandHandler } from "./bulk-duplicate-records.com
import { BulkUpdateRecordsCommandHandler } from "./bulk-update-records.command-handler"
import { CreateApiTokenCommandHandler } from "./create-api-token.command-handler"
import { CreateBaseCommandHandler } from "./create-base.command-handler"
+import { CreateFromShareCommandHandler } from "./create-from-share.command-handler"
import { CreateFromTemplateCommandHandler } from "./create-from-template.command-handler"
import { CreateRecordCommandHandler } from "./create-record.command-handler"
import { CreateRecordsCommandHandler } from "./create-records.command-handler"
@@ -79,7 +80,7 @@ export const commandHandlers = [
BulkUpdateRecordsCommandHandler,
CreateTableViewCommandHandler,
DuplicateViewCommandHandler,
- CreateFromTemplateCommandHandler,
+ CreateFromShareCommandHandler,
DeleteViewCommandHandler,
CreateBaseCommandHandler,
UpdateBaseCommandHandler,
@@ -106,4 +107,5 @@ export const commandHandlers = [
SubmitFormCommandHandler,
SetFieldWidthCommandHandler,
DuplicateTableFormCommandHandler,
+ CreateFromTemplateCommandHandler,
]
diff --git a/packages/commands/package.json b/packages/commands/package.json
index 421c37f60..d993de07b 100644
--- a/packages/commands/package.json
+++ b/packages/commands/package.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"@undb/base": "workspace:*",
+ "@undb/template": "workspace:*",
"@undb/domain": "workspace:*",
"@undb/openapi": "workspace:*",
"@undb/share": "workspace:*",
diff --git a/packages/commands/src/create-base.command.ts b/packages/commands/src/create-base.command.ts
index 4421cc8b7..5dc77541c 100644
--- a/packages/commands/src/create-base.command.ts
+++ b/packages/commands/src/create-base.command.ts
@@ -1,8 +1,17 @@
import { createBaseDTO } from "@undb/base"
import { Command, type CommandProps } from "@undb/domain"
import { z } from "@undb/zod"
+import { createTableCommand } from "./create-table.command"
-export const createBaseCommand = createBaseDTO.omit({ id: true })
+export const createBaseCommand = createBaseDTO
+ .omit({
+ id: true,
+ })
+ .merge(
+ z.object({
+ tables: createTableCommand.array().optional(),
+ }),
+ )
export type ICreateBaseCommand = z.infer
diff --git a/packages/commands/src/create-from-share.command.ts b/packages/commands/src/create-from-share.command.ts
new file mode 100644
index 000000000..51291da75
--- /dev/null
+++ b/packages/commands/src/create-from-share.command.ts
@@ -0,0 +1,28 @@
+import { Command, type CommandProps } from "@undb/domain"
+import { shareIdSchema } from "@undb/share"
+import { spaceIdSchema } from "@undb/space"
+import { z } from "@undb/zod"
+
+export const createFromShareCommand = z.object({
+ shareId: shareIdSchema,
+ targetSpaceId: spaceIdSchema.optional(),
+ name: z.string().optional(),
+ includeData: z.boolean().optional(),
+})
+
+export type ICreateFromShareCommand = z.infer
+
+export class CreateFromShareCommand extends Command implements ICreateFromShareCommand {
+ public readonly shareId: string
+ public readonly targetSpaceId?: string
+ public readonly name?: string
+ public readonly includeData?: boolean
+
+ constructor(props: CommandProps) {
+ super(props)
+ this.shareId = props.shareId
+ this.targetSpaceId = props.targetSpaceId
+ this.name = props.name
+ this.includeData = props.includeData
+ }
+}
diff --git a/packages/commands/src/create-from-template.command.ts b/packages/commands/src/create-from-template.command.ts
index b67a9d9c3..316aa444d 100644
--- a/packages/commands/src/create-from-template.command.ts
+++ b/packages/commands/src/create-from-template.command.ts
@@ -1,28 +1,24 @@
+import { baseIdSchema } from "@undb/base"
import { Command, type CommandProps } from "@undb/domain"
-import { shareIdSchema } from "@undb/share"
-import { spaceIdSchema } from "@undb/space"
import { z } from "@undb/zod"
export const createFromTemplateCommand = z.object({
- shareId: shareIdSchema,
- targetSpaceId: spaceIdSchema.optional(),
- name: z.string().optional(),
- includeData: z.boolean().optional(),
+ templateName: z.string(),
})
export type ICreateFromTemplateCommand = z.infer
+export const createFromTemplateCommandOutput = z.object({
+ baseIds: z.array(baseIdSchema),
+})
+
+export type ICreateFromTemplateCommandOutput = z.infer
+
export class CreateFromTemplateCommand extends Command implements ICreateFromTemplateCommand {
- public readonly shareId: string
- public readonly targetSpaceId?: string
- public readonly name?: string
- public readonly includeData?: boolean
+ public readonly templateName: string
constructor(props: CommandProps) {
super(props)
- this.shareId = props.shareId
- this.targetSpaceId = props.targetSpaceId
- this.name = props.name
- this.includeData = props.includeData
+ this.templateName = props.templateName
}
}
diff --git a/packages/commands/src/create-table.command.ts b/packages/commands/src/create-table.command.ts
index 2bc7520b9..5287c13c5 100644
--- a/packages/commands/src/create-table.command.ts
+++ b/packages/commands/src/create-table.command.ts
@@ -1,16 +1,20 @@
+import { baseIdSchema, baseNameSchema } from "@undb/base"
import { Command, type CommandProps } from "@undb/domain"
import type { ICreateSchemaDTO } from "@undb/table"
import { createTableDTO } from "@undb/table"
import { z } from "@undb/zod"
-export const createTableCommand = createTableDTO.omit({ spaceId: true })
+export const createTableCommand = createTableDTO
+ .omit({ spaceId: true })
+ .merge(z.object({ baseId: baseIdSchema.optional(), baseName: baseNameSchema.optional() }))
export type ICreateTableCommand = z.infer
export class CreateTableCommand extends Command implements ICreateTableCommand {
public readonly id?: string
public readonly name: string
- public readonly baseId: string
+ public readonly baseId?: string
+ public readonly baseName?: string
public readonly schema: ICreateSchemaDTO
constructor(props: CommandProps) {
@@ -18,6 +22,7 @@ export class CreateTableCommand extends Command implements ICreateTableCommand {
this.id = props.id
this.name = props.name
this.baseId = props.baseId
+ this.baseName = props.baseName
this.schema = props.schema
}
}
diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts
index 18f1e17db..adfca09be 100644
--- a/packages/commands/src/index.ts
+++ b/packages/commands/src/index.ts
@@ -4,6 +4,7 @@ export * from "./bulk-duplicate-records.command"
export * from "./bulk-update-records.command"
export * from "./create-api-token.command"
export * from "./create-base.command"
+export * from "./create-from-share.command"
export * from "./create-from-template.command"
export * from "./create-record.command"
export * from "./create-records.command"
diff --git a/packages/persistence/src/table/table.repository.ts b/packages/persistence/src/table/table.repository.ts
index 9f2eaa58e..8dc23e877 100644
--- a/packages/persistence/src/table/table.repository.ts
+++ b/packages/persistence/src/table/table.repository.ts
@@ -106,6 +106,12 @@ export class TableRepository implements ITableRepository {
await this.outboxService.save(table)
}
+ async insertMany(tables: TableDo[]): Promise {
+ for (const table of tables) {
+ await this.insert(table)
+ }
+ }
+
async bulkUpdate(updates: { table: TableDo; spec: Option }[]): Promise {
for (const update of updates) {
await this.#updateOneById(update.table, update.spec)
diff --git a/packages/table/src/modules/schema/fields/condition/condition.type.ts b/packages/table/src/modules/schema/fields/condition/condition.type.ts
index 209d15ce0..962b59d9a 100644
--- a/packages/table/src/modules/schema/fields/condition/condition.type.ts
+++ b/packages/table/src/modules/schema/fields/condition/condition.type.ts
@@ -29,12 +29,16 @@ export type MaybeConditionGroupChildren = Maybe
export function createConditionGroup(
optionType: OptionType,
fieldType: FieldOptionType,
+ level = 1,
): z.ZodType> {
+ const nested = level < 3 ? z.lazy(() => createConditionGroup(optionType, fieldType, level + 1)) : undefined
+ const child = nested
+ ? z.union([...createConditionSchema(fieldType).options, nested])
+ : z.union([...createConditionSchema(fieldType).options])
+
return z.object({
conjunction: z.enum(["and", "or"]),
- children: z.array(
- z.union([...createConditionSchema(fieldType).options, z.lazy(() => createConditionGroup(optionType, fieldType))]),
- ),
+ children: z.array(child),
disabled: z.boolean().optional(),
option: optionType,
}) as z.ZodType>
diff --git a/packages/table/src/modules/schema/fields/dto/create-field.dto.ts b/packages/table/src/modules/schema/fields/dto/create-field.dto.ts
index f876f1c61..48d319fd2 100644
--- a/packages/table/src/modules/schema/fields/dto/create-field.dto.ts
+++ b/packages/table/src/modules/schema/fields/dto/create-field.dto.ts
@@ -1,13 +1,13 @@
import { z } from "@undb/zod"
-import { createAttachmentFieldDTO } from "../variants/attachment-field"
+import { createAttachmentFieldDTO } from "../variants/attachment-field/attachment-field.vo"
import { createButtonFieldDTO } from "../variants/button-field/button-field.vo"
-import { createCheckboxFieldDTO } from "../variants/checkbox-field"
-import { createCurrencyFieldDTO } from "../variants/currency-field"
+import { createCheckboxFieldDTO } from "../variants/checkbox-field/checkbox-field.vo"
+import { createCurrencyFieldDTO } from "../variants/currency-field/currency-field.vo"
import { createDateFieldDTO } from "../variants/date-field/date-field.vo"
import { createDurationFieldDTO } from "../variants/duration-field/duration-field.vo"
-import { createEmailFieldDTO } from "../variants/email-field"
+import { createEmailFieldDTO } from "../variants/email-field/email-field.vo"
import { createJsonFieldDTO } from "../variants/json-field/json-field.vo"
-import { createLongTextFieldDTO } from "../variants/long-text-field"
+import { createLongTextFieldDTO } from "../variants/long-text-field/long-text-field.vo"
import { createNumberFieldDTO } from "../variants/number-field/number-field.vo"
import { createPercentageFieldDTO } from "../variants/percentage-field/percentage-field.vo"
import { createRatingFieldDTO } from "../variants/rating-field/rating-field.vo"
@@ -16,7 +16,7 @@ import { createRollupFieldDTO } from "../variants/rollup-field/rollup-field.vo"
import { createSelectFieldDTO } from "../variants/select-field/select-field.vo"
import { createStringFieldDTO } from "../variants/string-field/string-field.vo"
import { createUrlFieldDTO } from "../variants/url-field/url-field.vo"
-import { createUserFieldDTO } from "../variants/user-field"
+import { createUserFieldDTO } from "../variants/user-field/user-field.vo"
export const createFieldDTO = z.discriminatedUnion("type", [
createStringFieldDTO,
@@ -39,4 +39,25 @@ export const createFieldDTO = z.discriminatedUnion("type", [
createPercentageFieldDTO,
])
+export const createFieldWithoutNameDTO = z.discriminatedUnion("type", [
+ createStringFieldDTO.omit({ name: true }),
+ createNumberFieldDTO.omit({ name: true }),
+ createReferenceFieldDTO.omit({ name: true }),
+ createRollupFieldDTO.omit({ name: true }),
+ createSelectFieldDTO.omit({ name: true }),
+ createRatingFieldDTO.omit({ name: true }),
+ createEmailFieldDTO.omit({ name: true }),
+ createUrlFieldDTO.omit({ name: true }),
+ createAttachmentFieldDTO.omit({ name: true }),
+ createDateFieldDTO.omit({ name: true }),
+ createJsonFieldDTO.omit({ name: true }),
+ createCheckboxFieldDTO.omit({ name: true }),
+ createUserFieldDTO.omit({ name: true }),
+ createLongTextFieldDTO.omit({ name: true }),
+ createCurrencyFieldDTO.omit({ name: true }),
+ createButtonFieldDTO.omit({ name: true }),
+ createDurationFieldDTO.omit({ name: true }),
+ createPercentageFieldDTO.omit({ name: true }),
+])
+
export type ICreateFieldDTO = z.infer
diff --git a/packages/table/src/modules/schema/fields/variants/reference-field/reference-field.vo.ts b/packages/table/src/modules/schema/fields/variants/reference-field/reference-field.vo.ts
index 0eac8748d..3b6788f64 100644
--- a/packages/table/src/modules/schema/fields/variants/reference-field/reference-field.vo.ts
+++ b/packages/table/src/modules/schema/fields/variants/reference-field/reference-field.vo.ts
@@ -21,7 +21,7 @@ import { createReferenceFieldCondition, type IReferenceFieldConditionSchema } fr
export const REFERENCE_TYPE = "reference" as const
-const referenceFieldOption = z.object({
+export const referenceFieldOption = z.object({
isOwner: z.boolean(),
foreignTableId: tableId,
symmetricFieldId: fieldId.optional(),
diff --git a/packages/table/src/modules/schema/fields/variants/rollup-field/rollup-field.vo.ts b/packages/table/src/modules/schema/fields/variants/rollup-field/rollup-field.vo.ts
index 927334c3a..c98ee7034 100644
--- a/packages/table/src/modules/schema/fields/variants/rollup-field/rollup-field.vo.ts
+++ b/packages/table/src/modules/schema/fields/variants/rollup-field/rollup-field.vo.ts
@@ -17,7 +17,7 @@ export const rollupFn = z.enum(["sum", "average", "count", "min", "max", "lookup
export type IRollupFn = z.infer
-const rollupFieldOption = z.object({
+export const rollupFieldOption = z.object({
referenceFieldId: fieldId,
rollupFieldId: fieldId,
fn: rollupFn,
diff --git a/packages/table/src/services/methods/create-table.method.ts b/packages/table/src/services/methods/create-table.method.ts
index 86b5b31f1..f9967986c 100644
--- a/packages/table/src/services/methods/create-table.method.ts
+++ b/packages/table/src/services/methods/create-table.method.ts
@@ -1,3 +1,4 @@
+import { withUniqueBase } from "@undb/base"
import { applyRules, Some } from "@undb/domain"
import type { ICreateTableDTO } from "../../dto"
import { TableNameShouldBeUnique } from "../../rules/table-name-should-be-unique.rule"
@@ -6,8 +7,11 @@ import type { TableDo } from "../../table.do"
import type { TableService } from "../table.service"
export async function createTableMethod(this: TableService, dto: ICreateTableDTO): Promise {
- const spec = new TableBaseIdSpecification(dto.baseId)
- const baseTables = await this.repository.find(Some(spec))
+ const spec = withUniqueBase(dto).ok()
+ const base = (await this.baseRepository.findOne(spec.unwrap())).expect("base not found")
+
+ const baseIdSpec = new TableBaseIdSpecification(base.id.value)
+ const baseTables = await this.repository.find(Some(baseIdSpec))
const names = baseTables.map((table) => table.name.value).concat(dto.name)
applyRules(new TableNameShouldBeUnique(names))
diff --git a/packages/table/src/table.repository.ts b/packages/table/src/table.repository.ts
index a81ea405c..60230e4e0 100644
--- a/packages/table/src/table.repository.ts
+++ b/packages/table/src/table.repository.ts
@@ -6,8 +6,10 @@ import type { TableDo } from "./table.do"
export interface ITableRepository {
insert(table: TableDo): Promise
+ insertMany(tables: TableDo[]): Promise
updateOneById(table: TableDo, spec: Option): Promise
bulkUpdate(updates: { table: TableDo; spec: Option }[]): Promise
+
deleteOneById(table: TableDo): Promise
find(spec: Option, ignoreSpace?: boolean): Promise
diff --git a/packages/template/.gitignore b/packages/template/.gitignore
new file mode 100644
index 000000000..9b1ee42e8
--- /dev/null
+++ b/packages/template/.gitignore
@@ -0,0 +1,175 @@
+# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
+
+# Logs
+
+logs
+_.log
+npm-debug.log_
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Caches
+
+.cache
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# Runtime data
+
+pids
+_.pid
+_.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+
+lib-cov
+
+# Coverage directory used by tools like istanbul
+
+coverage
+*.lcov
+
+# nyc test coverage
+
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+
+bower_components
+
+# node-waf configuration
+
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+
+build/Release
+
+# Dependency directories
+
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+
+web_modules/
+
+# TypeScript cache
+
+*.tsbuildinfo
+
+# Optional npm cache directory
+
+.npm
+
+# Optional eslint cache
+
+.eslintcache
+
+# Optional stylelint cache
+
+.stylelintcache
+
+# Microbundle cache
+
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+
+.node_repl_history
+
+# Output of 'npm pack'
+
+*.tgz
+
+# Yarn Integrity file
+
+.yarn-integrity
+
+# dotenv environment variable files
+
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+
+.parcel-cache
+
+# Next.js build output
+
+.next
+out
+
+# Nuxt.js build / generate output
+
+.nuxt
+dist
+
+# Gatsby files
+
+# Comment in the public line in if your project uses Gatsby and not Next.js
+
+# https://nextjs.org/blog/next-9-1#public-directory-support
+
+# public
+
+# vuepress build output
+
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+
+.temp
+
+# Docusaurus cache and generated files
+
+.docusaurus
+
+# Serverless directories
+
+.serverless/
+
+# FuseBox cache
+
+.fusebox/
+
+# DynamoDB Local files
+
+.dynamodb/
+
+# TernJS port file
+
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+
+.vscode-test
+
+# yarn v2
+
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/packages/template/README.md b/packages/template/README.md
new file mode 100644
index 000000000..5ce3e9add
--- /dev/null
+++ b/packages/template/README.md
@@ -0,0 +1,15 @@
+# @undb/template
+
+To install dependencies:
+
+```bash
+bun install
+```
+
+To run:
+
+```bash
+bun run src/index.ts
+```
+
+This project was created using `bun init` in bun v1.1.29. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
diff --git a/packages/template/package.json b/packages/template/package.json
new file mode 100644
index 000000000..ead1da7f6
--- /dev/null
+++ b/packages/template/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "@undb/template",
+ "module": "src/index.ts",
+ "types": "src/index.d.ts",
+ "type": "module",
+ "dependencies": {
+ "@undb/base": "workspace:*",
+ "@undb/table": "workspace:*",
+ "@undb/zod": "workspace:*",
+ "@undb/di": "workspace:*",
+ "@undb/logger": "workspace:*",
+ "zod-to-json-schema": "^3.23.3"
+ },
+ "devDependencies": {
+ "@types/bun": "latest"
+ },
+ "peerDependencies": {
+ "typescript": "^5.0.0"
+ }
+}
diff --git a/packages/template/src/dto/index.ts b/packages/template/src/dto/index.ts
new file mode 100644
index 000000000..8d39f46f6
--- /dev/null
+++ b/packages/template/src/dto/index.ts
@@ -0,0 +1 @@
+export * from "./template.dto"
diff --git a/packages/template/src/dto/template.dto.ts b/packages/template/src/dto/template.dto.ts
new file mode 100644
index 000000000..d979de096
--- /dev/null
+++ b/packages/template/src/dto/template.dto.ts
@@ -0,0 +1,26 @@
+import { baseNameSchema } from "@undb/base"
+import { createFieldWithoutNameDTO, fieldId, tableName } from "@undb/table"
+import { z } from "@undb/zod"
+
+const templateSchemaDTO = z.record(fieldId, createFieldWithoutNameDTO)
+
+const basicTemplateTableDTO = z.object({
+ schema: templateSchemaDTO,
+})
+
+export const baseTemplateDTO = z.record(
+ baseNameSchema,
+ z.object({
+ tables: z.record(tableName, basicTemplateTableDTO),
+ }),
+)
+
+export type IBaseTemplateDTO = z.infer
+
+export const tableTemplateDTO = z
+ .object({
+ name: tableName,
+ })
+ .merge(basicTemplateTableDTO)
+
+export type ITableTemplateDTO = z.infer
diff --git a/packages/template/src/index.ts b/packages/template/src/index.ts
new file mode 100644
index 000000000..fa809484b
--- /dev/null
+++ b/packages/template/src/index.ts
@@ -0,0 +1,4 @@
+export * from "./dto"
+export * from "./schema"
+export * from "./service"
+export * from "./templates"
diff --git a/packages/template/src/schema/index.ts b/packages/template/src/schema/index.ts
new file mode 100644
index 000000000..485ab3c44
--- /dev/null
+++ b/packages/template/src/schema/index.ts
@@ -0,0 +1 @@
+export * from "./template.schema"
diff --git a/packages/template/src/schema/template.schema.ts b/packages/template/src/schema/template.schema.ts
new file mode 100644
index 000000000..78b4ba4f4
--- /dev/null
+++ b/packages/template/src/schema/template.schema.ts
@@ -0,0 +1,68 @@
+import { baseNameSchema } from "@undb/base"
+import {
+ attachmentFieldConstraint,
+ buttonFieldOption,
+ checkboxFieldConstraint,
+ currencyFieldConstraint,
+ currencyFieldOption,
+ dateFieldConstraint,
+ durationFieldConstraint,
+ emailFieldConstraint,
+ fieldId,
+ fieldName,
+ jsonFieldConstraint,
+ longTextFieldConstraint,
+ numberFieldConstraint,
+ percentageFieldConstraint,
+ ratingFieldConstraint,
+ referenceFieldConstraint,
+ referenceFieldOption,
+ rollupFieldOption,
+ selectFieldConstraint,
+ selectFieldOption,
+ stringFieldConstraint,
+ tableId,
+ tableName,
+ urlFieldConstraint,
+ userFieldConstraint,
+ viewFilterGroup,
+} from "@undb/table"
+import zodToJsonSchema from "zod-to-json-schema"
+import { baseTemplateDTO } from "../dto/template.dto"
+
+export const baseTemplateSchema = zodToJsonSchema(baseTemplateDTO, {
+ errorMessages: true,
+ definitions: {
+ tableId,
+ tableName,
+ baseName: baseNameSchema,
+ fieldId,
+ fieldName,
+ viewFilterGroup,
+
+ stringFieldConstraint,
+ numberFieldConstraint,
+ checkboxFieldConstraint,
+ attachmentFieldConstraint,
+ buttonFieldOption,
+
+ referenceFieldConstraint,
+ referenceFieldOption,
+ rollupFieldOption,
+
+ selectFieldOption,
+ selectFieldConstraint,
+
+ ratingFieldConstraint,
+ emailFieldConstraint,
+ urlFieldConstraint,
+ dateFieldConstraint,
+ jsonFieldConstraint,
+ userFieldConstraint,
+ longTextFieldConstraint,
+ currencyFieldConstraint,
+ currencyFieldOption,
+ durationFieldConstraint,
+ percentageFieldConstraint,
+ },
+})
diff --git a/packages/template/src/service/index.ts b/packages/template/src/service/index.ts
new file mode 100644
index 000000000..169ce6f84
--- /dev/null
+++ b/packages/template/src/service/index.ts
@@ -0,0 +1,2 @@
+export * from "./template.service"
+export * from "./template.service.provider"
diff --git a/packages/template/src/service/template.service.provider.ts b/packages/template/src/service/template.service.provider.ts
new file mode 100644
index 000000000..2586c7df4
--- /dev/null
+++ b/packages/template/src/service/template.service.provider.ts
@@ -0,0 +1,6 @@
+import { container, inject } from "@undb/di"
+import { TemplateService } from "./template.service"
+
+export const TEMPLATE_SERVICE = Symbol.for("TemplateService")
+export const injectTemplateService = () => inject(TEMPLATE_SERVICE)
+container.register(TEMPLATE_SERVICE, { useClass: TemplateService })
diff --git a/packages/template/src/service/template.service.ts b/packages/template/src/service/template.service.ts
new file mode 100644
index 000000000..1d98bea5f
--- /dev/null
+++ b/packages/template/src/service/template.service.ts
@@ -0,0 +1,36 @@
+import { Base, injectBaseRepository, WithBaseSpaceId, type IBaseRepository } from "@undb/base"
+import { singleton } from "@undb/di"
+import { createLogger } from "@undb/logger"
+import { injectTableRepository, TableDo, type ITableRepository } from "@undb/table"
+import type { IBaseTemplateDTO } from "../dto"
+import { TemplateFactory } from "../template.factory"
+
+export interface ITemplateService {
+ createBase(dto: IBaseTemplateDTO, spaceId: string): Promise<{ base: Base; tables: TableDo[] }[]>
+}
+
+@singleton()
+export class TemplateService implements ITemplateService {
+ private readonly logger = createLogger(TemplateService.name)
+
+ constructor(
+ @injectBaseRepository()
+ private readonly baseRepository: IBaseRepository,
+ @injectTableRepository()
+ private readonly tableRepository: ITableRepository,
+ ) {}
+
+ async createBase(dto: IBaseTemplateDTO, spaceId: string): Promise<{ base: Base; tables: TableDo[] }[]> {
+ this.logger.info(dto)
+ const bases = await this.baseRepository.find(new WithBaseSpaceId(spaceId))
+ const baseNames = bases.map((base) => base.name.value)
+ const result = TemplateFactory.create(dto, baseNames, spaceId)
+
+ for (const { base, tables } of result) {
+ await this.baseRepository.insert(base)
+ await this.tableRepository.insertMany(tables)
+ }
+
+ return result
+ }
+}
diff --git a/packages/template/src/template.factory.ts b/packages/template/src/template.factory.ts
new file mode 100644
index 000000000..c8a93001b
--- /dev/null
+++ b/packages/template/src/template.factory.ts
@@ -0,0 +1,27 @@
+import { Base, BaseFactory } from "@undb/base"
+import { type ICreateSchemaDTO, TableCreator, TableDo } from "@undb/table"
+import { getNextName } from "@undb/utils"
+import { type IBaseTemplateDTO } from "./dto/template.dto"
+
+export class TemplateFactory {
+ static create(template: IBaseTemplateDTO, baseNames: string[], spaceId: string): { base: Base; tables: TableDo[] }[] {
+ const result: { base: Base; tables: TableDo[] }[] = []
+ for (const [name, b] of Object.entries(template)) {
+ const baseName = getNextName(baseNames, name)
+ const base = BaseFactory.create({ name: baseName, spaceId })
+ const baseId = base.id.value
+
+ const tables: TableDo[] = []
+ for (const [name, table] of Object.entries(b.tables)) {
+ const schema = Object.entries(table.schema).map(([name, field]) => ({ ...field, name })) as ICreateSchemaDTO
+
+ const t = new TableCreator().create({ baseId, name, schema, spaceId })
+ tables.push(t)
+ }
+
+ result.push({ base, tables })
+ }
+
+ return result
+ }
+}
diff --git a/packages/template/src/templates/index.ts b/packages/template/src/templates/index.ts
new file mode 100644
index 000000000..f12a7b369
--- /dev/null
+++ b/packages/template/src/templates/index.ts
@@ -0,0 +1,6 @@
+import type { IBaseTemplateDTO } from "../dto"
+import { default as test } from "./test.base.json"
+
+const templates = { test } as Record
+
+export { templates }
diff --git a/packages/template/src/templates/test.base.json b/packages/template/src/templates/test.base.json
new file mode 100644
index 000000000..6fe83d4f2
--- /dev/null
+++ b/packages/template/src/templates/test.base.json
@@ -0,0 +1,32 @@
+{
+ "hello": {
+ "tables": {
+ "world": {
+ "schema": {
+ "name": {
+ "type": "string",
+ "constraint": {
+ "max": 10
+ },
+ "defaultValue": "world",
+ "display": true
+ },
+ "age": {
+ "type": "number",
+ "constraint": {
+ "min": 0
+ }
+ },
+ "parent": {
+ "type": "user",
+ "defaultValue": "@me"
+ },
+ "isActive": {
+ "type": "checkbox",
+ "defaultValue": true
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/template/tsconfig.json b/packages/template/tsconfig.json
new file mode 100644
index 000000000..34ae0fe3d
--- /dev/null
+++ b/packages/template/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ // Enable latest features
+ "lib": ["ESNext", "DOM"],
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleDetection": "force",
+ "jsx": "react-jsx",
+ "allowJs": true,
+
+ // Bundler mode
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+
+ // Best practices
+ "strict": true,
+ "skipLibCheck": true,
+ "noFallthroughCasesInSwitch": true,
+
+ // Some stricter flags (disabled by default)
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noPropertyAccessFromIndexSignature": false,
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true
+ }
+}
diff --git a/packages/trpc/package.json b/packages/trpc/package.json
index a19dd241d..b6d4e771e 100644
--- a/packages/trpc/package.json
+++ b/packages/trpc/package.json
@@ -20,6 +20,7 @@
"@undb/persistence": "workspace:*",
"@undb/queries": "workspace:*",
"@undb/table": "workspace:*",
+ "@undb/template": "workspace:*",
"@undb/zod": "workspace:*",
"zod-validation-error": "^3.3.1"
}
diff --git a/packages/trpc/src/router.ts b/packages/trpc/src/router.ts
index d6257bb1e..48e2f6920 100644
--- a/packages/trpc/src/router.ts
+++ b/packages/trpc/src/router.ts
@@ -4,6 +4,7 @@ import {
BulkUpdateRecordsCommand,
CreateApiTokenCommand,
CreateBaseCommand,
+ CreateFromShareCommand,
CreateFromTemplateCommand,
CreateRecordCommand,
CreateRecordsCommand,
@@ -55,7 +56,9 @@ import {
bulkduplicateRecordsCommand,
createApiTokenCommand,
createBaseCommand,
+ createFromShareCommand,
createFromTemplateCommand,
+ createFromTemplateCommandOutput,
createRecordCommand,
createRecordsCommand,
createSpaceCommand,
@@ -106,9 +109,9 @@ import {
updateaccountCommand,
} from "@undb/commands"
import { getCurrentSpaceId } from "@undb/context/server"
-import { CommandBus,QueryBus } from "@undb/cqrs"
+import { CommandBus, QueryBus } from "@undb/cqrs"
import { container } from "@undb/di"
-import type { ICommandBus,IQueryBus } from "@undb/domain"
+import type { ICommandBus, IQueryBus } from "@undb/domain"
import {
CountRecordsQuery,
GetAggregatesQuery,
@@ -136,7 +139,7 @@ import {
import { tableDTO } from "@undb/table"
import { z } from "@undb/zod"
import { authz } from "./authz.middleware"
-import { privateProcedure,publicProcedure,t } from "./trpc"
+import { privateProcedure, publicProcedure, t } from "./trpc"
const commandBus = container.resolve(CommandBus)
const queryBus = container.resolve(QueryBus)
@@ -361,11 +364,11 @@ const baseRouter = t.router({
}
return commandBus.execute(new CreateBaseCommand({ ...input, spaceId }))
}),
- createFromTemplate: privateProcedure
+ createFromShare: privateProcedure
// check authz in handler, because we can create base to another space
// .use(authz("base:create"))
- .input(createFromTemplateCommand)
- .mutation(({ input }) => commandBus.execute(new CreateFromTemplateCommand(input))),
+ .input(createFromShareCommand)
+ .mutation(({ input }) => commandBus.execute(new CreateFromShareCommand(input))),
duplicate: privateProcedure
.use(authz("base:create"))
.input(duplicateBaseCommand)
@@ -425,18 +428,26 @@ const apiTokenRouter = t.router({
const spaceRouter = t.router({
list: privateProcedure
- .input(getMemberSpacesQuery)
.use(authz("space:list"))
+ .input(getMemberSpacesQuery)
.query(({ input }) => queryBus.execute(new GetMemberSpacesQuery(input))),
create: privateProcedure
.input(createSpaceCommand)
.mutation(({ input }) => commandBus.execute(new CreateSpaceCommand(input))),
update: privateProcedure
- .input(updateSpaceCommand)
.use(authz("space:update"))
+ .input(updateSpaceCommand)
.mutation(({ input }) => commandBus.execute(new UpdateSpaceCommand(input))),
})
+const templateRouter = t.router({
+ createFromTemplate: privateProcedure
+ .use(authz("base:create"))
+ .input(createFromTemplateCommand)
+ .output(createFromTemplateCommandOutput)
+ .mutation(({ input }) => commandBus.execute(new CreateFromTemplateCommand(input))),
+})
+
export const route = t.router({
table: tableRouter,
record: recordRouter,
@@ -448,6 +459,7 @@ export const route = t.router({
space: spaceRouter,
apiToken: apiTokenRouter,
shareData: shareDataRouter,
+ template: templateRouter,
})
export type AppRouter = typeof route