create-atsdc-stack 1.1.0 → 1.2.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.
Files changed (48) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/CLAUDE.md +236 -215
  3. package/CONTRIBUTING.md +342 -342
  4. package/INSTALLATION.md +359 -359
  5. package/LICENSE +201 -201
  6. package/README.md +405 -405
  7. package/app/.env.example +17 -17
  8. package/app/.github/labeler.yml +61 -0
  9. package/app/.github/workflows/browser-tests.yml +101 -0
  10. package/app/.github/workflows/check.yml +24 -0
  11. package/app/.github/workflows/greetings.yml +16 -0
  12. package/app/.github/workflows/label.yml +22 -0
  13. package/app/.github/workflows/stale.yml +27 -0
  14. package/app/.github/workflows/summary.yml +34 -0
  15. package/app/.stylelintrc.json +8 -0
  16. package/app/README.md +251 -251
  17. package/app/astro.config.mjs +83 -83
  18. package/app/drizzle.config.ts +16 -16
  19. package/app/package.json +66 -52
  20. package/app/playwright.config.ts +27 -0
  21. package/app/public/manifest.webmanifest +36 -36
  22. package/app/pwa-assets.config.ts +8 -0
  23. package/app/src/components/Card.astro +36 -36
  24. package/app/src/db/initialize.ts +107 -107
  25. package/app/src/db/schema.ts +72 -72
  26. package/app/src/db/validations.ts +158 -158
  27. package/app/src/layouts/Layout.astro +63 -63
  28. package/app/src/lib/config.ts +36 -36
  29. package/app/src/lib/content-converter.ts +141 -141
  30. package/app/src/lib/dom-utils.ts +230 -230
  31. package/app/src/lib/exa-search.ts +269 -269
  32. package/app/src/pages/api/chat.ts +91 -91
  33. package/app/src/pages/api/posts.ts +350 -350
  34. package/app/src/pages/index.astro +87 -87
  35. package/app/src/styles/components/button.scss +152 -152
  36. package/app/src/styles/components/card.scss +180 -180
  37. package/app/src/styles/components/form.scss +240 -240
  38. package/app/src/styles/global.scss +141 -141
  39. package/app/src/styles/pages/index.scss +80 -80
  40. package/app/src/styles/reset.scss +83 -83
  41. package/app/src/styles/variables/globals.scss +96 -96
  42. package/app/src/styles/variables/mixins.scss +238 -238
  43. package/app/tests/browser.test.nopause.ts +10 -0
  44. package/app/tests/browser.test.ts +13 -0
  45. package/bin/cli.js +1151 -1151
  46. package/package.json +8 -6
  47. package/app/.astro/settings.json +0 -5
  48. package/app/.astro/types.d.ts +0 -1
