@kevinmarrec/create-app 0.6.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +1 -3
- package/package.json +22 -18
- package/template/.github/workflows/ci.yml +3 -3
- package/template/.scripts/dev.ts +8 -0
- package/template/.vscode/settings.json +1 -1
- package/template/client/.env +1 -0
- package/template/{frontend → client}/package.json +11 -9
- package/template/{frontend → client}/src/App.vue +9 -2
- package/template/client/src/composables/auth.ts +39 -0
- package/template/client/src/composables/content.ts +12 -0
- package/template/{frontend → client}/src/composables/index.ts +1 -0
- package/template/{frontend → client}/src/lib/orpc.ts +2 -2
- package/template/compose.yaml +5 -5
- package/template/knip.config.ts +11 -6
- package/template/package.json +10 -10
- package/template/server/.env.development +4 -0
- package/template/{backend → server}/package.json +7 -7
- package/template/{backend → server}/src/auth/index.ts +3 -1
- package/template/{backend → server}/src/database/drizzle/config.ts +2 -1
- package/template/server/src/database/index.ts +20 -0
- package/template/server/src/database/migrations/0000_init.sql +50 -0
- package/template/{backend → server}/src/database/migrations/meta/0000_snapshot.json +92 -105
- package/template/server/src/database/migrations/meta/_journal.json +13 -0
- package/template/server/src/database/schema/accounts.ts +20 -0
- package/template/server/src/database/schema/sessions.ts +15 -0
- package/template/server/src/database/schema/users.ts +12 -0
- package/template/server/src/database/schema/verifications.ts +11 -0
- package/template/{backend → server}/src/env.d.ts +2 -2
- package/template/{backend → server}/src/main.ts +20 -16
- package/template/server/src/orpc/context.ts +10 -0
- package/template/server/src/orpc/handler.ts +34 -0
- package/template/server/src/orpc/index.ts +18 -0
- package/template/server/src/orpc/middlewares/auth.ts +23 -0
- package/template/server/src/orpc/router/index.ts +19 -0
- package/template/server/src/utils/cors.ts +26 -0
- package/template/tsconfig.json +2 -2
- package/template/backend/.env.development +0 -4
- package/template/backend/src/database/index.ts +0 -23
- package/template/backend/src/database/migrations/0000_fluffy_salo.sql +0 -49
- package/template/backend/src/database/migrations/meta/_journal.json +0 -13
- package/template/backend/src/database/schema/accounts.ts +0 -20
- package/template/backend/src/database/schema/sessions.ts +0 -15
- package/template/backend/src/database/schema/users.ts +0 -12
- package/template/backend/src/database/schema/verifications.ts +0 -11
- package/template/backend/src/orpc/index.ts +0 -65
- package/template/backend/src/orpc/middlewares/auth.ts +0 -33
- package/template/backend/src/orpc/router/auth.ts +0 -54
- package/template/backend/src/orpc/router/index.ts +0 -9
- package/template/frontend/.env.development +0 -1
- package/template/frontend/src/composables/auth.ts +0 -31
- package/template/scripts/dev.ts +0 -8
- /package/template/{gitignore → .gitignore} +0 -0
- /package/template/{npmrc → .npmrc} +0 -0
- /package/template/{frontend → client}/index.html +0 -0
- /package/template/{frontend → client}/public/favicon.svg +0 -0
- /package/template/{frontend → client}/public/robots.txt +0 -0
- /package/template/{frontend → client}/src/components/.gitkeep +0 -0
- /package/template/{frontend → client}/src/env.d.ts +0 -0
- /package/template/{frontend → client}/src/locales/en.yml +0 -0
- /package/template/{frontend → client}/src/locales/fr.yml +0 -0
- /package/template/{frontend → client}/src/main.ts +0 -0
- /package/template/{frontend → client}/tsconfig.json +0 -0
- /package/template/{frontend → client}/uno.config.ts +0 -0
- /package/template/{frontend → client}/vite.config.ts +0 -0
- /package/template/{frontend → client}/wrangler.json +0 -0
- /package/template/{backend → server}/src/database/schema/index.ts +0 -0
- /package/template/{backend → server}/src/orpc/middlewares/index.ts +0 -0
- /package/template/{backend → server}/src/utils/logger.ts +0 -0
- /package/template/{backend → server}/tsconfig.json +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function cors(handler: (req: Request) => Promise<Response>) {
|
|
2
|
+
const allowedOrigins = import.meta.env.ALLOWED_ORIGINS.split(',')
|
|
3
|
+
|
|
4
|
+
return async (req: Request) => {
|
|
5
|
+
const origin = req.headers.get('origin') ?? ''
|
|
6
|
+
|
|
7
|
+
if (!origin || !allowedOrigins.includes(origin)) {
|
|
8
|
+
return new Response('Origin not allowed', { status: 403 })
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const response = req.method === 'OPTIONS'
|
|
12
|
+
? new Response(undefined, { status: 204 })
|
|
13
|
+
: await handler(req)
|
|
14
|
+
|
|
15
|
+
if (req.method === 'OPTIONS') {
|
|
16
|
+
response.headers.append('Access-Control-Allow-Headers', 'Content-Type')
|
|
17
|
+
response.headers.append('Access-Control-Allow-Methods', 'GET, HEAD, PUT, POST, DELETE, PATCH')
|
|
18
|
+
response.headers.append('Access-Control-Max-Age', '7200') // 2 hours https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
response.headers.append('Access-Control-Allow-Credentials', 'true')
|
|
22
|
+
response.headers.append('Access-Control-Allow-Origin', origin)
|
|
23
|
+
|
|
24
|
+
return response
|
|
25
|
+
}
|
|
26
|
+
}
|
package/template/tsconfig.json
CHANGED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { logger } from '@backend/utils/logger'
|
|
2
|
-
import { drizzle } from 'drizzle-orm/bun-sqlite'
|
|
3
|
-
|
|
4
|
-
import * as schema from './schema'
|
|
5
|
-
|
|
6
|
-
export const db = drizzle(import.meta.env.DATABASE_URL, {
|
|
7
|
-
casing: 'snake_case',
|
|
8
|
-
schema,
|
|
9
|
-
logger: {
|
|
10
|
-
logQuery: (query, params) => {
|
|
11
|
-
let msg = `[SQL] ${query}`
|
|
12
|
-
msg += params.length ? ` [${params}]` : ''
|
|
13
|
-
logger.debug(msg)
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
db.run('PRAGMA journal_mode = WAL')
|
|
19
|
-
db.run('PRAGMA journal_size_limit = 6144000')
|
|
20
|
-
db.run('PRAGMA synchronous = NORMAL')
|
|
21
|
-
db.run('PRAGMA foreign_keys = ON')
|
|
22
|
-
|
|
23
|
-
export type Database = typeof db
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
CREATE TABLE `users` (
|
|
2
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
-
`name` text NOT NULL,
|
|
4
|
-
`email` text NOT NULL,
|
|
5
|
-
`email_verified` integer DEFAULT false NOT NULL,
|
|
6
|
-
`image` text,
|
|
7
|
-
`created_at` integer NOT NULL,
|
|
8
|
-
`updated_at` integer NOT NULL
|
|
9
|
-
);
|
|
10
|
-
--> statement-breakpoint
|
|
11
|
-
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint
|
|
12
|
-
CREATE TABLE `sessions` (
|
|
13
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
14
|
-
`user_id` text NOT NULL,
|
|
15
|
-
`token` text NOT NULL,
|
|
16
|
-
`expires_at` integer NOT NULL,
|
|
17
|
-
`ip_address` text,
|
|
18
|
-
`user_agent` text,
|
|
19
|
-
`created_at` integer NOT NULL,
|
|
20
|
-
`updated_at` integer NOT NULL,
|
|
21
|
-
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
|
22
|
-
);
|
|
23
|
-
--> statement-breakpoint
|
|
24
|
-
CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint
|
|
25
|
-
CREATE TABLE `accounts` (
|
|
26
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
27
|
-
`user_id` text NOT NULL,
|
|
28
|
-
`account_id` text NOT NULL,
|
|
29
|
-
`provider_id` text NOT NULL,
|
|
30
|
-
`access_token` text,
|
|
31
|
-
`refresh_token` text,
|
|
32
|
-
`access_token_expires_at` integer,
|
|
33
|
-
`refresh_token_expires_at` integer,
|
|
34
|
-
`scope` text,
|
|
35
|
-
`id_token` text,
|
|
36
|
-
`password` text,
|
|
37
|
-
`created_at` integer NOT NULL,
|
|
38
|
-
`updated_at` integer NOT NULL,
|
|
39
|
-
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
|
|
40
|
-
);
|
|
41
|
-
--> statement-breakpoint
|
|
42
|
-
CREATE TABLE `verifications` (
|
|
43
|
-
`id` text PRIMARY KEY NOT NULL,
|
|
44
|
-
`identifier` text NOT NULL,
|
|
45
|
-
`value` text NOT NULL,
|
|
46
|
-
`expires_at` integer NOT NULL,
|
|
47
|
-
`created_at` integer NOT NULL,
|
|
48
|
-
`updated_at` integer NOT NULL
|
|
49
|
-
);
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { sqliteTable } from 'drizzle-orm/sqlite-core'
|
|
2
|
-
|
|
3
|
-
import { users } from './users'
|
|
4
|
-
|
|
5
|
-
// https://www.better-auth.com/docs/concepts/database#account
|
|
6
|
-
export const accounts = sqliteTable('accounts', t => ({
|
|
7
|
-
id: t.text().primaryKey(),
|
|
8
|
-
userId: t.text().notNull().references(() => users.id, { onDelete: 'cascade' }),
|
|
9
|
-
accountId: t.text().notNull(),
|
|
10
|
-
providerId: t.text().notNull(),
|
|
11
|
-
accessToken: t.text(),
|
|
12
|
-
refreshToken: t.text(),
|
|
13
|
-
accessTokenExpiresAt: t.integer({ mode: 'timestamp' }),
|
|
14
|
-
refreshTokenExpiresAt: t.integer({ mode: 'timestamp' }),
|
|
15
|
-
scope: t.text(),
|
|
16
|
-
idToken: t.text(),
|
|
17
|
-
password: t.text(),
|
|
18
|
-
createdAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
|
|
19
|
-
updatedAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()).$onUpdate(() => new Date()),
|
|
20
|
-
}))
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { sqliteTable } from 'drizzle-orm/sqlite-core'
|
|
2
|
-
|
|
3
|
-
import { users } from './users'
|
|
4
|
-
|
|
5
|
-
// https://www.better-auth.com/docs/concepts/database#session
|
|
6
|
-
export const sessions = sqliteTable('sessions', t => ({
|
|
7
|
-
id: t.text().primaryKey(),
|
|
8
|
-
userId: t.text().notNull().references(() => users.id, { onDelete: 'cascade' }),
|
|
9
|
-
token: t.text().notNull().unique(),
|
|
10
|
-
expiresAt: t.integer({ mode: 'timestamp' }).notNull(),
|
|
11
|
-
ipAddress: t.text(),
|
|
12
|
-
userAgent: t.text(),
|
|
13
|
-
createdAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
|
|
14
|
-
updatedAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()).$onUpdate(() => new Date()),
|
|
15
|
-
}))
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { sqliteTable } from 'drizzle-orm/sqlite-core'
|
|
2
|
-
|
|
3
|
-
// https://www.better-auth.com/docs/concepts/database#user
|
|
4
|
-
export const users = sqliteTable('users', t => ({
|
|
5
|
-
id: t.text().primaryKey(),
|
|
6
|
-
name: t.text().notNull(),
|
|
7
|
-
email: t.text().notNull().unique(),
|
|
8
|
-
emailVerified: t.integer({ mode: 'boolean' }).notNull().default(false),
|
|
9
|
-
image: t.text(),
|
|
10
|
-
createdAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
|
|
11
|
-
updatedAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()).$onUpdate(() => new Date()),
|
|
12
|
-
}))
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { sqliteTable } from 'drizzle-orm/sqlite-core'
|
|
2
|
-
|
|
3
|
-
// https://www.better-auth.com/docs/concepts/database#verification
|
|
4
|
-
export const verifications = sqliteTable('verifications', t => ({
|
|
5
|
-
id: t.text().primaryKey(),
|
|
6
|
-
identifier: t.text().notNull(),
|
|
7
|
-
value: t.text().notNull(),
|
|
8
|
-
expiresAt: t.integer({ mode: 'timestamp' }).notNull(),
|
|
9
|
-
createdAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
|
|
10
|
-
updatedAt: t.integer({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()).$onUpdate(() => new Date()),
|
|
11
|
-
}))
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { Auth } from '@backend/auth'
|
|
2
|
-
import type { Database } from '@backend/database'
|
|
3
|
-
import { requiredAuthMiddleware } from '@backend/orpc/middlewares'
|
|
4
|
-
import type { Logger } from '@backend/utils/logger'
|
|
5
|
-
import { onError, ORPCError, os, type Router } from '@orpc/server'
|
|
6
|
-
import { RPCHandler } from '@orpc/server/fetch'
|
|
7
|
-
import {
|
|
8
|
-
CORSPlugin,
|
|
9
|
-
RequestHeadersPlugin,
|
|
10
|
-
type RequestHeadersPluginContext,
|
|
11
|
-
ResponseHeadersPlugin,
|
|
12
|
-
type ResponseHeadersPluginContext,
|
|
13
|
-
} from '@orpc/server/plugins'
|
|
14
|
-
import { APIError } from 'better-auth/api'
|
|
15
|
-
|
|
16
|
-
/* Context */
|
|
17
|
-
|
|
18
|
-
export interface Context extends RequestHeadersPluginContext, ResponseHeadersPluginContext {
|
|
19
|
-
auth: Auth
|
|
20
|
-
db: Database
|
|
21
|
-
logger: Logger
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/* RPC Handler */
|
|
25
|
-
|
|
26
|
-
export function createRpcHandler<T extends Context>(router: Router<any, T>) {
|
|
27
|
-
return new RPCHandler<T>(router, {
|
|
28
|
-
plugins: [
|
|
29
|
-
new CORSPlugin({
|
|
30
|
-
credentials: true,
|
|
31
|
-
maxAge: 7200, // 2 hours https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Max-Age
|
|
32
|
-
}),
|
|
33
|
-
new RequestHeadersPlugin(),
|
|
34
|
-
new ResponseHeadersPlugin(),
|
|
35
|
-
],
|
|
36
|
-
clientInterceptors: [
|
|
37
|
-
onError((error, { context }) => {
|
|
38
|
-
if (error instanceof APIError) {
|
|
39
|
-
throw new ORPCError(error.body?.code ?? 'INTERNAL_SERVER_ERROR', {
|
|
40
|
-
status: error.statusCode,
|
|
41
|
-
message: error.body?.message,
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (error instanceof ORPCError) {
|
|
46
|
-
throw error
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
context.logger.error(error)
|
|
50
|
-
}),
|
|
51
|
-
],
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* Builder */
|
|
56
|
-
|
|
57
|
-
export const pub = os
|
|
58
|
-
.$context<Context>()
|
|
59
|
-
.errors({
|
|
60
|
-
UNAUTHORIZED: { status: 401 },
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
/** @beta */
|
|
64
|
-
export const authed = pub
|
|
65
|
-
.use(requiredAuthMiddleware)
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import type { Context } from '@backend/orpc'
|
|
2
|
-
import { ORPCError, os } from '@orpc/server'
|
|
3
|
-
|
|
4
|
-
export const authMiddleware = os
|
|
5
|
-
.$context<Context>()
|
|
6
|
-
.middleware(async ({ context, next }) => {
|
|
7
|
-
const { headers, response: session } = await context.auth.api.getSession({
|
|
8
|
-
headers: context.reqHeaders ?? new Headers(),
|
|
9
|
-
returnHeaders: true,
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
headers.forEach((v, k) => context.resHeaders?.append(k, v))
|
|
13
|
-
|
|
14
|
-
return next({
|
|
15
|
-
context: {
|
|
16
|
-
user: session ? session.user : null,
|
|
17
|
-
},
|
|
18
|
-
})
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
export const requiredAuthMiddleware = authMiddleware
|
|
22
|
-
.concat(async ({ context, next }) => {
|
|
23
|
-
if (!context.user) {
|
|
24
|
-
throw new ORPCError('UNAUTHORIZED')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return next({
|
|
28
|
-
context: {
|
|
29
|
-
user: context.user,
|
|
30
|
-
},
|
|
31
|
-
})
|
|
32
|
-
},
|
|
33
|
-
)
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { pub } from '@backend/orpc'
|
|
2
|
-
import { authMiddleware } from '@backend/orpc/middlewares'
|
|
3
|
-
import * as v from 'valibot'
|
|
4
|
-
|
|
5
|
-
export const getCurrentUser = pub
|
|
6
|
-
.use(authMiddleware)
|
|
7
|
-
.handler(async ({ context }) => context.user)
|
|
8
|
-
|
|
9
|
-
export const signUp = pub
|
|
10
|
-
.input(v.object({
|
|
11
|
-
name: v.string(),
|
|
12
|
-
email: v.pipe(v.string(), v.email()),
|
|
13
|
-
password: v.string(),
|
|
14
|
-
rememberMe: v.optional(v.boolean(), true),
|
|
15
|
-
}))
|
|
16
|
-
.handler(async ({ input, context: { resHeaders, auth } }) => {
|
|
17
|
-
const { headers, response } = await auth.api.signUpEmail({
|
|
18
|
-
body: input,
|
|
19
|
-
returnHeaders: true,
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
headers.forEach((v, k) => resHeaders?.append(k, v))
|
|
23
|
-
|
|
24
|
-
return response.user
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
export const signIn = pub
|
|
28
|
-
.input(v.object({
|
|
29
|
-
email: v.pipe(v.string(), v.email()),
|
|
30
|
-
password: v.string(),
|
|
31
|
-
rememberMe: v.optional(v.boolean(), true),
|
|
32
|
-
}))
|
|
33
|
-
.handler(async ({ input, context: { resHeaders, auth } }) => {
|
|
34
|
-
const { headers, response } = await auth.api.signInEmail({
|
|
35
|
-
body: input,
|
|
36
|
-
returnHeaders: true,
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
headers.forEach((v, k) => resHeaders?.append(k, v))
|
|
40
|
-
|
|
41
|
-
return response.user
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
export const signOut = pub
|
|
45
|
-
.handler(async ({ context: { auth, reqHeaders, resHeaders } }) => {
|
|
46
|
-
const { headers, response } = await auth.api.signOut({
|
|
47
|
-
headers: reqHeaders ?? new Headers(),
|
|
48
|
-
returnHeaders: true,
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
headers.forEach((v, k) => resHeaders?.append(k, v))
|
|
52
|
-
|
|
53
|
-
return response
|
|
54
|
-
})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VITE_API_URL=http://localhost:4000/rpc
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { orpc } from '@frontend/lib/orpc'
|
|
2
|
-
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
|
3
|
-
|
|
4
|
-
export function useAuth() {
|
|
5
|
-
const qc = useQueryClient()
|
|
6
|
-
|
|
7
|
-
const { data: user } = useQuery(orpc.auth.getCurrentUser.queryOptions({
|
|
8
|
-
retry: false,
|
|
9
|
-
}))
|
|
10
|
-
|
|
11
|
-
const signUp = useMutation(orpc.auth.signUp.mutationOptions({
|
|
12
|
-
onSuccess: () => qc.invalidateQueries({ queryKey: orpc.auth.getCurrentUser.queryKey() }),
|
|
13
|
-
}))
|
|
14
|
-
|
|
15
|
-
const signIn = useMutation(orpc.auth.signIn.mutationOptions({
|
|
16
|
-
onSuccess: () => qc.invalidateQueries({ queryKey: orpc.auth.getCurrentUser.queryKey() }),
|
|
17
|
-
}))
|
|
18
|
-
|
|
19
|
-
const signOut = useMutation(orpc.auth.signOut.mutationOptions({
|
|
20
|
-
onSuccess: () => {
|
|
21
|
-
qc.setQueryData<null>(orpc.auth.getCurrentUser.queryKey(), null)
|
|
22
|
-
},
|
|
23
|
-
}))
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
user,
|
|
27
|
-
signUp,
|
|
28
|
-
signIn,
|
|
29
|
-
signOut,
|
|
30
|
-
}
|
|
31
|
-
}
|
package/template/scripts/dev.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import concurrently, { type ConcurrentlyCommandInput } from 'concurrently'
|
|
2
|
-
|
|
3
|
-
const commandInputs: ConcurrentlyCommandInput[] = [
|
|
4
|
-
{ name: 'backend', command: `bun --cwd backend dev | pino-pretty`, prefixColor: 'blue' },
|
|
5
|
-
{ name: 'frontend', command: `bun --cwd frontend dev`, prefixColor: 'green' },
|
|
6
|
-
]
|
|
7
|
-
|
|
8
|
-
concurrently(commandInputs)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|