create-ailk 0.31.0 → 0.33.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 (86) hide show
  1. package/README.md +50 -8
  2. package/component-catalog.json +12 -1
  3. package/component-catalog.md +6 -5
  4. package/dist/body-heading.d.ts +13 -0
  5. package/dist/body-heading.js +19 -5
  6. package/dist/cutover.d.ts +6 -1
  7. package/dist/cutover.js +23 -6
  8. package/dist/export-content.d.ts +6 -3
  9. package/dist/export-content.js +79 -10
  10. package/dist/language-import.d.ts +16 -3
  11. package/dist/language-import.js +27 -11
  12. package/dist/lib/import-mdx.d.ts +42 -0
  13. package/dist/lib/import-mdx.js +584 -65
  14. package/dist/lib/render-comparison.d.ts +51 -0
  15. package/dist/lib/render-comparison.js +205 -6
  16. package/dist/mdx-braces.d.ts +73 -0
  17. package/dist/mdx-braces.js +285 -0
  18. package/dist/migrate-content.d.ts +1 -1
  19. package/dist/migrate-content.js +4 -2
  20. package/dist/programmatic.js +2 -8
  21. package/dist/seed-flows.d.ts +1 -1
  22. package/dist/seed-flows.js +9 -4
  23. package/dist/site-manifest.js +6 -38
  24. package/dist/site-roster.d.ts +46 -3
  25. package/dist/site-roster.js +124 -4
  26. package/dist/surfaces.d.ts +8 -0
  27. package/dist/surfaces.js +17 -0
  28. package/dist/update.d.ts +21 -1
  29. package/dist/update.js +142 -11
  30. package/package.json +3 -3
  31. package/templates/.github/workflows/ci.yml +2 -0
  32. package/templates/apps/api/CLAUDE.md +7 -5
  33. package/templates/apps/api/src/lib/__mocks__/prisma.ts +13 -0
  34. package/templates/apps/mcp/.env.example +7 -1
  35. package/templates/apps/web/__tests__/setup.ts +9 -0
  36. package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +28 -0
  37. package/templates/apps/web/app/[locale]/(auth)/verify-email/__tests__/actions.test.ts +94 -0
  38. package/templates/apps/web/app/[locale]/(auth)/verify-email/actions.ts +77 -0
  39. package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +79 -14
  40. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +7 -0
  41. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +7 -0
  42. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +7 -0
  43. package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +3 -0
  44. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +3 -0
  45. package/templates/apps/web/app/[locale]/about/__tests__/about-routes.test.tsx +3 -0
  46. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -0
  47. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +21 -0
  48. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +15 -9
  49. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +38 -105
  50. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.invite.test.tsx +72 -58
  51. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/verify.test.tsx +249 -0
  52. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +164 -20
  53. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/by-result.test.tsx +269 -0
  54. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/score.test.tsx +179 -0
  55. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +108 -40
  56. package/templates/apps/web/app/[locale]/flows/[slug]/verify/page.tsx +153 -0
  57. package/templates/apps/web/app/[locale]/flows/[slug]/verify.actions.ts +151 -0
  58. package/templates/apps/web/app/[locale]/meet/confirmed/BookingHandOff.tsx +1 -1
  59. package/templates/apps/web/app/[locale]/pricing/[slug]/__tests__/page.test.tsx +3 -0
  60. package/templates/apps/web/app/[locale]/pricing/__tests__/offerings.test.tsx +3 -0
  61. package/templates/apps/web/app/[locale]/pricing/__tests__/page.test.tsx +3 -0
  62. package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +3 -0
  63. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +3 -0
  64. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -0
  65. package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +3 -0
  66. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +3 -20
  67. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +2 -9
  68. package/templates/apps/web/auth.ts +22 -6
  69. package/templates/apps/web/lib/__tests__/account-config.test.ts +69 -0
  70. package/templates/apps/web/lib/__tests__/auth-email.test.ts +99 -1
  71. package/templates/apps/web/lib/__tests__/page-chrome.test.ts +3 -0
  72. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +134 -10
  73. package/templates/apps/web/lib/__tests__/telemetry-scrub.test.ts +20 -0
  74. package/templates/apps/web/lib/account-config.ts +52 -14
  75. package/templates/apps/web/lib/auth-email.ts +87 -5
  76. package/templates/apps/web/lib/telemetry-scrub.ts +6 -0
  77. package/templates/apps/web/messages/en.json +26 -1
  78. package/templates/apps/web/src/lib/routes.ts +5 -3
  79. package/templates/database/seed.ts +7 -7
  80. package/templates/package.json +2 -1
  81. package/templates/tools/body-heading.ts +440 -0
  82. package/templates/tools/check-mdx-body.ts +123 -0
  83. package/templates/tools/mdx-braces.ts +303 -0
  84. package/versions.json +48 -48
  85. package/templates/apps/web/app/[locale]/thanks/[slug]/AccountInviteLink.tsx +0 -89
  86. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/AccountInviteLink.test.tsx +0 -104