@@ -1,72 +1,72 @@
1
- import { pgTable, text, timestamp, boolean, varchar } from 'drizzle-orm/pg-core';
2
- import { nanoid } from 'nanoid';
3
-
4
- /**
5
- * Posts table schema
6
- * Uses NanoID for primary keys for better URL-safe unique identifiers
7
- */
8
- export const posts = pgTable('posts', {
9
- // Primary key using NanoID (21 characters, URL-safe)
10
- id: varchar('id', { length: 21 })
11
- .primaryKey()
12
- .$defaultFn(() => nanoid()),
13
-
14
- // Post content fields
15
- title: varchar('title', { length: 255 }).notNull(),
16
- slug: varchar('slug', { length: 255 }).notNull().unique(),
17
- content: text('content').notNull(),
18
- excerpt: text('excerpt'),
19
-
20
- // Author information (Clerk user ID)
21
- authorId: varchar('author_id', { length: 255 }).notNull(),
22
- authorName: varchar('author_name', { length: 255 }),
23
-
24
- // Post metadata
25
- published: boolean('published').default(false).notNull(),
26
- featured: boolean('featured').default(false).notNull(),
27
-
28
- // SEO fields
29
- metaTitle: varchar('meta_title', { length: 255 }),
30
- metaDescription: text('meta_description'),
31
-
32
- // Timestamps
33
- createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
34
- updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
35
- publishedAt: timestamp('published_at', { withTimezone: true }),
36
- });
37
-
38
- /**
39
- * Comments table schema
40
- * Demonstrates relationship with posts using NanoID
41
- */
42
- export const comments = pgTable('comments', {
43
- id: varchar('id', { length: 21 })
44
- .primaryKey()
45
- .$defaultFn(() => nanoid()),
46
-
47
- // Foreign key to posts table
48
- postId: varchar('post_id', { length: 21 })
49
- .notNull()
50
- .references(() => posts.id, { onDelete: 'cascade' }),
51
-
52
- // Comment content
53
- content: text('content').notNull(),
54
-
55
- // Author information (Clerk user ID)
56
- authorId: varchar('author_id', { length: 255 }).notNull(),
57
- authorName: varchar('author_name', { length: 255 }),
58
-
59
- // Moderation
60
- approved: boolean('approved').default(false).notNull(),
61
- flagged: boolean('flagged').default(false).notNull(),
62
-
63
- // Timestamps
64
- createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
65
- updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
66
- });
67
-
68
- // Type exports for use in application
69
- export type Post = typeof posts.$inferSelect;
70
- export type NewPost = typeof posts.$inferInsert;
71
- export type Comment = typeof comments.$inferSelect;
72
- export type NewComment = typeof comments.$inferInsert;
1
+ import { pgTable, text, timestamp, boolean, varchar } from 'drizzle-orm/pg-core';
2
+ import { nanoid } from 'nanoid';
3
+
4
+ /**
5
+ * Posts table schema
6
+ * Uses NanoID for primary keys for better URL-safe unique identifiers
7
+ */
8
+ export const posts = pgTable('posts', {
9
+ // Primary key using NanoID (21 characters, URL-safe)
10
+ id: varchar('id', { length: 21 })
11
+ .primaryKey()
12
+ .$defaultFn(() => nanoid()),
13
+
14
+ // Post content fields
15
+ title: varchar('title', { length: 255 }).notNull(),
16
+ slug: varchar('slug', { length: 255 }).notNull().unique(),
17
+ content: text('content').notNull(),
18
+ excerpt: text('excerpt'),
19
+
20
+ // Author information (Clerk user ID)
21
+ authorId: varchar('author_id', { length: 255 }).notNull(),
22
+ authorName: varchar('author_name', { length: 255 }),
23
+
24
+ // Post metadata
25
+ published: boolean('published').default(false).notNull(),
26
+ featured: boolean('featured').default(false).notNull(),
27
+
28
+ // SEO fields
29
+ metaTitle: varchar('meta_title', { length: 255 }),
30
+ metaDescription: text('meta_description'),
31
+
32
+ // Timestamps
33
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
34
+ updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
35
+ publishedAt: timestamp('published_at', { withTimezone: true }),
36
+ });
37
+
38
+ /**
39
+ * Comments table schema
40
+ * Demonstrates relationship with posts using NanoID
41
+ */
42
+ export const comments = pgTable('comments', {
43
+ id: varchar('id', { length: 21 })
44
+ .primaryKey()
45
+ .$defaultFn(() => nanoid()),
46
+
47
+ // Foreign key to posts table
48
+ postId: varchar('post_id', { length: 21 })
49
+ .notNull()
50
+ .references(() => posts.id, { onDelete: 'cascade' }),
51
+
52
+ // Comment content
53
+ content: text('content').notNull(),
54
+
55
+ // Author information (Clerk user ID)
56
+ authorId: varchar('author_id', { length: 255 }).notNull(),
57
+ authorName: varchar('author_name', { length: 255 }),
58
+
59
+ // Moderation
60
+ approved: boolean('approved').default(false).notNull(),
61
+ flagged: boolean('flagged').default(false).notNull(),
62
+
63
+ // Timestamps
64
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
65
+ updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
66
+ });
67
+
68
+ // Type exports for use in application
69
+ export type Post = typeof posts.$inferSelect;
70
+ export type NewPost = typeof posts.$inferInsert;
71
+ export type Comment = typeof comments.$inferSelect;
72
+ export type NewComment = typeof comments.$inferInsert;
@@ -1,158 +1,158 @@
1
- import { z } from 'zod';
2
-
3
- /**
4
- * Zod validation schemas for database models
5
- * Provides runtime type safety and validation for user inputs
6
- */
7
-
8
- // Slug validation helper
9
- const slugSchema = z
10
- .string()
11
- .min(1, 'Slug is required')
12
- .max(255, 'Slug must be 255 characters or less')
13
- .regex(
14
- /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
15
- 'Slug must be lowercase alphanumeric with hyphens only'
16
- );
17
-
18
- /**
19
- * Post validation schemas
20
- */
21
-
22
- // Schema for creating a new post
23
- export const createPostSchema = z.object({
24
- title: z
25
- .string()
26
- .min(1, 'Title is required')
27
- .max(255, 'Title must be 255 characters or less')
28
- .trim(),
29
-
30
- slug: slugSchema,
31
-
32
- content: z
33
- .string()
34
- .min(1, 'Content is required')
35
- .max(50000, 'Content must be 50,000 characters or less'),
36
-
37
- excerpt: z
38
- .string()
39
- .max(500, 'Excerpt must be 500 characters or less')
40
- .optional()
41
- .nullable(),
42
-
43
- authorId: z.string().min(1, 'Author ID is required'),
44
-
45
- authorName: z
46
- .string()
47
- .max(255, 'Author name must be 255 characters or less')
48
- .optional()
49
- .nullable(),
50
-
51
- published: z.boolean().default(false),
52
-
53
- featured: z.boolean().default(false),
54
-
55
- metaTitle: z
56
- .string()
57
- .max(255, 'Meta title must be 255 characters or less')
58
- .optional()
59
- .nullable(),
60
-
61
- metaDescription: z
62
- .string()
63
- .max(500, 'Meta description must be 500 characters or less')
64
- .optional()
65
- .nullable(),
66
- });
67
-
68
- // Schema for updating an existing post
69
- export const updatePostSchema = createPostSchema.partial().extend({
70
- id: z.string().length(21, 'Invalid post ID'),
71
- });
72
-
73
- // Schema for publishing/unpublishing a post
74
- export const publishPostSchema = z.object({
75
- id: z.string().length(21, 'Invalid post ID'),
76
- published: z.boolean(),
77
- });
78
-
79
- // Schema for post query parameters
80
- export const postQuerySchema = z.object({
81
- page: z.coerce.number().int().positive().default(1),
82
- limit: z.coerce.number().int().positive().max(100).default(10),
83
- published: z
84
- .string()
85
- .transform((val) => val === 'true')
86
- .optional(),
87
- featured: z
88
- .string()
89
- .transform((val) => val === 'true')
90
- .optional(),
91
- authorId: z.string().optional(),
92
- search: z.string().optional(),
93
- });
94
-
95
- /**
96
- * Comment validation schemas
97
- */
98
-
99
- // Schema for creating a new comment
100
- export const createCommentSchema = z.object({
101
- postId: z.string().length(21, 'Invalid post ID'),
102
-
103
- content: z
104
- .string()
105
- .min(1, 'Comment content is required')
106
- .max(2000, 'Comment must be 2,000 characters or less')
107
- .trim(),
108
-
109
- authorId: z.string().min(1, 'Author ID is required'),
110
-
111
- authorName: z
112
- .string()
113
- .max(255, 'Author name must be 255 characters or less')
114
- .optional()
115
- .nullable(),
116
- });
117
-
118
- // Schema for updating a comment
119
- export const updateCommentSchema = z.object({
120
- id: z.string().length(21, 'Invalid comment ID'),
121
- content: z
122
- .string()
123
- .min(1, 'Comment content is required')
124
- .max(2000, 'Comment must be 2,000 characters or less')
125
- .trim(),
126
- });
127
-
128
- // Schema for moderating a comment
129
- export const moderateCommentSchema = z.object({
130
- id: z.string().length(21, 'Invalid comment ID'),
131
- approved: z.boolean().optional(),
132
- flagged: z.boolean().optional(),
133
- });
134
-
135
- // Schema for comment query parameters
136
- export const commentQuerySchema = z.object({
137
- postId: z.string().length(21, 'Invalid post ID').optional(),
138
- page: z.coerce.number().int().positive().default(1),
139
- limit: z.coerce.number().int().positive().max(100).default(20),
140
- approved: z
141
- .string()
142
- .transform((val) => val === 'true')
143
- .optional(),
144
- authorId: z.string().optional(),
145
- });
146
-
147
- /**
148
- * Type exports for use in application
149
- */
150
- export type CreatePostInput = z.infer<typeof createPostSchema>;
151
- export type UpdatePostInput = z.infer<typeof updatePostSchema>;
152
- export type PublishPostInput = z.infer<typeof publishPostSchema>;
153
- export type PostQueryInput = z.infer<typeof postQuerySchema>;
154
-
155
- export type CreateCommentInput = z.infer<typeof createCommentSchema>;
156
- export type UpdateCommentInput = z.infer<typeof updateCommentSchema>;
157
- export type ModerateCommentInput = z.infer<typeof moderateCommentSchema>;
158
- export type CommentQueryInput = z.infer<typeof commentQuerySchema>;
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * Zod validation schemas for database models
5
+ * Provides runtime type safety and validation for user inputs
6
+ */
7
+
8
+ // Slug validation helper
9
+ const slugSchema = z
10
+ .string()
11
+ .min(1, 'Slug is required')
12
+ .max(255, 'Slug must be 255 characters or less')
13
+ .regex(
14
+ /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
15
+ 'Slug must be lowercase alphanumeric with hyphens only'
16
+ );
17
+
18
+ /**
19
+ * Post validation schemas
20
+ */
21
+
22
+ // Schema for creating a new post
23
+ export const createPostSchema = z.object({
24
+ title: z
25
+ .string()
26
+ .min(1, 'Title is required')
27
+ .max(255, 'Title must be 255 characters or less')
28
+ .trim(),
29
+
30
+ slug: slugSchema,
31
+
32
+ content: z
33
+ .string()
34
+ .min(1, 'Content is required')
35
+ .max(50000, 'Content must be 50,000 characters or less'),
36
+
37
+ excerpt: z
38
+ .string()
39
+ .max(500, 'Excerpt must be 500 characters or less')
40
+ .optional()
41
+ .nullable(),
42
+
43
+ authorId: z.string().min(1, 'Author ID is required'),
44
+
45
+ authorName: z
46
+ .string()
47
+ .max(255, 'Author name must be 255 characters or less')
48
+ .optional()
49
+ .nullable(),
50
+
51
+ published: z.boolean().default(false),
52
+
53
+ featured: z.boolean().default(false),
54
+
55
+ metaTitle: z
56
+ .string()
57
+ .max(255, 'Meta title must be 255 characters or less')
58
+ .optional()
59
+ .nullable(),
60
+
61
+ metaDescription: z
62
+ .string()
63
+ .max(500, 'Meta description must be 500 characters or less')
64
+ .optional()
65
+ .nullable(),
66
+ });
67
+
68
+ // Schema for updating an existing post
69
+ export const updatePostSchema = createPostSchema.partial().extend({
70
+ id: z.string().length(21, 'Invalid post ID'),
71
+ });
72
+
73
+ // Schema for publishing/unpublishing a post
74
+ export const publishPostSchema = z.object({
75
+ id: z.string().length(21, 'Invalid post ID'),
76
+ published: z.boolean(),
77
+ });
78
+
79
+ // Schema for post query parameters
80
+ export const postQuerySchema = z.object({
81
+ page: z.coerce.number().int().positive().default(1),
82
+ limit: z.coerce.number().int().positive().max(100).default(10),
83
+ published: z
84
+ .string()
85
+ .transform((val) => val === 'true')
86
+ .optional(),
87
+ featured: z
88
+ .string()
89
+ .transform((val) => val === 'true')
90
+ .optional(),
91
+ authorId: z.string().optional(),
92
+ search: z.string().optional(),
93
+ });
94
+
95
+ /**
96
+ * Comment validation schemas
97
+ */
98
+
99
+ // Schema for creating a new comment
100
+ export const createCommentSchema = z.object({
101
+ postId: z.string().length(21, 'Invalid post ID'),
102
+
103
+ content: z
104
+ .string()
105
+ .min(1, 'Comment content is required')
106
+ .max(2000, 'Comment must be 2,000 characters or less')
107
+ .trim(),
108
+
109
+ authorId: z.string().min(1, 'Author ID is required'),
110
+
111
+ authorName: z
112
+ .string()
113
+ .max(255, 'Author name must be 255 characters or less')
114
+ .optional()
115
+ .nullable(),
116
+ });
117
+
118
+ // Schema for updating a comment
119
+ export const updateCommentSchema = z.object({
120
+ id: z.string().length(21, 'Invalid comment ID'),
121
+ content: z
122
+ .string()
123
+ .min(1, 'Comment content is required')
124
+ .max(2000, 'Comment must be 2,000 characters or less')
125
+ .trim(),
126
+ });
127
+
128
+ // Schema for moderating a comment
129
+ export const moderateCommentSchema = z.object({
130
+ id: z.string().length(21, 'Invalid comment ID'),
131
+ approved: z.boolean().optional(),
132
+ flagged: z.boolean().optional(),
133
+ });
134
+
135
+ // Schema for comment query parameters
136
+ export const commentQuerySchema = z.object({
137
+ postId: z.string().length(21, 'Invalid post ID').optional(),
138
+ page: z.coerce.number().int().positive().default(1),
139
+ limit: z.coerce.number().int().positive().max(100).default(20),
140
+ approved: z
141
+ .string()
142
+ .transform((val) => val === 'true')
143
+ .optional(),
144
+ authorId: z.string().optional(),
145
+ });
146
+
147
+ /**
148
+ * Type exports for use in application
149
+ */
150
+ export type CreatePostInput = z.infer<typeof createPostSchema>;
151
+ export type UpdatePostInput = z.infer<typeof updatePostSchema>;
152
+ export type PublishPostInput = z.infer<typeof publishPostSchema>;
153
+ export type PostQueryInput = z.infer<typeof postQuerySchema>;
154
+
155
+ export type CreateCommentInput = z.infer<typeof createCommentSchema>;
156
+ export type UpdateCommentInput = z.infer<typeof updateCommentSchema>;
157
+ export type ModerateCommentInput = z.infer<typeof moderateCommentSchema>;
158
+ export type CommentQueryInput = z.infer<typeof commentQuerySchema>;
@@ -1,63 +1,63 @@
1
- ---
2
- /**
3
- * Base Layout Component
4
- * Demonstrates proper SCSS architecture:
5
- * - No <style> tags in this file
6
- * - All styles imported from external SCSS files
7
- * - Global styles applied via import in the head
8
- */
9
-
10
- import { siteConfig } from '@/lib/config';
11
-
12
- interface Props {
13
- pageTitle?: string | string[];
14
- description?: string;
15
- }
16
-
17
- const {
18
- pageTitle,
19
- description = siteConfig.stackDescription,
20
- } = Astro.props;
21
-
22
- const pageArray = Array.isArray(pageTitle) ? pageTitle : [pageTitle];
23
-
24
- const firstElement = pageArray?.[0];
25
- const shouldSkipFirst =
26
- firstElement === '' ||
27
- firstElement === siteConfig.stackName ||
28
- firstElement?.toLowerCase() === 'index' ||
29
- firstElement?.toLowerCase() === 'home';
30
-
31
- const newPageTitle = (pageArray && pageArray.length > 0 && firstElement)
32
- ? shouldSkipFirst
33
- ? pageArray.filter((a, i) => i !== 0)
34
- : pageArray
35
- : null;
36
-
37
- const title = newPageTitle ? `${newPageTitle.join(' | ')} | ${siteConfig.stackName}` : siteConfig.stackName;
38
- ---
39
-
40
- <!doctype html>
41
- <html lang="en">
42
- <head>
43
- <meta charset="UTF-8" />
44
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
45
- <meta name="description" content={description} />
46
- <meta name="generator" content={Astro.generator} />
47
-
48
- <!-- Favicons -->
49
- <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
50
- <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
51
-
52
- <!-- PWA manifest -->
53
- <link rel="manifest" href="/manifest.webmanifest" />
54
-
55
- <title>{title}</title>
56
-
57
- <!-- Global styles imported from external SCSS file -->
58
- <link rel="stylesheet" href="/src/styles/global.scss" />
59
- </head>
60
- <body>
61
- <slot />
62
- </body>
63
- </html>
1
+ ---
2
+ /**
3
+ * Base Layout Component
4
+ * Demonstrates proper SCSS architecture:
5
+ * - No <style> tags in this file
6
+ * - All styles imported from external SCSS files
7
+ * - Global styles applied via import in the head
8
+ */
9
+
10
+ import { siteConfig } from '@/lib/config';
11
+
12
+ interface Props {
13
+ pageTitle?: string | string[];
14
+ description?: string;
15
+ }
16
+
17
+ const {
18
+ pageTitle,
19
+ description = siteConfig.stackDescription,
20
+ } = Astro.props;
21
+
22
+ const pageArray = Array.isArray(pageTitle) ? pageTitle : [pageTitle];
23
+
24
+ const firstElement = pageArray?.[0];
25
+ const shouldSkipFirst =
26
+ firstElement === '' ||
27
+ firstElement === siteConfig.stackName ||
28
+ firstElement?.toLowerCase() === 'index' ||
29
+ firstElement?.toLowerCase() === 'home';
30
+
31
+ const newPageTitle = (pageArray && pageArray.length > 0 && firstElement)
32
+ ? shouldSkipFirst
33
+ ? pageArray.filter((a, i) => i !== 0)
34
+ : pageArray
35
+ : null;
36
+
37
+ const title = newPageTitle ? `${newPageTitle.join(' | ')} | ${siteConfig.stackName}` : siteConfig.stackName;
38
+ ---
39
+
40
+ <!doctype html>
41
+ <html lang="en">
42
+ <head>
43
+ <meta charset="UTF-8" />
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
45
+ <meta name="description" content={description} />
46
+ <meta name="generator" content={Astro.generator} />
47
+
48
+ <!-- Favicons -->
49
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
50
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
51
+
52
+ <!-- PWA manifest -->
53
+ <link rel="manifest" href="/manifest.webmanifest" />
54
+
55
+ <title>{title}</title>
56
+
57
+ <!-- Global styles imported from external SCSS file -->
58
+ <link rel="stylesheet" href="/src/styles/global.scss" />
59
+ </head>
60
+ <body>
61
+ <slot />
62
+ </body>
63
+ </html>
@@ -1,36 +1,36 @@
1
- /**
2
- * Site Configuration
3
- * Central configuration for the application that will be rendered on screen
4
- */
5
-
6
- /**
7
- * The core stack identifier - change this in ONE place to update everywhere
8
- */
9
- const STACK_SHORT_NAME = 'ATSDC';
10
-
11
- export const siteConfig = {
12
- /**
13
- * Short name for the stack (used in PWA and compact displays)
14
- */
15
- stackShortName: STACK_SHORT_NAME,
16
-
17
- /**
18
- * The full name of the stack/framework (derived from stackShortName)
19
- */
20
- stackName: `${STACK_SHORT_NAME} Stack`,
21
-
22
- /**
23
- * Full description of the stack
24
- */
25
- stackDescription:
26
- 'Full-stack application built with Astro, TypeScript, Drizzle, Clerk, and SCSS',
27
-
28
- /**
29
- * Docs URL
30
- */
31
- docsUrl: 'https://github.com/adarshrkumar/The-ATSDC-Stack',
32
- /**
33
- * GitHub repository URL
34
- */
35
- githubUrl: 'https://github.com/adarshrkumar/The-ATSDC-Stack',
36
- } as const;
1
+ /**
2
+ * Site Configuration
3
+ * Central configuration for the application that will be rendered on screen
4
+ */
5
+
6
+ /**
7
+ * The core stack identifier - change this in ONE place to update everywhere
8
+ */
9
+ const STACK_SHORT_NAME = 'ATSDC';
10
+
11
+ export const siteConfig = {
12
+ /**
13
+ * Short name for the stack (used in PWA and compact displays)
14
+ */
15
+ stackShortName: STACK_SHORT_NAME,
16
+
17
+ /**
18
+ * The full name of the stack/framework (derived from stackShortName)
19
+ */
20
+ stackName: `${STACK_SHORT_NAME} Stack`,
21
+
22
+ /**
23
+ * Full description of the stack
24
+ */
25
+ stackDescription:
26
+ 'Full-stack application built with Astro, TypeScript, Drizzle, Clerk, and SCSS',
27
+
28
+ /**
29
+ * Docs URL
30
+ */
31
+ docsUrl: 'https://github.com/adarshrkumar/The-ATSDC-Stack',
32
+ /**
33
+ * GitHub repository URL
34
+ */
35
+ githubUrl: 'https://github.com/adarshrkumar/The-ATSDC-Stack',
36
+ } as const;