create-atsdc-stack 1.0.1 → 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 -0
  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 -1138
  46. package/package.json +8 -6
  47. package/app/.astro/settings.json +0 -5
  48. package/app/.astro/types.d.ts +0 -1
@@ -1,230 +1,230 @@
1
- /**
2
- * DOM Manipulation Utilities
3
- * Provides functions for manipulating HTML content using Cheerio
4
- */
5
-
6
- import * as cheerio from 'cheerio';
7
-
8
- /**
9
- * Extract metadata from HTML content
10
- * @param html - The HTML string to parse
11
- * @returns Metadata object
12
- */
13
- export function extractMetadata(html: string) {
14
- const $ = cheerio.load(html);
15
-
16
- return {
17
- title: $('title').text() || $('h1').first().text() || '',
18
- description: $('meta[name="description"]').attr('content') || '',
19
- keywords: $('meta[name="keywords"]').attr('content') || '',
20
- ogTitle: $('meta[property="og:title"]').attr('content') || '',
21
- ogDescription: $('meta[property="og:description"]').attr('content') || '',
22
- ogImage: $('meta[property="og:image"]').attr('content') || '',
23
- twitterCard: $('meta[name="twitter:card"]').attr('content') || '',
24
- canonical: $('link[rel="canonical"]').attr('href') || '',
25
- };
26
- }
27
-
28
- /**
29
- * Extract all links from HTML
30
- * @param html - The HTML string to parse
31
- * @returns Array of link objects
32
- */
33
- export function extractLinks(html: string) {
34
- const $ = cheerio.load(html);
35
- const links: Array<{ text: string; href: string; title?: string }> = [];
36
-
37
- $('a').each((_, element) => {
38
- const $el = $(element);
39
- const href = $el.attr('href');
40
- if (href) {
41
- links.push({
42
- text: $el.text().trim(),
43
- href,
44
- title: $el.attr('title'),
45
- });
46
- }
47
- });
48
-
49
- return links;
50
- }
51
-
52
- /**
53
- * Extract all images from HTML
54
- * @param html - The HTML string to parse
55
- * @returns Array of image objects
56
- */
57
- export function extractImages(html: string) {
58
- const $ = cheerio.load(html);
59
- const images: Array<{ src: string; alt?: string; title?: string }> = [];
60
-
61
- $('img').each((_, element) => {
62
- const $el = $(element);
63
- const src = $el.attr('src');
64
- if (src) {
65
- images.push({
66
- src,
67
- alt: $el.attr('alt'),
68
- title: $el.attr('title'),
69
- });
70
- }
71
- });
72
-
73
- return images;
74
- }
75
-
76
- /**
77
- * Extract headings from HTML
78
- * @param html - The HTML string to parse
79
- * @returns Array of heading objects
80
- */
81
- export function extractHeadings(html: string) {
82
- const $ = cheerio.load(html);
83
- const headings: Array<{ level: number; text: string; id?: string }> = [];
84
-
85
- $('h1, h2, h3, h4, h5, h6').each((_, element) => {
86
- const $el = $(element);
87
- const tagName = $el.prop('tagName')?.toLowerCase();
88
- const level = parseInt(tagName?.replace('h', '') || '1');
89
-
90
- headings.push({
91
- level,
92
- text: $el.text().trim(),
93
- id: $el.attr('id'),
94
- });
95
- });
96
-
97
- return headings;
98
- }
99
-
100
- /**
101
- * Generate table of contents from HTML
102
- * @param html - The HTML string to parse
103
- * @param maxLevel - Maximum heading level to include (default: 3)
104
- * @returns Array of TOC items
105
- */
106
- export function generateTableOfContents(html: string, maxLevel: number = 3) {
107
- const headings = extractHeadings(html);
108
- return headings
109
- .filter((h) => h.level <= maxLevel)
110
- .map((h) => ({
111
- ...h,
112
- slug: h.id || h.text.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, ''),
113
- }));
114
- }
115
-
116
- /**
117
- * Clean HTML content
118
- * Removes scripts, styles, and other potentially dangerous elements
119
- * @param html - The HTML string to clean
120
- * @returns Cleaned HTML string
121
- */
122
- export function cleanHtml(html: string): string {
123
- const $ = cheerio.load(html);
124
-
125
- // Remove dangerous elements
126
- $('script, style, iframe, object, embed').remove();
127
-
128
- // Remove event handlers
129
- $('*').each((_, element) => {
130
- const $el = $(element);
131
- const attrs = $el.attr();
132
-
133
- if (attrs) {
134
- Object.keys(attrs).forEach((attr) => {
135
- if (attr.startsWith('on')) {
136
- $el.removeAttr(attr);
137
- }
138
- });
139
- }
140
- });
141
-
142
- return $.html();
143
- }
144
-
145
- /**
146
- * Extract text content from HTML
147
- * @param html - The HTML string to parse
148
- * @returns Plain text string
149
- */
150
- export function extractTextContent(html: string): string {
151
- const $ = cheerio.load(html);
152
-
153
- // Remove script and style elements
154
- $('script, style').remove();
155
-
156
- return $('body').text().replace(/\s+/g, ' ').trim();
157
- }
158
-
159
- /**
160
- * Add IDs to headings for anchor linking
161
- * @param html - The HTML string to process
162
- * @returns HTML with IDs added to headings
163
- */
164
- export function addHeadingIds(html: string): string {
165
- const $ = cheerio.load(html);
166
-
167
- $('h1, h2, h3, h4, h5, h6').each((_, element) => {
168
- const $el = $(element);
169
-
170
- if (!$el.attr('id')) {
171
- const text = $el.text().trim();
172
- const id = text.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '');
173
- $el.attr('id', id);
174
- }
175
- });
176
-
177
- return $.html();
178
- }
179
-
180
- /**
181
- * Add target="_blank" to external links
182
- * @param html - The HTML string to process
183
- * @param domain - The current domain (optional)
184
- * @returns HTML with external links updated
185
- */
186
- export function addExternalLinkTargets(html: string, domain?: string): string {
187
- const $ = cheerio.load(html);
188
-
189
- $('a').each((_, element) => {
190
- const $el = $(element);
191
- const href = $el.attr('href');
192
-
193
- if (href && (href.startsWith('http://') || href.startsWith('https://'))) {
194
- if (!domain || !href.includes(domain)) {
195
- $el.attr('target', '_blank');
196
- $el.attr('rel', 'noopener noreferrer');
197
- }
198
- }
199
- });
200
-
201
- return $.html();
202
- }
203
-
204
- /**
205
- * Calculate reading time for HTML content
206
- * @param html - The HTML string to analyze
207
- * @param wordsPerMinute - Average reading speed (default: 200)
208
- * @returns Reading time in minutes
209
- */
210
- export function calculateReadingTime(html: string, wordsPerMinute: number = 200): number {
211
- const text = extractTextContent(html);
212
- const wordCount = text.split(/\s+/).length;
213
- return Math.ceil(wordCount / wordsPerMinute);
214
- }
215
-
216
- /**
217
- * Wrap tables in a responsive container
218
- * @param html - The HTML string to process
219
- * @returns HTML with tables wrapped
220
- */
221
- export function wrapTables(html: string): string {
222
- const $ = cheerio.load(html);
223
-
224
- $('table').each((_, element) => {
225
- const $el = $(element);
226
- $el.wrap('<div class="table-wrapper" style="overflow-x: auto;"></div>');
227
- });
228
-
229
- return $.html();
230
- }
1
+ /**
2
+ * DOM Manipulation Utilities
3
+ * Provides functions for manipulating HTML content using Cheerio
4
+ */
5
+
6
+ import * as cheerio from 'cheerio';
7
+
8
+ /**
9
+ * Extract metadata from HTML content
10
+ * @param html - The HTML string to parse
11
+ * @returns Metadata object
12
+ */
13
+ export function extractMetadata(html: string) {
14
+ const $ = cheerio.load(html);
15
+
16
+ return {
17
+ title: $('title').text() || $('h1').first().text() || '',
18
+ description: $('meta[name="description"]').attr('content') || '',
19
+ keywords: $('meta[name="keywords"]').attr('content') || '',
20
+ ogTitle: $('meta[property="og:title"]').attr('content') || '',
21
+ ogDescription: $('meta[property="og:description"]').attr('content') || '',
22
+ ogImage: $('meta[property="og:image"]').attr('content') || '',
23
+ twitterCard: $('meta[name="twitter:card"]').attr('content') || '',
24
+ canonical: $('link[rel="canonical"]').attr('href') || '',
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Extract all links from HTML
30
+ * @param html - The HTML string to parse
31
+ * @returns Array of link objects
32
+ */
33
+ export function extractLinks(html: string) {
34
+ const $ = cheerio.load(html);
35
+ const links: Array<{ text: string; href: string; title?: string }> = [];
36
+
37
+ $('a').each((_, element) => {
38
+ const $el = $(element);
39
+ const href = $el.attr('href');
40
+ if (href) {
41
+ links.push({
42
+ text: $el.text().trim(),
43
+ href,
44
+ title: $el.attr('title'),
45
+ });
46
+ }
47
+ });
48
+
49
+ return links;
50
+ }
51
+
52
+ /**
53
+ * Extract all images from HTML
54
+ * @param html - The HTML string to parse
55
+ * @returns Array of image objects
56
+ */
57
+ export function extractImages(html: string) {
58
+ const $ = cheerio.load(html);
59
+ const images: Array<{ src: string; alt?: string; title?: string }> = [];
60
+
61
+ $('img').each((_, element) => {
62
+ const $el = $(element);
63
+ const src = $el.attr('src');
64
+ if (src) {
65
+ images.push({
66
+ src,
67
+ alt: $el.attr('alt'),
68
+ title: $el.attr('title'),
69
+ });
70
+ }
71
+ });
72
+
73
+ return images;
74
+ }
75
+
76
+ /**
77
+ * Extract headings from HTML
78
+ * @param html - The HTML string to parse
79
+ * @returns Array of heading objects
80
+ */
81
+ export function extractHeadings(html: string) {
82
+ const $ = cheerio.load(html);
83
+ const headings: Array<{ level: number; text: string; id?: string }> = [];
84
+
85
+ $('h1, h2, h3, h4, h5, h6').each((_, element) => {
86
+ const $el = $(element);
87
+ const tagName = $el.prop('tagName')?.toLowerCase();
88
+ const level = parseInt(tagName?.replace('h', '') || '1');
89
+
90
+ headings.push({
91
+ level,
92
+ text: $el.text().trim(),
93
+ id: $el.attr('id'),
94
+ });
95
+ });
96
+
97
+ return headings;
98
+ }
99
+
100
+ /**
101
+ * Generate table of contents from HTML
102
+ * @param html - The HTML string to parse
103
+ * @param maxLevel - Maximum heading level to include (default: 3)
104
+ * @returns Array of TOC items
105
+ */
106
+ export function generateTableOfContents(html: string, maxLevel: number = 3) {
107
+ const headings = extractHeadings(html);
108
+ return headings
109
+ .filter((h) => h.level <= maxLevel)
110
+ .map((h) => ({
111
+ ...h,
112
+ slug: h.id || h.text.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, ''),
113
+ }));
114
+ }
115
+
116
+ /**
117
+ * Clean HTML content
118
+ * Removes scripts, styles, and other potentially dangerous elements
119
+ * @param html - The HTML string to clean
120
+ * @returns Cleaned HTML string
121
+ */
122
+ export function cleanHtml(html: string): string {
123
+ const $ = cheerio.load(html);
124
+
125
+ // Remove dangerous elements
126
+ $('script, style, iframe, object, embed').remove();
127
+
128
+ // Remove event handlers
129
+ $('*').each((_, element) => {
130
+ const $el = $(element);
131
+ const attrs = $el.attr();
132
+
133
+ if (attrs) {
134
+ Object.keys(attrs).forEach((attr) => {
135
+ if (attr.startsWith('on')) {
136
+ $el.removeAttr(attr);
137
+ }
138
+ });
139
+ }
140
+ });
141
+
142
+ return $.html();
143
+ }
144
+
145
+ /**
146
+ * Extract text content from HTML
147
+ * @param html - The HTML string to parse
148
+ * @returns Plain text string
149
+ */
150
+ export function extractTextContent(html: string): string {
151
+ const $ = cheerio.load(html);
152
+
153
+ // Remove script and style elements
154
+ $('script, style').remove();
155
+
156
+ return $('body').text().replace(/\s+/g, ' ').trim();
157
+ }
158
+
159
+ /**
160
+ * Add IDs to headings for anchor linking
161
+ * @param html - The HTML string to process
162
+ * @returns HTML with IDs added to headings
163
+ */
164
+ export function addHeadingIds(html: string): string {
165
+ const $ = cheerio.load(html);
166
+
167
+ $('h1, h2, h3, h4, h5, h6').each((_, element) => {
168
+ const $el = $(element);
169
+
170
+ if (!$el.attr('id')) {
171
+ const text = $el.text().trim();
172
+ const id = text.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '');
173
+ $el.attr('id', id);
174
+ }
175
+ });
176
+
177
+ return $.html();
178
+ }
179
+
180
+ /**
181
+ * Add target="_blank" to external links
182
+ * @param html - The HTML string to process
183
+ * @param domain - The current domain (optional)
184
+ * @returns HTML with external links updated
185
+ */
186
+ export function addExternalLinkTargets(html: string, domain?: string): string {
187
+ const $ = cheerio.load(html);
188
+
189
+ $('a').each((_, element) => {
190
+ const $el = $(element);
191
+ const href = $el.attr('href');
192
+
193
+ if (href && (href.startsWith('http://') || href.startsWith('https://'))) {
194
+ if (!domain || !href.includes(domain)) {
195
+ $el.attr('target', '_blank');
196
+ $el.attr('rel', 'noopener noreferrer');
197
+ }
198
+ }
199
+ });
200
+
201
+ return $.html();
202
+ }
203
+
204
+ /**
205
+ * Calculate reading time for HTML content
206
+ * @param html - The HTML string to analyze
207
+ * @param wordsPerMinute - Average reading speed (default: 200)
208
+ * @returns Reading time in minutes
209
+ */
210
+ export function calculateReadingTime(html: string, wordsPerMinute: number = 200): number {
211
+ const text = extractTextContent(html);
212
+ const wordCount = text.split(/\s+/).length;
213
+ return Math.ceil(wordCount / wordsPerMinute);
214
+ }
215
+
216
+ /**
217
+ * Wrap tables in a responsive container
218
+ * @param html - The HTML string to process
219
+ * @returns HTML with tables wrapped
220
+ */
221
+ export function wrapTables(html: string): string {
222
+ const $ = cheerio.load(html);
223
+
224
+ $('table').each((_, element) => {
225
+ const $el = $(element);
226
+ $el.wrap('<div class="table-wrapper" style="overflow-x: auto;"></div>');
227
+ });
228
+
229
+ return $.html();
230
+ }