@@ -88,7 +88,7 @@ function entriesOf(section) {
88
88
  if (!str(raw["question"]) || !str(raw["answer"]))
89
89
  return undefined;
90
90
  entries.push({
91
- key: raw["question"],
91
+ key: JSON.stringify([raw["question"], raw["answer"]]),
92
92
  words: { question: raw["question"], answer: raw["answer"] },
93
93
  kicker: raw["kicker"] !== undefined,
94
94
  });
@@ -113,12 +113,13 @@ export function withLanguage(adapters, options) {
113
113
  let state = "unprobed";
114
114
  let offStatus;
115
115
  const items = new Map();
116
- /** In-run FAQ slug claims, so two questions never share a slug (D2); a term's slug is its key. */
116
+ /** In-run FAQ slug claims, so two items never share a slug (D2, #10778 D2); a term's slug is its key. */
117
117
  const claimed = new Map();
118
118
  const occurrences = [];
119
119
  const homes = [];
120
120
  const kickers = [];
121
121
  const displaced = [];
122
+ const collisions = [];
122
123
  async function call(method, url, body) {
123
124
  for (let retry = 0;; retry++) {
124
125
  let response;
@@ -208,9 +209,11 @@ export function withLanguage(adapters, options) {
208
209
  throw new Error(`language: ${kind}/${known.slug}: ${known.failed}`);
209
210
  if (home && !known.winner.home) {
210
211
  // D3 — the home page outranks a page read before it; an item this run
211
- // created takes its words, and a found one is never written (D4).
212
+ // created takes its words unless they are already its own (#10778 D4),
213
+ // and a found one is never written (D4).
214
+ const rewords = known.created && options.apply && !same(known.winner.words, entry.words);
212
215
  known.winner = { page, words: entry.words, home: true };
213
- if (known.created && options.apply)
216
+ if (rewords)
214
217
  await write(known, entry.words, known.version ?? null);
215
218
  }
216
219
  return known;
@@ -221,15 +224,22 @@ export function withLanguage(adapters, options) {
221
224
  stored = await read(kind, slug);
222
225
  }
223
226
  else {
224
- const first = faqSlug(entry.key);
227
+ const question = entry.words["question"];
228
+ const first = faqSlug(question);
225
229
  let skipped;
230
+ let collided;
226
231
  for (let n = 1;; n++) {
227
232
  const candidate = n === 1 ? first : `${first}-${n}`;
233
+ // Claimed in this run by another pair: the same question answered otherwise
234
+ // is a collision (#10778 D2), another question that slugs alike is not.
228
235
  const holder = claimed.get(candidate);
229
- if (holder !== undefined && holder !== entry.key)
236
+ if (holder !== undefined) {
237
+ if (holder.winner.words["question"] === question)
238
+ collided ??= { slug: candidate, from: holder.winner.page };
230
239
  continue;
240
+ }
231
241
  stored = await read(kind, candidate);
232
- if (stored !== null && stored.words["question"] !== entry.key) {
242
+ if (stored !== null && stored.words["question"] !== question) {
233
243
  skipped ??= candidate;
234
244
  continue;
235
245
  }
@@ -238,9 +248,9 @@ export function withLanguage(adapters, options) {
238
248
  }
239
249
  if (skipped !== undefined)
240
250
  displaced.push({ slug: skipped, next: slug });
251
+ if (collided !== undefined)
252
+ collisions.push({ ...collided, page, next: slug });
241
253
  }
242
- if (kind === "faq")
243
- claimed.set(slug, entry.key);
244
254
  const item = {
245
255
  kind,
246
256
  slug,
@@ -249,6 +259,8 @@ export function withLanguage(adapters, options) {
249
259
  ...(stored !== null && { stored }),
250
260
  };
251
261
  items.set(mapKey, item);
262
+ if (kind === "faq")
263
+ claimed.set(slug, item);
252
264
  if (stored === null)
253
265
  await write(item, entry.words, null);
254
266
  return item;
@@ -355,7 +367,9 @@ export function withLanguage(adapters, options) {
355
367
  const key = `${PREFIX[item.kind]}${item.slug}`;
356
368
  const live = item.created || item.stored.live;
357
369
  if (item.kind === home.kind && item.failed === undefined && live && !home.shown.has(key)) {
358
- topUps.push({ kind: item.kind, slug: item.slug, page: `/${home.slug}` });
370
+ const shown = item.created ? item.winner.words : (item.stored.published ?? {});
371
+ const name = shown[item.kind === "faq" ? "question" : "term"] ?? "";
372
+ topUps.push({ kind: item.kind, slug: item.slug, page: `/${home.slug}`, name });
359
373
  }
360
374
  }
361
375
  }
@@ -371,6 +385,7 @@ export function withLanguage(adapters, options) {
371
385
  topUps,
372
386
  droppedKickers: kickers,
373
387
  displaced,
388
+ collisions,
374
389
  };
375
390
  }
376
391
  return {
@@ -421,13 +436,14 @@ export function languageCountsLine(result, apply) {
421
436
  const { created, unchanged, kept, failed } = result.counts;
422
437
  return `language: ${apply ? "created" : "create"} ${created} · unchanged ${unchanged} · kept ${kept} · failed ${failed}`;
423
438
  }
424
- /** Every line D7 prints, in order: items, counts, render changes, top-ups, dropped kickers, displacements. */
439
+ /** Every line D7 prints, in order: items, counts, render changes, collisions, top-ups, dropped kickers, displacements. */
425
440
  export function languageLines(result, apply) {
426
441
  const verb = (r) => (!apply && r === "created" ? "create" : r);
427
442
  return [
428
443
  ...result.items.map((i) => `language: ${i.kind}/${i.slug}: ${verb(i.result)}${i.reason === undefined ? "" : ` — ${i.reason}`}`),
429
444
  languageCountsLine(result, apply),
430
445
  ...result.renderChanges.map(renderChangeLine),
446
+ ...result.collisions.map((c) => `language: faq/${c.slug} on ${c.page}: not merged — ${c.from} answers the same question otherwise; ${c.page} pins faq/${c.next}`),
431
447
  ...result.topUps.map((t) => `language: ${t.kind}/${t.slug}: now also on ${t.page}`),
432
448
  ...result.droppedKickers.map((k) => `language: faq/${k.slug}: kicker dropped`),
433
449
  ...result.displaced.map((d) => `language: faq/${d.slug}: the workspace holds a different question here; the file's is ${d.next}`),
@@ -32,6 +32,19 @@ export interface MdxAdapterModule {
32
32
  data: Record<string, unknown>;
33
33
  content: string;
34
34
  };
35
+ /**
36
+ * The site-field rule from the adapter's validation package (#10806 D4);
37
+ * without it the editorial walk sends no `siteFields` and no `tags`, and
38
+ * names the keys it would have sent.
39
+ */
40
+ siteFields?: SiteFieldRules;
41
+ }
42
+ /** The validation package's site-field rule, typed structurally. */
43
+ export interface SiteFieldRules {
44
+ refusal: (key: string, value: unknown) => string | null;
45
+ publicPageTags: (tags: readonly unknown[]) => string[];
46
+ maxKeys: number;
47
+ maxBytes: number;
35
48
  }
36
49
  export interface ImportMdxOptions {
37
50
  /** The site's content root (`content/`). */
@@ -88,6 +101,35 @@ interface At {
88
101
  export declare function outcomePath(at: At): string;
89
102
  /** The whole report, one line per page and a final count line. */
90
103
  export declare function formatImportMdxReport(result: ImportMdxResult): string;
104
+ /**
105
+ * A section string's inline HTML as the constrained Markdown the upstream
106
+ * renderers read (#10804 D3): a link with an allowed href becomes
107
+ * `[label](href)`, `<br>` a newline, a block or list item a paragraph break,
108
+ * and every other tag goes, its text kept. A string with no tag comes back
109
+ * byte-identical; the result never holds one. `notCarried` names, sorted,
110
+ * every tag the layout does not keep.
111
+ */
112
+ export declare function inlineHtmlToMarkdown(value: string): {
113
+ text: string;
114
+ notCarried: string[];
115
+ };
116
+ /**
117
+ * Each section with every string in its `data` that holds a tag converted by
118
+ * `inlineHtmlToMarkdown` (#10804 D2); keys and tag-free strings are kept as
119
+ * they are. `notCarried` is the sorted union of the names it did not carry.
120
+ */
121
+ export declare function sectionsWithoutHtml<T>(sections: readonly T[]): {
122
+ sections: T[];
123
+ notCarried: string[];
124
+ };
125
+ /**
126
+ * The first section-string or key rule of the hub's structure write
127
+ * (`checkSectionData`) the layout breaks, as `<message> (sections[i].data<path>)`,
128
+ * or `undefined` (#10804 D5). The depth and size bounds are not restated.
129
+ */
130
+ export declare function layoutRefusal(layout: {
131
+ sections: readonly unknown[];
132
+ }): string | undefined;
91
133
  export declare function importMdx(opts: ImportMdxOptions): Promise<ImportMdxResult>;
92
134
  /**
93
135
  * The site's installed adapter package, resolved from its `apps/web` — never