@nonext/husk 0.2.3

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 (89) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/LICENSE +166 -0
  3. package/README.md +140 -0
  4. package/api/public-api.md +1070 -0
  5. package/dist/adapter-C_0mhENy.d.ts +320 -0
  6. package/dist/admin/admin.css +5 -0
  7. package/dist/admin/index.d.ts +265 -0
  8. package/dist/admin/index.js +393 -0
  9. package/dist/admin/index.js.map +1 -0
  10. package/dist/admin/tokens.css +597 -0
  11. package/dist/admin/ui.d.ts +3013 -0
  12. package/dist/admin/ui.js +9783 -0
  13. package/dist/admin/ui.js.map +1 -0
  14. package/dist/auth/index.d.ts +4 -0
  15. package/dist/auth/index.js +9 -0
  16. package/dist/auth/index.js.map +1 -0
  17. package/dist/auth-B2gBtHl5.d.ts +28 -0
  18. package/dist/can-Chy7zmx7.d.ts +69 -0
  19. package/dist/chunk-3I6337WJ.js +83 -0
  20. package/dist/chunk-3I6337WJ.js.map +1 -0
  21. package/dist/chunk-4Z6P3XCE.js +62 -0
  22. package/dist/chunk-4Z6P3XCE.js.map +1 -0
  23. package/dist/chunk-6E3ZS5Z5.js +1007 -0
  24. package/dist/chunk-6E3ZS5Z5.js.map +1 -0
  25. package/dist/chunk-77JDRXXU.js +3 -0
  26. package/dist/chunk-77JDRXXU.js.map +1 -0
  27. package/dist/chunk-FHYWJCG6.js +171 -0
  28. package/dist/chunk-FHYWJCG6.js.map +1 -0
  29. package/dist/chunk-JAE7HHMM.js +506 -0
  30. package/dist/chunk-JAE7HHMM.js.map +1 -0
  31. package/dist/chunk-NPDBQBLW.js +68 -0
  32. package/dist/chunk-NPDBQBLW.js.map +1 -0
  33. package/dist/chunk-ORMYARXD.js +6 -0
  34. package/dist/chunk-ORMYARXD.js.map +1 -0
  35. package/dist/chunk-R4O7APIX.js +51 -0
  36. package/dist/chunk-R4O7APIX.js.map +1 -0
  37. package/dist/chunk-ROLKRJWI.js +187 -0
  38. package/dist/chunk-ROLKRJWI.js.map +1 -0
  39. package/dist/chunk-SOTQ3WP5.js +31 -0
  40. package/dist/chunk-SOTQ3WP5.js.map +1 -0
  41. package/dist/chunk-TCFFHTY3.js +97 -0
  42. package/dist/chunk-TCFFHTY3.js.map +1 -0
  43. package/dist/chunk-TS7Y7GEZ.js +7 -0
  44. package/dist/chunk-TS7Y7GEZ.js.map +1 -0
  45. package/dist/chunk-VLGR5PZX.js +3 -0
  46. package/dist/chunk-VLGR5PZX.js.map +1 -0
  47. package/dist/chunk-ZW2PUSVS.js +40 -0
  48. package/dist/chunk-ZW2PUSVS.js.map +1 -0
  49. package/dist/chunk-ZYNBMYJ6.js +392 -0
  50. package/dist/chunk-ZYNBMYJ6.js.map +1 -0
  51. package/dist/cli/nonext-husk.js +3360 -0
  52. package/dist/cli/nonext-husk.js.map +1 -0
  53. package/dist/cli/templates/firestore.rules +393 -0
  54. package/dist/cli/templates/storage.rules +165 -0
  55. package/dist/client/index.d.ts +27 -0
  56. package/dist/client/index.js +15 -0
  57. package/dist/client/index.js.map +1 -0
  58. package/dist/cms-9fSA3uQV.d.ts +158 -0
  59. package/dist/core/index.d.ts +1267 -0
  60. package/dist/core/index.js +12 -0
  61. package/dist/core/index.js.map +1 -0
  62. package/dist/entry-D3kiE8T1.d.ts +163 -0
  63. package/dist/field-Diyf5siF.d.ts +158 -0
  64. package/dist/field-type-D25Vojb0.d.ts +157 -0
  65. package/dist/firebase/index.d.ts +251 -0
  66. package/dist/firebase/index.js +991 -0
  67. package/dist/firebase/index.js.map +1 -0
  68. package/dist/index-5ylaMBUH.d.ts +155 -0
  69. package/dist/index.d.ts +14 -0
  70. package/dist/index.js +17 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/permissions/index.d.ts +101 -0
  73. package/dist/permissions/index.js +7 -0
  74. package/dist/permissions/index.js.map +1 -0
  75. package/dist/registry-CUC25qay.d.ts +59 -0
  76. package/dist/richtext/index.d.ts +85 -0
  77. package/dist/richtext/index.js +127 -0
  78. package/dist/richtext/index.js.map +1 -0
  79. package/dist/schema/index.d.ts +50 -0
  80. package/dist/schema/index.js +5 -0
  81. package/dist/schema/index.js.map +1 -0
  82. package/dist/server/index.d.ts +39 -0
  83. package/dist/server/index.js +25 -0
  84. package/dist/server/index.js.map +1 -0
  85. package/dist/types/index.d.ts +36 -0
  86. package/dist/types/index.js +6 -0
  87. package/dist/types/index.js.map +1 -0
  88. package/dist/write-limits-Djmm6L3R.d.ts +215 -0
  89. package/package.json +136 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/args.ts","../../src/core/errors.ts","../../src/core/list-view.ts","../../src/core/indexes.ts","../../src/cli/rules-template.ts","../../src/cli/doctor.ts","../../src/cli/fs.ts","../../src/cli/load-config.ts","../../src/cli/options.ts","../../src/cli/generate/context.ts","../../src/cli/generate/admin-files.ts","../../src/cli/generate/lib-files.ts","../../src/cli/generate/starter.ts","../../src/cli/generate/project-files.ts","../../src/cli/plan.ts","../../src/cli/project.ts","../../src/cli/prompt.ts","../../src/cli/diff.ts","../../src/cli/report.ts","../../src/cli/nonext-husk.ts"],"names":["readFileSync","join","lines","row","column","MARK","resolve"],"mappings":"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","file":"nonext-husk.js","sourcesContent":["/**\n * Argument parsing for `nonext-husk`, kept away from the process so it can be tested.\n *\n * Deliberately small: a hand-written parser over a closed option list rather than a\n * dependency. The CLI ships inside `@nonext/husk`, and a customer installing a CMS should\n * not also install an argument parser they never chose.\n *\n * Unknown options are an error rather than something to ignore. `--no-media` and\n * `--nomedia` differ by one character, and silently accepting the second would scaffold an\n * app with a media library the developer asked not to have.\n */\n\nexport type Command = \"init\" | \"doctor\" | \"help\" | \"version\"\n\nexport interface ParsedArgs {\n command: Command\n /** Where the customer project is. Relative paths resolve against the process cwd. */\n cwd: string | undefined\n /** Skip every prompt and take the defaults plus whatever flags were passed. */\n yes: boolean\n /** Report what would be written and write nothing. */\n dryRun: boolean\n /** Overwrite a file that exists and differs. Off by default; see `apply.ts`. */\n force: boolean\n projectId: string | undefined\n storageBucket: string | undefined\n adminRoute: string | undefined\n media: boolean | undefined\n auth: boolean | undefined\n emulators: boolean | undefined\n}\n\nexport class ArgumentError extends Error {\n override readonly name = \"ArgumentError\"\n}\n\nconst BOOLEAN_FLAGS = new Set([\n \"yes\",\n \"dry-run\",\n \"force\",\n \"media\",\n \"auth\",\n \"emulators\",\n \"help\",\n \"version\",\n])\n\nconst VALUE_FLAGS = new Set([\"cwd\", \"project-id\", \"storage-bucket\", \"admin-route\"])\n\nfunction base(): ParsedArgs {\n return {\n command: \"help\",\n cwd: undefined,\n yes: false,\n dryRun: false,\n force: false,\n projectId: undefined,\n storageBucket: undefined,\n adminRoute: undefined,\n media: undefined,\n auth: undefined,\n emulators: undefined,\n }\n}\n\n/** `--flag=value`, `--flag value`, `--no-flag`, `-y`. Anything else is refused. */\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\n const parsed = base()\n const flags = new Map<string, string | boolean>()\n let commandSeen = false\n\n for (let index = 0; index < argv.length; index += 1) {\n const token = argv[index] ?? \"\"\n\n if (token === \"-y\") {\n flags.set(\"yes\", true)\n continue\n }\n if (token === \"-h\") {\n flags.set(\"help\", true)\n continue\n }\n\n if (!token.startsWith(\"--\")) {\n if (commandSeen) {\n throw new ArgumentError(`Unexpected argument \"${token}\".`)\n }\n if (token !== \"init\" && token !== \"doctor\" && token !== \"help\") {\n throw new ArgumentError(\n `Unknown command \"${token}\". Expected \"init\", \"doctor\" or \"help\".`,\n )\n }\n parsed.command = token\n commandSeen = true\n continue\n }\n\n const body = token.slice(2)\n const equals = body.indexOf(\"=\")\n const name = equals === -1 ? body : body.slice(0, equals)\n const inlineValue = equals === -1 ? undefined : body.slice(equals + 1)\n\n if (name.startsWith(\"no-\") && BOOLEAN_FLAGS.has(name.slice(3))) {\n if (inlineValue !== undefined) {\n throw new ArgumentError(`--${name} takes no value.`)\n }\n flags.set(name.slice(3), false)\n continue\n }\n\n if (BOOLEAN_FLAGS.has(name)) {\n if (inlineValue !== undefined) {\n throw new ArgumentError(`--${name} takes no value.`)\n }\n flags.set(name, true)\n continue\n }\n\n if (VALUE_FLAGS.has(name)) {\n const value = inlineValue ?? argv[index + 1]\n if (value === undefined || value.startsWith(\"--\")) {\n throw new ArgumentError(`--${name} needs a value.`)\n }\n if (inlineValue === undefined) index += 1\n flags.set(name, value)\n continue\n }\n\n throw new ArgumentError(`Unknown option \"${token}\".`)\n }\n\n if (flags.get(\"version\") === true) return { ...parsed, command: \"version\" }\n if (flags.get(\"help\") === true) return { ...parsed, command: \"help\" }\n\n return {\n ...parsed,\n yes: flags.get(\"yes\") === true,\n dryRun: flags.get(\"dry-run\") === true,\n force: flags.get(\"force\") === true,\n cwd: stringFlag(flags, \"cwd\"),\n projectId: stringFlag(flags, \"project-id\"),\n storageBucket: stringFlag(flags, \"storage-bucket\"),\n adminRoute: stringFlag(flags, \"admin-route\"),\n media: booleanFlag(flags, \"media\"),\n auth: booleanFlag(flags, \"auth\"),\n emulators: booleanFlag(flags, \"emulators\"),\n }\n}\n\nfunction stringFlag(\n flags: ReadonlyMap<string, string | boolean>,\n name: string,\n): string | undefined {\n const value = flags.get(name)\n return typeof value === \"string\" ? value : undefined\n}\n\nfunction booleanFlag(\n flags: ReadonlyMap<string, string | boolean>,\n name: string,\n): boolean | undefined {\n const value = flags.get(name)\n return typeof value === \"boolean\" ? value : undefined\n}\n\nexport const HELP = `nonext-husk - Husk, the Nonext CMS\n\nUsage\n npx nonext-husk init [options] scaffold Husk into this Next.js app\n npx nonext-husk doctor [options] check an existing Husk installation\n\nOptions\n -y, --yes take every default and ask nothing\n --cwd <dir> the project directory (default: the current one)\n --project-id <id> the Firebase project id\n --storage-bucket <b> the Storage bucket (default: <project-id>.firebasestorage.app)\n --admin-route <path> where the admin mounts (default: /admin)\n --no-media leave out the media library\n --no-auth leave out the sign-in screen and the session route\n --no-emulators target a real Firebase project instead of the emulators\n --dry-run list what would be written and write nothing\n --force overwrite files that exist and differ\n -h, --help this text\n --version the installed @nonext/husk version\n`\n","import type { FieldError } from \"../types/entry.js\"\n\n/**\n * A content type, field map, or registry is malformed. Thrown at definition time, which\n * for a code-defined type means at module load, so the mistake surfaces before any data\n * is written.\n */\nexport class SchemaError extends Error {\n override readonly name = \"SchemaError\"\n\n constructor(message: string) {\n super(message)\n }\n}\n\n/** Entry data failed the compiled validator. Carries one error per offending field path. */\nexport class ValidationError extends Error {\n override readonly name = \"ValidationError\"\n readonly errors: readonly FieldError[]\n\n constructor(errors: readonly FieldError[]) {\n super(\n errors.length === 1 && errors[0]\n ? `${errors[0].path}: ${errors[0].message}`\n : `${errors.length} fields failed validation`,\n )\n this.errors = errors\n }\n}\n\n/** A schema write was attempted against a content type that does not accept one. */\nexport class LockedTypeError extends Error {\n override readonly name = \"LockedTypeError\"\n readonly slug: string\n\n constructor(slug: string, reason?: string) {\n super(\n reason ??\n `Content type \"${slug}\" is locked. It is defined in code and cannot be changed through the Schema Builder or the adapter.`,\n )\n this.slug = slug\n }\n}\n\n/** A singleton was asked to hold more than the one entry it is allowed. */\nexport class SingletonError extends Error {\n override readonly name = \"SingletonError\"\n readonly slug: string\n\n constructor(slug: string, message: string) {\n super(message)\n this.slug = slug\n }\n}\n\n/**\n * The caller is not allowed to do this. Thrown by the adapter, from the same `can()` the\n * UI asked before it drew the button and the same matrix firestore.rules enforces.\n *\n * It exists as its own class because \"you may not\" and \"that is malformed\" need different\n * answers: a permission refusal is never fixed by editing the value. Carrying the action\n * and the content type lets a caller say which one without parsing the message.\n */\nexport class PermissionError extends Error {\n override readonly name = \"PermissionError\"\n readonly action: string\n /** Content type slug. Absent for an action that is not scoped to one. */\n readonly contentType: string | undefined\n\n constructor(action: string, contentType?: string, message?: string) {\n super(\n message ??\n (contentType === undefined\n ? `This account may not ${action}.`\n : `This account may not ${action} entries of \"${contentType}\".`),\n )\n this.action = action\n this.contentType = contentType\n }\n}\n\n/**\n * A write was refused for its size, its shape, or its timing rather than its content.\n * See core/write-limits.ts for which of these the security rules enforce as well and\n * which are the adapter's alone.\n */\nexport class WriteLimitError extends Error {\n override readonly name = \"WriteLimitError\"\n readonly code: string\n readonly path: string\n\n constructor(rejection: { code: string; path: string; message: string }) {\n super(rejection.message)\n this.code = rejection.code\n this.path = rejection.path\n }\n}\n","import type { EntryFilter, ListQuery, SortSpec } from \"./adapter.js\"\nimport { normalizeLimit } from \"./adapter.js\"\nimport type { ContentTypeLike } from \"../types/content-type.js\"\nimport type { EntryStatus } from \"../types/entry.js\"\nimport type { FieldDefinition, FieldType } from \"../types/field.js\"\n\nimport { orderedFields } from \"./schema-editor.js\"\n\n/**\n * What a collection list is, derived from a content type.\n *\n * The screen renders this; it never decides it. Columns, the sortable set, the filter\n * controls and the query that reads a page are all computed here from the schema, which is\n * what makes a new content type a data change and not a screen change.\n *\n * Nothing in this file imports React, Firebase or the field registry, so it is domain\n * logic: it runs in the node test project, in the SDK, and in a server component.\n */\n\n/** How a column's cells are typeset. The cell itself never inspects the field type. */\nexport type ColumnAlign = \"text\" | \"numeric\" | \"date\"\n\n/**\n * What a field type contributes to a table.\n *\n * `Record<FieldType, ...>` on purpose: adding a thirteenth field type fails to typecheck\n * until somebody decides whether it fits in a table cell. This is the one place that\n * decision lives, the same way `fieldTypes` is the one place a field type is registered.\n * `\"none\"` means the value cannot be reduced to one readable line, so it gets no column:\n * a rich text document, an uploaded file, a repeater of rows.\n */\nconst COLUMN_ALIGN: Record<FieldType, ColumnAlign | \"none\"> = {\n text: \"text\",\n textarea: \"none\",\n number: \"numeric\",\n boolean: \"text\",\n select: \"text\",\n date: \"date\",\n slug: \"text\",\n richtext: \"none\",\n image: \"none\",\n file: \"none\",\n relation: \"none\",\n repeater: \"none\",\n}\n\n/**\n * What a field type can be filtered by.\n *\n * \"Simple\" means a closed set of values the schema already names, matched by equality.\n * `select` carries its options, and `boolean` has exactly two; both produce a filter\n * control with no free text and a query Firestore answers from one index. Everything else\n * is excluded and the reasons differ: a `text`, `slug` or `richtext` value is open-ended,\n * so equality on it is useless and substring matching is not on offer (see `search`);\n * `number` and `date` want ranges, which need an interval control and a second index axis;\n * `image`, `file` and `relation` are ids, which a reader cannot pick from a dropdown; a\n * `repeater` has no single value at all.\n */\nconst FILTERABLE: readonly FieldType[] = [\"select\", \"boolean\"]\n\nexport type ColumnKind = \"title\" | \"field\" | \"status\" | \"updated\"\n\nexport interface ListColumn {\n /** Field key, or `status` / `updatedAt` for the two metadata columns. */\n key: string\n label: string\n kind: ColumnKind\n align: ColumnAlign\n /**\n * Whether the header offers sorting. Bounded on purpose: every sortable column costs a\n * pair of composite indexes per filter combination, so the list sorts by title, by the\n * type's declared default sort field, and by last update, and not by every column it\n * shows. See `sortableFieldKeys` and core/indexes.ts.\n */\n sortable: boolean\n}\n\nexport interface ListFilterOption {\n value: string\n label: string\n}\n\nexport interface ListFilterDescriptor {\n /** Field key the filter applies to. */\n key: string\n label: string\n options: readonly ListFilterOption[]\n /**\n * `array-contains` for a multi-select, whose stored value is an array; `==` otherwise.\n * The screen passes this straight through, so it never decides an operator.\n */\n op: EntryFilter[\"op\"]\n /** Filter values are strings in the UI; a boolean field converts back on the way out. */\n valueType: \"string\" | \"boolean\"\n}\n\n/** Everything the reader has narrowed the list to, as one serializable value. */\nexport interface ListViewState {\n /** Prefix term for the title field. Empty means no search. */\n search: string\n status: EntryStatus | \"any\"\n /** Field key to selected option value. A key absent means that filter is off. */\n filters: Readonly<Record<string, string>>\n sort: SortSpec\n /** Opaque, from a previous page. The UI stores it and never builds one. */\n cursor: string | null\n}\n\n/** Last touched first: what an editor who just saved something expects to see at the top. */\nexport const FALLBACK_SORT: SortSpec = { field: \"updatedAt\", direction: \"desc\" }\n\nexport function defaultListSort(type: ContentTypeLike): SortSpec {\n return type.defaultSort === undefined\n ? FALLBACK_SORT\n : { field: type.defaultSort.field, direction: type.defaultSort.direction }\n}\n\nexport function initialListState(type: ContentTypeLike): ListViewState {\n return {\n search: \"\",\n status: \"any\",\n filters: {},\n sort: defaultListSort(type),\n cursor: null,\n }\n}\n\n/** A content type can only be searched when it names the field to match against. */\nexport function canSearch(type: ContentTypeLike): boolean {\n return type.titleField !== undefined\n}\n\n/** True while a term is narrowing the list, which is also when sorting is not on offer. */\nexport function isSearching(state: ListViewState): boolean {\n return state.search.trim() !== \"\"\n}\n\n/**\n * The keys a list may sort by: the title, the type's own default sort field, and the last\n * update. Kept small deliberately; see `ListColumn.sortable`.\n */\nexport function sortableFieldKeys(type: ContentTypeLike): readonly string[] {\n const keys = new Set<string>()\n if (type.titleField !== undefined) keys.add(type.titleField)\n if (type.defaultSort !== undefined) keys.add(type.defaultSort.field)\n keys.add(\"updatedAt\")\n return [...keys]\n}\n\n/** How many schema fields get their own column before the row would stop being readable. */\nexport const MAX_FIELD_COLUMNS = 2\n\nexport interface ListColumnOptions {\n maxFieldColumns?: number\n}\n\n/**\n * The columns, in reading order: title, up to two field columns, status, updated.\n *\n * Hidden fields are skipped because the form does not show them either, and the title\n * field is not repeated as a field column. A type with no `titleField` still gets a title\n * column: `entryTitle` names the entry \"Untitled\" and the row remains clickable, which is\n * better than a list whose rows have no handle at all.\n */\nexport function listColumns(\n type: ContentTypeLike,\n { maxFieldColumns = MAX_FIELD_COLUMNS }: ListColumnOptions = {},\n): readonly ListColumn[] {\n const sortable = new Set(sortableFieldKeys(type))\n const titleKey = type.titleField\n const titleField = titleKey === undefined ? undefined : type.fields[titleKey]\n\n const columns: ListColumn[] = [\n {\n key: titleKey ?? \"title\",\n label: titleField?.label ?? \"Title\",\n kind: \"title\",\n align: \"text\",\n sortable: titleKey !== undefined && sortable.has(titleKey),\n },\n ]\n\n for (const { key, field } of orderedFields(type)) {\n if (columns.length > maxFieldColumns) break\n if (key === titleKey || field.hidden === true) continue\n\n const align = COLUMN_ALIGN[field.type]\n if (align === \"none\") continue\n\n columns.push({\n key,\n label: field.label,\n kind: \"field\",\n align,\n sortable: sortable.has(key),\n })\n }\n\n columns.push(\n { key: \"status\", label: \"Status\", kind: \"status\", align: \"text\", sortable: false },\n {\n key: \"updatedAt\",\n label: \"Updated\",\n kind: \"updated\",\n align: \"date\",\n sortable: true,\n },\n )\n\n return columns\n}\n\n/** Whether a field definition can back a filter control. */\nexport function isFilterableField(field: FieldDefinition): boolean {\n return FILTERABLE.includes(field.type)\n}\n\n/**\n * The field filters a content type offers, in schema order.\n *\n * Status is not among them: it is entry metadata rather than a field, every content type\n * has it, and the adapter takes it as its own argument.\n */\nexport function listFilters(type: ContentTypeLike): readonly ListFilterDescriptor[] {\n const descriptors: ListFilterDescriptor[] = []\n\n for (const { key, field } of orderedFields(type)) {\n if (field.hidden === true || !isFilterableField(field)) continue\n\n if (field.type === \"select\") {\n descriptors.push({\n key,\n label: field.label,\n options: field.options.map((option) => ({\n value: option.value,\n label: option.label,\n })),\n // A multi-select stores an array, so equality would only match an entry whose\n // whole array is that one value.\n op: field.multiple === true ? \"array-contains\" : \"==\",\n valueType: \"string\",\n })\n continue\n }\n\n descriptors.push({\n key,\n label: field.label,\n options: [\n { value: \"true\", label: \"Yes\" },\n { value: \"false\", label: \"No\" },\n ],\n op: \"==\",\n valueType: \"boolean\",\n })\n }\n\n return descriptors\n}\n\n/**\n * The active filters as adapter filters.\n *\n * A stored value the schema no longer offers is dropped rather than sent: a filter for a\n * select option that was removed from the content type would return nothing and read as\n * \"there are no entries\".\n */\nexport function listFilterQuery(\n type: ContentTypeLike,\n filters: Readonly<Record<string, string>>,\n): readonly EntryFilter[] {\n const active: EntryFilter[] = []\n\n for (const descriptor of listFilters(type)) {\n const chosen = filters[descriptor.key]\n if (chosen === undefined || chosen === \"\") continue\n if (!descriptor.options.some((option) => option.value === chosen)) continue\n\n active.push({\n field: descriptor.key,\n op: descriptor.op,\n value: descriptor.valueType === \"boolean\" ? chosen === \"true\" : chosen,\n })\n }\n\n return active\n}\n\nexport interface BuildListQueryOptions {\n pageSize?: number\n}\n\n/**\n * The read for one page.\n *\n * The sort is omitted while a search is active rather than sent and ignored: the adapter\n * forces title order for a prefix range, and a query object that still claimed another\n * order would be a lie the caller could act on.\n */\nexport function buildListQuery(\n type: ContentTypeLike,\n state: ListViewState,\n { pageSize }: BuildListQueryOptions = {},\n): ListQuery {\n const term = state.search.trim()\n const searching = term !== \"\" && canSearch(type)\n\n return {\n filters: listFilterQuery(type, state.filters),\n ...(searching ? { search: term } : { sort: state.sort }),\n limit: normalizeLimit(pageSize),\n cursor: state.cursor,\n status: state.status,\n }\n}\n\n/** The same narrowing as a count: no order, no cursor, no page size. */\nexport function buildCountQuery(\n type: ContentTypeLike,\n state: ListViewState,\n): Pick<ListQuery, \"filters\" | \"status\" | \"search\"> {\n const term = state.search.trim()\n\n return {\n filters: listFilterQuery(type, state.filters),\n status: state.status,\n ...(term !== \"\" && canSearch(type) ? { search: term } : {}),\n }\n}\n\n/** Whether anything is narrowing the list, which is what tells an empty result apart. */\nexport function isNarrowed(state: ListViewState): boolean {\n return (\n isSearching(state) ||\n state.status !== \"any\" ||\n Object.values(state.filters).some((value) => value !== \"\")\n )\n}\n\n/**\n * The next sort after a header is clicked.\n *\n * Clicking the sorted column flips it. Clicking another column starts in the direction\n * that column is usually read in: newest first for a date, A to Z for everything else.\n */\nexport function nextSort(\n current: SortSpec,\n column: ListColumn,\n key = column.key,\n): SortSpec {\n if (current.field === key) {\n return { field: key, direction: current.direction === \"asc\" ? \"desc\" : \"asc\" }\n }\n return { field: key, direction: column.align === \"date\" ? \"desc\" : \"asc\" }\n}\n","import { SchemaError } from \"./errors.js\"\nimport { isFilterableField, sortableFieldKeys } from \"./list-view.js\"\nimport type { ContentTypeLike } from \"../types/content-type.js\"\n\n/**\n * The composite indexes a collection list needs, derived from the content types.\n *\n * `.docs/rules/firestore-indexes.md` requires every query to ship with its index, and the\n * list view's queries are generated, so the index entries have to be generated too. A\n * filter on a field plus an ordering on another field is a composite index, and the field\n * paths come from the customer's schema, which no hand-written file can know in advance.\n *\n * Entries of every content type live in a subcollection called `entries`, so one\n * `COLLECTION`-scoped index serves every type that uses the same field path. That is why\n * the result is deduplicated across types rather than emitted per type.\n *\n * What is deliberately not emitted: a query with no filter at all. Ordering by one field\n * with `__name__` as the tiebreaker in the same direction is served by Firestore's\n * automatic single-field indexes, and emitting it would waste an index slot.\n */\n\nexport type IndexOrder = \"ASCENDING\" | \"DESCENDING\"\n\nexport interface FirestoreIndexField {\n fieldPath: string\n order: IndexOrder\n}\n\nexport interface FirestoreIndex {\n collectionGroup: string\n queryScope: \"COLLECTION\"\n fields: readonly FirestoreIndexField[]\n}\n\nexport const ENTRY_COLLECTION_GROUP = \"entries\"\n\n/**\n * Firestore allows 200 composite indexes per database. Passing it is not a warning to be\n * ignored later: the deploy fails. Better to fail here, where the schema that caused it is\n * in hand, than in a customer's deploy log.\n */\nexport const MAX_ENTRY_INDEXES = 200\n\nfunction order(direction: \"asc\" | \"desc\"): IndexOrder {\n return direction === \"asc\" ? \"ASCENDING\" : \"DESCENDING\"\n}\n\n/** Every subset of the filterable fields, since the toolbar lets them combine freely. */\nfunction subsets(keys: readonly string[]): readonly (readonly string[])[] {\n return keys.reduce<readonly (readonly string[])[]>(\n (found, key) => [...found, ...found.map((subset) => [...subset, key])],\n [[]],\n )\n}\n\nfunction filterableKeys(type: ContentTypeLike): readonly string[] {\n return Object.entries(type.fields)\n .filter(([, field]) => field.hidden !== true && isFilterableField(field))\n .map(([key]) => key)\n}\n\nfunction keyOf(index: FirestoreIndex): string {\n return index.fields.map((field) => `${field.fieldPath}:${field.order}`).join(\"|\")\n}\n\n/**\n * Every index the generated list view can ask for, across the given content types.\n *\n * The shape of each query it covers is: an optional `status` equality, zero or more field\n * equalities, then the ordering field and `__name__` in the same direction. A prefix\n * search needs no extra entry: it ranges over the title field and orders by it, which is\n * the ordering already emitted for the title column.\n */\nexport function requiredEntryIndexes(\n types: readonly ContentTypeLike[],\n): readonly FirestoreIndex[] {\n const found = new Map<string, FirestoreIndex>()\n\n for (const type of types) {\n if (type.kind !== \"collection\") continue\n\n for (const filters of subsets(filterableKeys(type))) {\n for (const sortField of sortableFieldKeys(type)) {\n for (const direction of [\"asc\", \"desc\"] as const) {\n for (const withStatus of [true, false]) {\n // No equality at all is an automatic single-field index.\n if (!withStatus && filters.length === 0) continue\n // An equality on the field being ordered by is answered by the same index\n // without the ordering, and Firestore rejects the duplicate field path.\n if (filters.includes(sortField)) continue\n\n const index: FirestoreIndex = {\n collectionGroup: ENTRY_COLLECTION_GROUP,\n queryScope: \"COLLECTION\",\n fields: [\n ...(withStatus\n ? [{ fieldPath: \"status\", order: \"ASCENDING\" as const }]\n : []),\n ...filters.map((key) => ({\n fieldPath: key,\n order: \"ASCENDING\" as const,\n })),\n { fieldPath: sortField, order: order(direction) },\n { fieldPath: \"__name__\", order: order(direction) },\n ],\n }\n\n found.set(keyOf(index), index)\n }\n }\n }\n }\n }\n\n const indexes = [...found.values()].sort((left, right) =>\n keyOf(left).localeCompare(keyOf(right)),\n )\n\n if (indexes.length > MAX_ENTRY_INDEXES) {\n throw new SchemaError(\n `These content types need ${indexes.length} composite indexes on \"entries\", and Firestore allows ${MAX_ENTRY_INDEXES} per database. Reduce the number of filterable fields or sort fields.`,\n )\n }\n\n return indexes\n}\n\n/** Whether a checked-in index file already covers one required index. */\nexport function indexCovered(\n file: { indexes?: readonly FirestoreIndex[] },\n required: FirestoreIndex,\n): boolean {\n return (file.indexes ?? []).some(\n (candidate) =>\n candidate.collectionGroup === required.collectionGroup &&\n keyOf(candidate) === keyOf(required),\n )\n}\n\n/** The required indexes missing from a checked-in index file, for a drift test. */\nexport function missingEntryIndexes(\n file: { indexes?: readonly FirestoreIndex[] },\n types: readonly ContentTypeLike[],\n): readonly FirestoreIndex[] {\n return requiredEntryIndexes(types).filter((required) => !indexCovered(file, required))\n}\n","import { createHash } from \"node:crypto\"\nimport { readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\n\n/**\n * The security rules Husk ships, and the stamp that makes a stale copy detectable.\n *\n * `firestore.rules` and `storage.rules` are not configuration a customer writes. They are\n * the third layer of the same permission model as `can()` and the adapter, they are tested\n * against the emulators in this repository, and a project running an older copy of them is\n * running a permission model the installed package no longer agrees with. That is the\n * failure `doctor` has to be able to see, and it has to see it without deploying anything\n * and without a Firebase project.\n *\n * So every generated rules file carries a stamp naming the checksum of the template it came\n * from. `doctor` recomputes the checksum of the template in the installed package and\n * compares. Three outcomes, all of them honest:\n *\n * - **Stamp matches, body matches.** Current.\n * - **Stamp matches, body differs.** Edited on purpose. Husk says so and says nothing about\n * whether the edit is still correct, because it cannot know.\n * - **Stamp differs, or is absent.** Stale, or never came from Husk at all.\n *\n * What none of this proves is what is *deployed*. The file on disk and the rules Firestore\n * is enforcing are two different things, and only a real project can answer the second.\n * `doctor` says that in as many words rather than implying a green check covers it.\n */\n\nexport type RulesKind = \"firestore\" | \"storage\"\n\nexport interface RulesTemplates {\n read(kind: RulesKind): string\n}\n\nconst FILE_NAME: Record<RulesKind, string> = {\n firestore: \"firestore.rules\",\n storage: \"storage.rules\",\n}\n\n/**\n * The templates as they ship, beside the built CLI.\n *\n * They are copied into `dist/cli/templates` by the build rather than inlined into the\n * bundle, so that the file a customer ends up with is byte for byte the file this\n * repository's rules tests run against. A copy transformed through a string literal would\n * be a second version of the rules that nothing tests.\n */\nexport function createFileTemplates(directory: string): RulesTemplates {\n return {\n read(kind) {\n return readFileSync(join(directory, FILE_NAME[kind]), \"utf8\").replace(/\\r\\n/g, \"\\n\")\n },\n }\n}\n\nexport function checksum(body: string): string {\n return createHash(\"sha256\")\n .update(body.replace(/\\r\\n/g, \"\\n\"))\n .digest(\"hex\")\n .slice(0, 12)\n}\n\nconst STAMP = /husk-template:\\s*(firestore|storage)\\.rules@([0-9a-f]{12})/\n\n/**\n * The stamped file, which is what lands in the customer project.\n *\n * The comment goes above `rules_version` rather than below it, because a rules file has to\n * declare its version before anything else and a comment is not a statement.\n */\nexport function stampRules(kind: RulesKind, body: string): string {\n return `// Husk security rules, generated by nonext-husk init.\n//\n// They are the third layer of the same permission model as can() and the adapter, and they\n// are tested against the emulators in the @nonext/husk repository. Edit them if your project\n// needs more, but do not weaken them: the admin assumes they hold.\n//\n// Deploy with: npx firebase deploy --only ${kind === \"firestore\" ? \"firestore:rules,firestore:indexes\" : \"storage\"}\n//\n// husk-template: ${FILE_NAME[kind]}@${checksum(body)}\n\n${body.replace(/^\\s+/, \"\")}`\n}\n\n/** The checksum a generated rules file was written from, or null when it carries no stamp. */\nexport function readStamp(text: string): { kind: RulesKind; checksum: string } | null {\n const found = STAMP.exec(text)\n if (found === null) return null\n return { kind: found[1] as RulesKind, checksum: found[2] ?? \"\" }\n}\n","import { missingEntryIndexes } from \"../core/indexes.js\"\nimport type { FirestoreIndex } from \"../core/indexes.js\"\nimport type { ContentTypeLike } from \"../types/content-type.js\"\n\nimport type { ProjectFs } from \"./fs.js\"\nimport { checksum, readStamp } from \"./rules-template.js\"\nimport type { RulesTemplates } from \"./rules-template.js\"\n\n/**\n * `nonext-husk doctor`: the questions a Husk installation can be asked without deploying\n * anything, and an honest account of the ones it cannot.\n *\n * The distinction matters more here than anywhere else in the CLI. A checker that prints a\n * green line for something it never looked at is worse than no checker: the developer now\n * has evidence for a belief that is false. So every check returns one of three states, and\n * `unchecked` is a first-class result with a reason attached, not a silent pass.\n *\n * What it genuinely proves, on the files alone:\n *\n * - The config is there, loads, and the installed package matches the one it was generated\n * with.\n * - The rules on disk came from this version of Husk, rather than from an older one.\n * - Every composite index the generated list views can ask for is in the index file.\n * - The Firebase config points at the three files, so a deploy would carry them.\n *\n * What it cannot prove without a real Firebase project, stated rather than implied:\n *\n * - That the rules and indexes on disk have been **deployed**. The file and the thing\n * Firestore is enforcing are two different artefacts, and only the project can be asked\n * which one it has.\n * - That the `schemaVersion` on stored entries matches what the code declares today. That\n * is a Firestore read, which needs credentials and a migration run, not a file.\n * - That the Storage bucket exists, or that Authentication is switched on for the project.\n */\n\nexport type CheckStatus = \"ok\" | \"problem\" | \"unchecked\"\n\nexport interface Check {\n name: string\n status: CheckStatus\n detail: string\n /** What to do about it. Present on a problem, and on an unchecked item that has a path. */\n fix?: string\n}\n\nexport interface HuskConfigSummary {\n generatedWith: string\n adminRoute: string\n projectId: string\n media: boolean\n auth: boolean\n emulators: boolean\n}\n\nexport interface DoctorInput {\n fs: ProjectFs\n templates: RulesTemplates\n /** The version of `@nonext/husk` actually installed in the project. */\n installedVersion: string\n /** The config summary, or the reason it could not be read. */\n config: HuskConfigSummary | { error: string }\n /** The code-defined content types, or the reason they could not be loaded. */\n contentTypes: readonly ContentTypeLike[] | { error: string }\n}\n\nexport function runChecks(input: DoctorInput): readonly Check[] {\n return [\n configCheck(input),\n versionCheck(input),\n rulesCheck(input, \"firestore\", \"firestore.rules\"),\n ...(shouldCheckStorage(input) ? [rulesCheck(input, \"storage\", \"storage.rules\")] : []),\n indexCoverageCheck(input),\n firebaseWiringCheck(input),\n environmentCheck(input),\n ...undeployableChecks(),\n ]\n}\n\nexport function exitCode(checks: readonly Check[]): number {\n return checks.some((check) => check.status === \"problem\") ? 1 : 0\n}\n\nfunction shouldCheckStorage(input: DoctorInput): boolean {\n return \"error\" in input.config ? input.fs.exists(\"storage.rules\") : input.config.media\n}\n\nfunction configCheck(input: DoctorInput): Check {\n if (!input.fs.exists(\"cms.config.ts\")) {\n return {\n name: \"config\",\n status: \"problem\",\n detail: \"cms.config.ts is missing.\",\n fix: \"Run `npx nonext-husk init` in this project.\",\n }\n }\n if (\"error\" in input.config) {\n return {\n name: \"config\",\n status: \"problem\",\n detail: `cms.config.ts could not be read: ${input.config.error}`,\n fix: \"Check that cms.config.ts exports `huskConfig` and `codeContentTypes`.\",\n }\n }\n\n return {\n name: \"config\",\n status: \"ok\",\n detail: `admin at ${input.config.adminRoute}, project ${input.config.projectId}, media ${input.config.media ? \"on\" : \"off\"}, auth ${input.config.auth ? \"on\" : \"off\"}.`,\n }\n}\n\n/**\n * Version skew: the `@nonext/husk` in node_modules against the one the project was\n * scaffolded with.\n *\n * On 0.x the minor version is where breaking changes go, which the package's own versioning\n * note says in as many words, so a differing minor is a real problem rather than a nag. A\n * differing patch is worth saying and is not a failure.\n */\nfunction versionCheck(input: DoctorInput): Check {\n if (\"error\" in input.config) {\n return {\n name: \"version skew\",\n status: \"unchecked\",\n detail: \"The config could not be read, so there is nothing to compare against.\",\n }\n }\n\n const generated = input.config.generatedWith\n const installed = input.installedVersion\n\n if (generated === installed) {\n return { name: \"version skew\", status: \"ok\", detail: `@nonext/husk ${installed}.` }\n }\n\n const breaking = breakingApart(generated, installed)\n\n return {\n name: \"version skew\",\n status: breaking ? \"problem\" : \"ok\",\n detail: `Scaffolded with ${generated}, running ${installed}.`,\n fix: breaking\n ? \"Read the CHANGELOG for the versions in between. Generated files under app/ and lib/husk/ may need updating by hand, and `npx nonext-husk init` will show a diff for each of them rather than overwriting your work.\"\n : \"Patch releases do not change the generated files.\",\n }\n}\n\nfunction breakingApart(left: string, right: string): boolean {\n const [leftMajor = \"\", leftMinor = \"\"] = left.split(\".\")\n const [rightMajor = \"\", rightMinor = \"\"] = right.split(\".\")\n if (leftMajor !== rightMajor) return true\n // 0.x: the minor is the breaking slot. See packages/cms/README.md, Versioning.\n return leftMajor === \"0\" && leftMinor !== rightMinor\n}\n\n/**\n * A stale rules file: one that came from an older `@nonext/husk` than the installed one.\n *\n * Detected from the stamp `init` writes into the file, compared against the checksum of the\n * template in the installed package. No deploy, no project, no network.\n */\nfunction rulesCheck(\n input: DoctorInput,\n kind: \"firestore\" | \"storage\",\n path: string,\n): Check {\n const name = `${path}`\n\n if (!input.fs.exists(path)) {\n return {\n name,\n status: \"problem\",\n detail: `${path} is missing, so nothing is protecting this project's data.`,\n fix: \"Run `npx nonext-husk init` to write it, then deploy it.\",\n }\n }\n\n const text = input.fs.read(path)\n const shipped = input.templates.read(kind)\n const expected = checksum(shipped)\n const stamp = readStamp(text)\n\n if (stamp === null) {\n return {\n name,\n status: \"problem\",\n detail: `${path} carries no Husk stamp, so it did not come from this tool and cannot be compared with the rules the installed package expects.`,\n fix: `Compare it with the rules in node_modules/@nonext/husk/dist/cli/templates/${path}, or let \\`npx nonext-husk init --force\\` replace it.`,\n }\n }\n\n if (stamp.checksum !== expected) {\n return {\n name,\n status: \"problem\",\n detail: `${path} was generated by an older @nonext/husk (template ${stamp.checksum}, installed ${expected}). The admin assumes the newer rules.`,\n fix: `Run \\`npx nonext-husk init --force\\` to rewrite it, then \\`npx firebase deploy --only ${kind === \"firestore\" ? \"firestore:rules\" : \"storage\"}\\`.`,\n }\n }\n\n // The stamp is current. Whether the body still is, is a separate question: a developer is\n // allowed to add rules for their own collections, and Husk has no way to judge whether an\n // edit is still safe.\n const edited = text !== stampedShape(text, shipped)\n\n return {\n name,\n status: \"ok\",\n detail: edited\n ? `Current template (${expected}), with local edits. Husk does not check whether those edits still satisfy the admin.`\n : `Current template (${expected}).`,\n }\n}\n\n/** The file as it would look with the shipped body, keeping whatever header it has. */\nfunction stampedShape(existing: string, shipped: string): string {\n const marker = existing.indexOf(\"rules_version\")\n return marker === -1 ? shipped : existing.slice(0, marker) + shipped.trimStart()\n}\n\n/**\n * Index coverage: every composite index the generated list views can ask for.\n *\n * The collection list builds its queries from the schema, so the indexes those queries need\n * come from the schema too. A missing one is not a warning that resolves itself; it is a\n * query that throws `FAILED_PRECONDITION` the first time an editor touches that filter, in\n * production, with a link to a console the editor cannot use.\n */\nfunction indexCoverageCheck(input: DoctorInput): Check {\n const name = \"index coverage\"\n\n if (\"error\" in input.contentTypes) {\n return {\n name,\n status: \"unchecked\",\n detail: `The content types could not be loaded: ${input.contentTypes.error}`,\n fix: \"Without them there is no way to know which composite indexes the list views need.\",\n }\n }\n if (!input.fs.exists(\"firestore.indexes.json\")) {\n return {\n name,\n status: \"problem\",\n detail: \"firestore.indexes.json is missing.\",\n fix: \"Run `npx nonext-husk init` to write it.\",\n }\n }\n\n let file: { indexes?: readonly FirestoreIndex[] }\n try {\n file = JSON.parse(input.fs.read(\"firestore.indexes.json\")) as {\n indexes?: readonly FirestoreIndex[]\n }\n } catch {\n return {\n name,\n status: \"problem\",\n detail: \"firestore.indexes.json is not valid JSON.\",\n fix: \"Fix the file, or delete it and run `npx nonext-husk init`.\",\n }\n }\n\n const missing = missingEntryIndexes(file, input.contentTypes)\n if (missing.length === 0) {\n return {\n name,\n status: \"ok\",\n detail: `${String((file.indexes ?? []).length)} composite index(es) cover every query the list views generate.`,\n }\n }\n\n const described = missing\n .slice(0, 5)\n .map((index) =>\n index.fields.map((field) => `${field.fieldPath} ${field.order}`).join(\", \"),\n )\n .map((line) => ` - entries: ${line}`)\n .join(\"\\n\")\n\n return {\n name,\n status: \"problem\",\n detail: `${String(missing.length)} composite index(es) are missing from firestore.indexes.json:\\n${described}${missing.length > 5 ? `\\n - ... and ${String(missing.length - 5)} more` : \"\"}`,\n fix: \"Add the entries above to firestore.indexes.json, then `npx firebase deploy --only firestore:indexes`.\",\n }\n}\n\nfunction firebaseWiringCheck(input: DoctorInput): Check {\n const name = \"firebase.json\"\n\n if (!input.fs.exists(\"firebase.json\")) {\n return {\n name,\n status: \"problem\",\n detail:\n \"firebase.json is missing, so there is nothing to deploy the rules or the indexes with.\",\n fix: \"Run `npx nonext-husk init`.\",\n }\n }\n\n let config: Record<string, unknown>\n try {\n config = JSON.parse(input.fs.read(\"firebase.json\")) as Record<string, unknown>\n } catch {\n return {\n name,\n status: \"problem\",\n detail: \"firebase.json is not valid JSON.\",\n fix: \"Fix the file by hand.\",\n }\n }\n\n const firestore = config[\"firestore\"] as Record<string, unknown> | undefined\n const storage = config[\"storage\"] as Record<string, unknown> | undefined\n const wantsStorage = shouldCheckStorage(input)\n\n const missing = [\n firestore?.[\"rules\"] === \"firestore.rules\" ? null : \"firestore.rules\",\n firestore?.[\"indexes\"] === \"firestore.indexes.json\" ? null : \"firestore.indexes.json\",\n !wantsStorage || storage?.[\"rules\"] === \"storage.rules\" ? null : \"storage.rules\",\n ].filter((entry): entry is string => entry !== null)\n\n return missing.length === 0\n ? { name, status: \"ok\", detail: \"Points at the rules and the index file.\" }\n : {\n name,\n status: \"problem\",\n detail: `firebase.json does not point at ${missing.join(\", \")}, so a deploy would not carry ${missing.length === 1 ? \"it\" : \"them\"}.`,\n fix: \"Add the missing entries to firebase.json.\",\n }\n}\n\n/**\n * The environment variable that is not obvious.\n *\n * `FIREBASE_AUTH_EMULATOR_HOST` is read on the server. Without it the token verifier\n * refuses to be constructed, the app falls back to the one that trusts nothing, and every\n * admin route redirects to a sign-in screen that sign-in never leaves. It looks exactly\n * like a broken login and it is a missing line in a file.\n */\nfunction environmentCheck(input: DoctorInput): Check {\n const name = \"environment\"\n\n if (\"error\" in input.config) {\n return {\n name,\n status: \"unchecked\",\n detail:\n \"The config could not be read, so there is no way to know whether this project targets the emulators.\",\n }\n }\n if (!input.config.emulators) {\n return {\n name,\n status: \"ok\",\n detail: \"This project does not target the Firebase emulators.\",\n }\n }\n if (!input.fs.exists(\".env.local\")) {\n return {\n name,\n status: \"problem\",\n detail:\n \".env.local is missing, so FIREBASE_AUTH_EMULATOR_HOST is not set and every admin route will redirect to sign-in.\",\n fix: \"Run `npx nonext-husk init` to write it.\",\n }\n }\n\n const text = input.fs.read(\".env.local\")\n const missing = [\n \"FIREBASE_AUTH_EMULATOR_HOST\",\n \"NEXT_PUBLIC_FIREBASE_PROJECT_ID\",\n ].filter((key) => !new RegExp(`^\\\\s*${key}\\\\s*=\\\\s*\\\\S`, \"m\").test(text))\n\n return missing.length === 0\n ? { name, status: \"ok\", detail: \".env.local carries the emulator environment.\" }\n : {\n name,\n status: \"problem\",\n detail: `.env.local is missing ${missing.join(\" and \")}.`,\n fix: \"Add the missing lines. Without FIREBASE_AUTH_EMULATOR_HOST the server verifier trusts nothing and the admin is unreachable.\",\n }\n}\n\n/** The things a file on disk cannot answer. Reported, never quietly passed. */\nfunction undeployableChecks(): readonly Check[] {\n return [\n {\n name: \"rules deployment\",\n status: \"unchecked\",\n detail:\n \"Whether the rules on disk are the rules Firestore and Storage are enforcing is a property of the Firebase project, not of this directory.\",\n fix: \"Run `npx firebase deploy --only firestore:rules,firestore:indexes,storage` and check the console.\",\n },\n {\n name: \"stored schema versions\",\n status: \"unchecked\",\n detail:\n \"Whether stored entries carry the schemaVersion the code declares today needs a Firestore read, credentials, and the migration runner.\",\n fix: \"Open the Developer area in the admin, which reads it from the project you are signed in to.\",\n },\n ]\n}\n","import {\n mkdirSync,\n readFileSync,\n readdirSync,\n renameSync,\n statSync,\n writeFileSync,\n} from \"node:fs\"\nimport { dirname, join, posix, relative, resolve, sep } from \"node:path\"\n\n/**\n * The filesystem as the CLI sees it: four reads and two writes, over POSIX-style relative\n * paths rooted at the customer project.\n *\n * It is an interface rather than direct `node:fs` calls so that the parts that decide what\n * to write, what already exists and what conflicts can be tested without a temp directory.\n * Those decisions are the whole product here: a test that has to create real files to ask\n * \"would this overwrite something\" tends to check one case and stop.\n *\n * Paths are always relative to the project root and always use `/`, including on Windows.\n * The node implementation is the only place that knows about `\\`.\n */\nexport interface ProjectFs {\n exists(path: string): boolean\n isDirectory(path: string): boolean\n read(path: string): string\n list(path: string): readonly string[]\n write(path: string, contents: string): void\n move(from: string, to: string): void\n}\n\nexport function createNodeFs(root: string): ProjectFs {\n const absolute = (path: string): string => resolve(root, ...path.split(\"/\"))\n\n return {\n exists(path) {\n try {\n statSync(absolute(path))\n return true\n } catch {\n return false\n }\n },\n isDirectory(path) {\n try {\n return statSync(absolute(path)).isDirectory()\n } catch {\n return false\n }\n },\n read(path) {\n // Normalized on read so a file written on Windows and a template built here compare\n // as equal text rather than as two encodings of the same lines.\n return readFileSync(absolute(path), \"utf8\").replace(/\\r\\n/g, \"\\n\")\n },\n list(path) {\n try {\n return readdirSync(absolute(path)).sort()\n } catch {\n return []\n }\n },\n write(path, contents) {\n const target = absolute(path)\n mkdirSync(dirname(target), { recursive: true })\n writeFileSync(target, contents, \"utf8\")\n },\n move(from, to) {\n const target = absolute(to)\n mkdirSync(dirname(target), { recursive: true })\n renameSync(absolute(from), target)\n },\n }\n}\n\n/** A project-relative POSIX path from an absolute one. Windows separators included. */\nexport function toProjectPath(root: string, absolutePath: string): string {\n return relative(root, absolutePath).split(sep).join(\"/\")\n}\n\n/**\n * The import specifier that gets from one generated module to another.\n *\n * Generated files import each other by relative path rather than through the `@/*` alias,\n * because the alias is optional in `create-next-app` and a scaffold that silently depends\n * on an answer the developer gave to a different tool is a scaffold that breaks for half\n * its users.\n */\nexport function importSpecifier(fromFile: string, toFile: string): string {\n const from = posix.dirname(fromFile)\n const to = toFile.replace(/\\.tsx?$/, \"\")\n const relativePath = posix.relative(from, to)\n return relativePath.startsWith(\".\") ? relativePath : `./${relativePath}`\n}\n\n/** `join` for the project-relative POSIX paths everything in this folder speaks. */\nexport function projectJoin(...parts: readonly string[]): string {\n return posix.join(...parts.filter((part) => part.length > 0))\n}\n\n/** Absolute path, for the few places that have to hand one to another process. */\nexport function absolutePath(root: string, path: string): string {\n return join(root, ...path.split(\"/\"))\n}\n","import { existsSync, readFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { pathToFileURL } from \"node:url\"\n\nimport type { ContentTypeLike } from \"../types/content-type.js\"\n\nimport type { HuskConfigSummary } from \"./doctor.js\"\n\n/**\n * Reading `cms.config.ts` from outside the app that owns it.\n *\n * `doctor` needs two things out of that file: the scaffold's own record of itself, and the\n * content types, because the composite indexes a list view needs are derived from the\n * schema and nothing else can supply them.\n *\n * It is imported rather than parsed. Node has erased TypeScript types on `import` since\n * 22.18, the file is inside the project it belongs to so `@nonext/husk` resolves from that\n * project's `node_modules`, and importing it means the content types `doctor` reasons about\n * are the same objects the admin builds its registry from rather than a regex's opinion of\n * them.\n *\n * The cost is a real dependency on the runtime, so both halves fail softly and say why. An\n * older Node, a config that throws on import, a config that exports something unexpected:\n * each returns an error string, and `doctor` prints the affected check as `unchecked` with\n * that reason attached instead of passing it.\n */\n\ninterface ConfigModule {\n huskConfig?: unknown\n codeContentTypes?: unknown\n}\n\nexport interface LoadedConfig {\n config: HuskConfigSummary | { error: string }\n contentTypes: readonly ContentTypeLike[] | { error: string }\n}\n\nexport async function loadHuskConfig(root: string): Promise<LoadedConfig> {\n const path = join(root, \"cms.config.ts\")\n if (!existsSync(path)) {\n const reason = \"cms.config.ts is not in this directory.\"\n return { config: { error: reason }, contentTypes: { error: reason } }\n }\n\n let module: ConfigModule\n try {\n // A cache buster, so a second run in the same process (the tests do this) sees the file\n // as it is now rather than as it was.\n module = (await import(\n `${pathToFileURL(path).href}?at=${String(Date.now())}`\n )) as ConfigModule\n } catch (thrown) {\n const reason = describe(thrown)\n return { config: { error: reason }, contentTypes: { error: reason } }\n }\n\n return {\n config: summarize(module.huskConfig),\n contentTypes: contentTypesOf(module.codeContentTypes),\n }\n}\n\nfunction describe(thrown: unknown): string {\n const message = thrown instanceof Error ? thrown.message : String(thrown)\n return message.includes(\"Unknown file extension\") ||\n message.includes(\"ERR_UNKNOWN_FILE_EXTENSION\")\n ? `this Node cannot import a TypeScript file (${process.version}). Node 22.18 or newer erases types on import; below that, run doctor with --experimental-strip-types.`\n : message\n}\n\nfunction summarize(value: unknown): HuskConfigSummary | { error: string } {\n if (value === null || typeof value !== \"object\") {\n return { error: \"cms.config.ts does not export `huskConfig`.\" }\n }\n\n const record = value as Record<string, unknown>\n const strings = [\"generatedWith\", \"adminRoute\", \"projectId\"] as const\n const booleans = [\"media\", \"auth\", \"emulators\"] as const\n\n for (const key of strings) {\n if (typeof record[key] !== \"string\") {\n return { error: `huskConfig.${key} is missing or is not a string.` }\n }\n }\n for (const key of booleans) {\n if (typeof record[key] !== \"boolean\") {\n return { error: `huskConfig.${key} is missing or is not a boolean.` }\n }\n }\n\n return {\n generatedWith: record[\"generatedWith\"] as string,\n adminRoute: record[\"adminRoute\"] as string,\n projectId: record[\"projectId\"] as string,\n media: record[\"media\"] as boolean,\n auth: record[\"auth\"] as boolean,\n emulators: record[\"emulators\"] as boolean,\n }\n}\n\nfunction contentTypesOf(value: unknown): readonly ContentTypeLike[] | { error: string } {\n if (!Array.isArray(value)) {\n return { error: \"cms.config.ts does not export a `codeContentTypes` array.\" }\n }\n\n const invalid = value.filter(\n (entry: unknown) =>\n entry === null ||\n typeof entry !== \"object\" ||\n typeof (entry as { slug?: unknown }).slug !== \"string\" ||\n typeof (entry as { fields?: unknown }).fields !== \"object\",\n )\n if (invalid.length > 0) {\n return { error: \"codeContentTypes holds something that is not a content type.\" }\n }\n\n return value as readonly ContentTypeLike[]\n}\n\n/**\n * The `@nonext/husk` the project actually has installed.\n *\n * Read from the project's own `node_modules` rather than from the running CLI, because\n * those can differ: `npx nonext-husk` will happily fetch a newer package than the one the\n * app is built against, and version skew between the two is one of the things `doctor`\n * exists to name.\n */\nexport function installedVersion(root: string, fallback: string): string {\n const manifest = join(root, \"node_modules\", \"@nonext\", \"cms\", \"package.json\")\n try {\n const parsed = JSON.parse(readFileSync(manifest, \"utf8\")) as { version?: unknown }\n return typeof parsed.version === \"string\" ? parsed.version : fallback\n } catch {\n return fallback\n }\n}\n","import { ArgumentError } from \"./args.js\"\nimport type { ParsedArgs } from \"./args.js\"\n\n/**\n * The answers `init` scaffolds from, and the validation that stands between a prompt and a\n * generated file.\n *\n * Every field here changes what lands on disk, so every one of them is validated before a\n * single file is written. A project id with a slash in it would be a path traversal; an\n * admin route with a space in it would be a route segment Next cannot match; a route that\n * collides with `api` would shadow the session handler this same run generates.\n */\n\nexport interface InitOptions {\n /** Firebase project id. Also the emulator project id. */\n projectId: string\n /** Firebase Storage bucket. Only used when the media library is on. */\n storageBucket: string\n /** Where the admin mounts, leading slash, no trailing slash. */\n adminRoute: string\n /** Generate the media library route, the uploader wiring and `storage.rules`. */\n media: boolean\n /** Generate the sign-in screen and the session route handler. */\n auth: boolean\n /** Point the generated Firebase clients at the Emulator Suite. */\n emulators: boolean\n}\n\nexport const DEFAULT_ADMIN_ROUTE = \"/admin\"\n\n/**\n * Route segments the generated app already owns, so the admin may not be mounted on one.\n *\n * `api` carries the session route this run writes. The others are Next's own file\n * conventions at the top of `app/`, and a route group cannot contain a segment that is one\n * of them without the framework treating it as that convention.\n */\nconst RESERVED_SEGMENTS = new Set([\"api\", \"_next\", \"favicon.ico\"])\n\nexport function normalizeAdminRoute(input: string): string {\n const trimmed = input.trim()\n const withSlash = trimmed.startsWith(\"/\") ? trimmed : `/${trimmed}`\n const withoutTrailing = withSlash.length > 1 ? withSlash.replace(/\\/+$/, \"\") : withSlash\n\n if (withoutTrailing === \"/\") {\n throw new ArgumentError(\n 'The admin route cannot be \"/\". The website lives there; pick something like \"/admin\".',\n )\n }\n if (!/^(\\/[a-z0-9][a-z0-9-]*)+$/.test(withoutTrailing)) {\n throw new ArgumentError(\n `\"${input}\" is not a usable admin route. Use lower-case segments of letters, digits and hyphens, for example \"/admin\" or \"/studio/cms\".`,\n )\n }\n for (const segment of withoutTrailing.slice(1).split(\"/\")) {\n if (RESERVED_SEGMENTS.has(segment)) {\n throw new ArgumentError(\n `\"${segment}\" is a reserved route segment in a Next app. Pick another admin route.`,\n )\n }\n }\n\n return withoutTrailing\n}\n\nexport function validateProjectId(input: string): string {\n const trimmed = input.trim()\n // Firebase's own rule, which is also what keeps this value safe to interpolate into a\n // generated file and into a `.firebaserc`.\n if (!/^[a-z][a-z0-9-]{3,29}$/.test(trimmed)) {\n throw new ArgumentError(\n `\"${input}\" is not a Firebase project id. Ids are 4 to 30 characters, start with a letter, and contain lower-case letters, digits and hyphens.`,\n )\n }\n return trimmed\n}\n\nexport function validateStorageBucket(input: string): string {\n const trimmed = input.trim()\n if (!/^[a-z0-9][a-z0-9._-]{2,221}$/.test(trimmed)) {\n throw new ArgumentError(`\"${input}\" is not a Storage bucket name.`)\n }\n return trimmed\n}\n\nexport function defaultStorageBucket(projectId: string): string {\n return `${projectId}.firebasestorage.app`\n}\n\n/** The non-interactive path: flags plus defaults, with nothing left to ask. */\nexport function optionsFromArgs(args: ParsedArgs): InitOptions {\n const projectId = validateProjectId(args.projectId ?? \"husk-dev-project\")\n return {\n projectId,\n storageBucket:\n args.storageBucket === undefined\n ? defaultStorageBucket(projectId)\n : validateStorageBucket(args.storageBucket),\n adminRoute: normalizeAdminRoute(args.adminRoute ?? DEFAULT_ADMIN_ROUTE),\n media: args.media ?? true,\n auth: args.auth ?? true,\n emulators: args.emulators ?? true,\n }\n}\n\n/** The admin route as path segments, for building directories. */\nexport function adminSegments(adminRoute: string): readonly string[] {\n return adminRoute.slice(1).split(\"/\")\n}\n","import { importSpecifier, projectJoin } from \"../fs.js\"\nimport { adminSegments } from \"../options.js\"\nimport type { InitOptions } from \"../options.js\"\nimport type { NextProject } from \"../project.js\"\n\n/**\n * Everything a template needs to know, computed once.\n *\n * The directory layout is derived rather than hard-coded because two answers move it: an\n * app may use `src/`, and the admin route is the developer's to choose. A template that\n * wrote `\"../../lib/session\"` would be correct for `/admin` in a project without `src/` and\n * wrong for everything else, so templates ask `specifier()` instead of spelling a path.\n */\nexport interface GenerateContext {\n options: InitOptions\n project: NextProject\n /** `app/(husk)`: the admin's route group, which is where its root layout lives. */\n huskDir: string\n /** `app/(husk)/admin`, or deeper when the admin route has more than one segment. */\n adminDir: string\n /** `app/(husk)/admin/(shell)`: everything behind the session guard. */\n shellDir: string\n /** `app/(site)`: the website's route group after init has moved it there. */\n siteDir: string\n /** `lib/husk`: the four files that hold this app's half of the wiring. */\n libDir: string\n /** The `@nonext/husk` version this scaffold was generated with. */\n version: string\n}\n\nexport function createContext(\n options: InitOptions,\n project: NextProject,\n version: string,\n): GenerateContext {\n const huskDir = projectJoin(project.appDir, \"(husk)\")\n const adminDir = projectJoin(huskDir, ...adminSegments(options.adminRoute))\n\n return {\n options,\n project,\n huskDir,\n adminDir,\n shellDir: projectJoin(adminDir, \"(shell)\"),\n siteDir: projectJoin(project.appDir, \"(site)\"),\n libDir: projectJoin(project.libDir, \"husk\"),\n version,\n }\n}\n\n/** The import specifier from one generated file to another, both project-relative. */\nexport function specifier(fromFile: string, toFile: string): string {\n return importSpecifier(fromFile, toFile)\n}\n","import { projectJoin } from \"../fs.js\"\n\nimport { specifier } from \"./context.js\"\nimport type { GenerateContext } from \"./context.js\"\n\n/**\n * The admin routes.\n *\n * Every screen here is the package's. What is generated is the thin host half: the session\n * check on the server, the adapter and the live registry in the browser, and the answers\n * only an app can give, such as where \"back\" goes after a delete and how this app signs a\n * user out.\n *\n * Nothing in this file names a content type. One route serves every collection, every\n * singleton, create and edit alike, because the screens are generated from the registry.\n * That is the whole claim of a schema-driven CMS, and it is why adding a content type later\n * needs no new route and no second run of `init`.\n *\n * Two structural decisions are load-bearing and easy to undo by accident:\n *\n * 1. **The admin has its own root layout**, inside the `(husk)` route group. It renders its\n * own `<html>` and `<body>`, so the website's stylesheet, fonts and providers never\n * reach it and nothing it loads leaks back out. Next renders one root layout per\n * request, so the two never coexist in a document.\n * 2. **`@nonext/husk/admin` and `@nonext/husk/admin/ui` are different entries.** The first is\n * server-safe; the second carries React hooks. A server component that imports from the\n * second fails at request time, not at build time.\n */\n\nexport function adminFiles(context: GenerateContext): Record<string, string> {\n const { huskDir, shellDir, adminDir, project, options } = context\n\n const notFound = projectJoin(huskDir, \"admin-not-found.tsx\")\n const routeError = projectJoin(huskDir, \"admin-route-error.tsx\")\n const chrome = projectJoin(huskDir, \"admin-chrome.tsx\")\n const provider = projectJoin(huskDir, \"husk-provider.tsx\")\n const shell = projectJoin(huskDir, \"live-admin-shell.tsx\")\n const session = projectJoin(context.libDir, \"session.ts\")\n const metadata = projectJoin(context.libDir, \"admin-metadata.ts\")\n const firebaseClient = projectJoin(context.libDir, \"firebase-client.ts\")\n const config = \"cms.config.ts\"\n\n const contentDir = projectJoin(shellDir, \"content\", \"[type]\")\n const contentRoute = projectJoin(contentDir, \"content-route.tsx\")\n\n const files: Record<string, string> = {\n [projectJoin(huskDir, \"layout.tsx\")]: adminRootLayout(),\n [notFound]: adminNotFound(options.adminRoute),\n [routeError]: adminRouteErrorBody(options.adminRoute),\n [provider]: huskProvider(context, {\n config: specifier(provider, config),\n firebaseClient: specifier(provider, firebaseClient),\n }),\n [shell]: liveAdminShell(specifier(shell, provider)),\n [chrome]: adminChrome(context, {\n provider: specifier(chrome, provider),\n shell: specifier(chrome, shell),\n accountMenu: specifier(chrome, projectJoin(shellDir, \"account-menu.tsx\")),\n navigationGuard: specifier(chrome, projectJoin(shellDir, \"navigation-guard.tsx\")),\n }),\n [projectJoin(huskDir, \"error.tsx\")]: boundary(\n specifier(projectJoin(huskDir, \"error.tsx\"), routeError),\n \"Admin\",\n \"The boundary above the shell layout. A segment's own error.tsx does not catch a throw\\n * in that segment's layout, and the shell layout is where the session is read.\",\n ),\n [projectJoin(huskDir, \"not-found.tsx\")]: huskNotFound(context, {\n session: specifier(projectJoin(huskDir, \"not-found.tsx\"), session),\n chrome: specifier(projectJoin(huskDir, \"not-found.tsx\"), chrome),\n notFound: specifier(projectJoin(huskDir, \"not-found.tsx\"), notFound),\n }),\n\n [projectJoin(shellDir, \"layout.tsx\")]: shellLayout(context, {\n session: specifier(projectJoin(shellDir, \"layout.tsx\"), session),\n chrome: specifier(projectJoin(shellDir, \"layout.tsx\"), chrome),\n }),\n [projectJoin(shellDir, \"page.tsx\")]: dashboardPage(context, {\n session: specifier(projectJoin(shellDir, \"page.tsx\"), session),\n metadata: specifier(projectJoin(shellDir, \"page.tsx\"), metadata),\n panel: specifier(\n projectJoin(shellDir, \"page.tsx\"),\n projectJoin(shellDir, \"dashboard-panel.tsx\"),\n ),\n }),\n [projectJoin(shellDir, \"dashboard-panel.tsx\")]: dashboardPanel(\n specifier(projectJoin(shellDir, \"dashboard-panel.tsx\"), provider),\n ),\n [projectJoin(shellDir, \"account-menu.tsx\")]: accountMenu(\n context,\n specifier(projectJoin(shellDir, \"account-menu.tsx\"), firebaseClient),\n ),\n [projectJoin(shellDir, \"navigation-guard.tsx\")]: navigationGuard(),\n [projectJoin(shellDir, \"error.tsx\")]: boundary(\n specifier(projectJoin(shellDir, \"error.tsx\"), routeError),\n \"Admin\",\n \"The boundary for every route under the shell that has no closer one. It renders inside\\n * the shell layout, so the sidebar survives the failure and the reader still has somewhere\\n * to go.\",\n ),\n [projectJoin(shellDir, \"[...missing]\", \"page.tsx\")]: catchAll(\n specifier(projectJoin(shellDir, \"[...missing]\", \"page.tsx\"), notFound),\n ),\n\n [projectJoin(contentDir, \"page.tsx\")]: contentListPage(context, {\n session: specifier(projectJoin(contentDir, \"page.tsx\"), session),\n metadata: specifier(projectJoin(contentDir, \"page.tsx\"), metadata),\n route: specifier(projectJoin(contentDir, \"page.tsx\"), contentRoute),\n }),\n [contentRoute]: contentRouteFile(context, {\n notFound: specifier(contentRoute, notFound),\n provider: specifier(contentRoute, provider),\n collection: specifier(\n contentRoute,\n projectJoin(contentDir, \"collection-panel.tsx\"),\n ),\n entry: specifier(contentRoute, projectJoin(contentDir, \"entry-panel.tsx\")),\n }),\n [projectJoin(contentDir, \"collection-panel.tsx\")]: collectionPanel(\n context,\n specifier(projectJoin(contentDir, \"collection-panel.tsx\"), provider),\n ),\n [projectJoin(contentDir, \"entry-panel.tsx\")]: entryPanel(context, {\n provider: specifier(projectJoin(contentDir, \"entry-panel.tsx\"), provider),\n firebaseClient: specifier(\n projectJoin(contentDir, \"entry-panel.tsx\"),\n firebaseClient,\n ),\n }),\n [projectJoin(contentDir, \"error.tsx\")]: boundary(\n specifier(projectJoin(contentDir, \"error.tsx\"), routeError),\n \"Content\",\n \"The boundary for one content type's list and entry screens. A throw here takes down the\\n * content area and nothing else.\",\n ),\n [projectJoin(contentDir, \"new\", \"page.tsx\")]: contentCreatePage(context, {\n session: specifier(projectJoin(contentDir, \"new\", \"page.tsx\"), session),\n metadata: specifier(projectJoin(contentDir, \"new\", \"page.tsx\"), metadata),\n route: specifier(projectJoin(contentDir, \"new\", \"page.tsx\"), contentRoute),\n }),\n [projectJoin(contentDir, \"[entry]\", \"page.tsx\")]: contentEditPage(context, {\n session: specifier(projectJoin(contentDir, \"[entry]\", \"page.tsx\"), session),\n metadata: specifier(projectJoin(contentDir, \"[entry]\", \"page.tsx\"), metadata),\n route: specifier(projectJoin(contentDir, \"[entry]\", \"page.tsx\"), contentRoute),\n }),\n\n [projectJoin(shellDir, \"developer\", \"[[...section]]\", \"page.tsx\")]: developerPage(\n context,\n {\n session: specifier(\n projectJoin(shellDir, \"developer\", \"[[...section]]\", \"page.tsx\"),\n session,\n ),\n metadata: specifier(\n projectJoin(shellDir, \"developer\", \"[[...section]]\", \"page.tsx\"),\n metadata,\n ),\n config: specifier(\n projectJoin(shellDir, \"developer\", \"[[...section]]\", \"page.tsx\"),\n config,\n ),\n },\n ),\n [projectJoin(shellDir, \"developer\", \"error.tsx\")]: boundary(\n specifier(projectJoin(shellDir, \"developer\", \"error.tsx\"), routeError),\n \"Developer\",\n \"The boundary for the Developer area, including the Schema Builder. A malformed stored\\n * content type throws while it renders, and that must not take down the one route that\\n * would let an admin fix it.\",\n ),\n [projectJoin(shellDir, \"developer\", \"content-types\", \"page.tsx\")]: contentTypesPage(\n context,\n {\n session: specifier(\n projectJoin(shellDir, \"developer\", \"content-types\", \"page.tsx\"),\n session,\n ),\n metadata: specifier(\n projectJoin(shellDir, \"developer\", \"content-types\", \"page.tsx\"),\n metadata,\n ),\n panel: specifier(\n projectJoin(shellDir, \"developer\", \"content-types\", \"page.tsx\"),\n projectJoin(shellDir, \"developer\", \"content-types\", \"content-types-panel.tsx\"),\n ),\n },\n ),\n [projectJoin(shellDir, \"developer\", \"content-types\", \"content-types-panel.tsx\")]:\n contentTypesPanel(\n context,\n specifier(\n projectJoin(shellDir, \"developer\", \"content-types\", \"content-types-panel.tsx\"),\n provider,\n ),\n ),\n [projectJoin(shellDir, \"developer\", \"content-types\", \"[slug]\", \"page.tsx\")]:\n contentTypePage(context, {\n session: specifier(\n projectJoin(shellDir, \"developer\", \"content-types\", \"[slug]\", \"page.tsx\"),\n session,\n ),\n metadata: specifier(\n projectJoin(shellDir, \"developer\", \"content-types\", \"[slug]\", \"page.tsx\"),\n metadata,\n ),\n panel: specifier(\n projectJoin(shellDir, \"developer\", \"content-types\", \"[slug]\", \"page.tsx\"),\n projectJoin(\n shellDir,\n \"developer\",\n \"content-types\",\n \"[slug]\",\n \"content-type-panel.tsx\",\n ),\n ),\n }),\n [projectJoin(\n shellDir,\n \"developer\",\n \"content-types\",\n \"[slug]\",\n \"content-type-panel.tsx\",\n )]: contentTypePanel(context, {\n provider: specifier(\n projectJoin(\n shellDir,\n \"developer\",\n \"content-types\",\n \"[slug]\",\n \"content-type-panel.tsx\",\n ),\n provider,\n ),\n notFound: specifier(\n projectJoin(\n shellDir,\n \"developer\",\n \"content-types\",\n \"[slug]\",\n \"content-type-panel.tsx\",\n ),\n notFound,\n ),\n }),\n\n [projectJoin(project.appDir, \"global-error.tsx\")]: globalError(),\n }\n\n if (options.media) {\n const mediaDir = projectJoin(shellDir, \"media\")\n files[projectJoin(mediaDir, \"page.tsx\")] = mediaPage(context, {\n session: specifier(projectJoin(mediaDir, \"page.tsx\"), session),\n metadata: specifier(projectJoin(mediaDir, \"page.tsx\"), metadata),\n panel: specifier(\n projectJoin(mediaDir, \"page.tsx\"),\n projectJoin(mediaDir, \"media-panel.tsx\"),\n ),\n })\n files[projectJoin(mediaDir, \"media-panel.tsx\")] = mediaPanel(\n specifier(projectJoin(mediaDir, \"media-panel.tsx\"), provider),\n )\n files[projectJoin(mediaDir, \"error.tsx\")] = boundary(\n specifier(projectJoin(mediaDir, \"error.tsx\"), routeError),\n \"Media\",\n \"The boundary for the media library. Uploads and Storage reads fail in ways entry screens\\n * do not, and a failed thumbnail scan must not blank the rest of the admin.\",\n )\n }\n\n if (options.auth) {\n const loginDir = projectJoin(adminDir, \"login\")\n files[projectJoin(loginDir, \"page.tsx\")] = loginPage(\n specifier(\n projectJoin(loginDir, \"page.tsx\"),\n projectJoin(loginDir, \"login-panel.tsx\"),\n ),\n )\n files[projectJoin(loginDir, \"login-panel.tsx\")] = loginPanel(\n context,\n specifier(projectJoin(loginDir, \"login-panel.tsx\"), firebaseClient),\n )\n files[projectJoin(loginDir, \"error.tsx\")] = boundary(\n specifier(projectJoin(loginDir, \"error.tsx\"), routeError),\n \"Sign in\",\n \"The boundary for the sign-in screen. It sits outside the shell, so it has no sidebar to\\n * fall back on and the link back to the admin is the only way out.\",\n )\n files[projectJoin(project.appDir, \"api\", \"husk\", \"session\", \"route.ts\")] =\n sessionRoute(context)\n }\n\n return files\n}\n\nfunction adminRootLayout(): string {\n return `import \"@nonext/husk/admin.css\"\n\nimport type { Metadata } from \"next\"\nimport { Inter } from \"next/font/google\"\n\nconst inter = Inter({\n subsets: [\"latin\"],\n variable: \"--font-inter\",\n display: \"swap\",\n})\n\nexport const metadata: Metadata = {\n title: { default: \"Husk\", template: \"%s | Husk\" },\n}\n\n/**\n * How this app mounts Husk: a second root layout, one stylesheet import, one font.\n *\n * It is a root layout, not a nested one, so the admin renders its own \\`<html>\\` and\n * \\`<body>\\` and never inherits the website's stylesheet, fonts, providers or body classes,\n * and nothing the admin loads can leak back out to the website. Next picks one root layout\n * per request, so the two never coexist in a document. This is why your own root layout\n * lives under \\`(site)\\`.\n *\n * The font is wired here rather than inside the package because \\`next/font\\` is a\n * build-time transform in this app; the package only asks for the \\`--font-inter\\` variable\n * its token layer reads.\n */\nexport default function HuskRootLayout({ children }: { children: React.ReactNode }) {\n return (\n <html lang=\"en\" className={inter.variable}>\n <body className=\"font-sans antialiased\">{children}</body>\n </html>\n )\n}\n`\n}\n\nfunction adminNotFound(adminRoute: string): string {\n return `import { NotFoundScreen } from \"@nonext/husk/admin/ui\"\nimport Link from \"next/link\"\n\n/** The one 404 body in the admin, so the catch-all route and the boundary cannot drift. */\nexport function AdminNotFoundScreen() {\n return (\n <NotFoundScreen>\n <Link href=\"${adminRoute}\" className=\"text-body-sm text-text-primary underline\">\n Back to the dashboard\n </Link>\n </NotFoundScreen>\n )\n}\n`\n}\n\nfunction adminRouteErrorBody(adminRoute: string): string {\n return `\"use client\"\n\nimport { RouteError } from \"@nonext/husk/admin/ui\"\nimport Link from \"next/link\"\n\n/**\n * The one error-boundary body in the admin, so every \\`error.tsx\\` apologises the same way.\n *\n * Each route segment still gets its own \\`error.tsx\\`. That is what decides how much of the\n * screen a failure takes down: a throw inside the media library replaces the media library\n * and leaves the sidebar and every other route reachable. One boundary at the top would\n * have replaced the whole admin with an apology.\n */\nexport function AdminRouteError({\n error,\n reset,\n area,\n}: {\n error: Error & { digest?: string }\n reset: () => void\n /** Names the part of the admin that failed, so the screen says where it happened. */\n area: string\n}) {\n return (\n <RouteError error={error} reset={reset} title={area}>\n <Link href=\"${adminRoute}\" className=\"text-body-sm text-text-primary underline\">\n Back to the dashboard\n </Link>\n </RouteError>\n )\n}\n`\n}\n\nfunction huskProvider(\n context: GenerateContext,\n imports: { config: string; firebaseClient: string },\n): string {\n const { media } = context.options\n\n return `\"use client\"\n\nimport { fieldTypes, useLiveRegistry } from \"@nonext/husk/admin/ui\"\n${media ? 'import type { MediaUploader } from \"@nonext/husk/admin/ui\"\\n' : \"\"}import { createRegistryStore } from \"@nonext/husk/core\"\nimport type { CmsAdapter, Registry } from \"@nonext/husk/core\"\nimport { createFirestoreAdapter${media ? \", createMediaUploader\" : \"\"} } from \"@nonext/husk/firebase\"\nimport type { AuthUser } from \"@nonext/husk/types\"\nimport { createContext, useContext, useMemo, useState } from \"react\"\nimport type { ReactNode } from \"react\"\n\nimport { codeContentTypes } from \"${imports.config}\"\nimport { huskFirestore${media ? \", huskStorage\" : \"\"} } from \"${imports.firebaseClient}\"\n\n/**\n * One adapter, one ${media ? \"uploader, one \" : \"\"}live registry for the whole admin.\n *\n * The registry is fed by a Firestore listener over \\`schemas\\`, which is what puts a content\n * type created in the Schema Builder into the sidebar, with working routes, without a\n * rebuild and without a reload. Code-defined types come from cms.config.ts and always win a\n * slug conflict.\n *\n * It is a host concern rather than a package one because only a host can hold the Firebase\n * handle. The package supplies the store and the hook; this is the wiring.\n */\n\nexport interface HuskContextValue {\n registry: Registry\n adapter: CmsAdapter\n${media ? \" uploader: MediaUploader\\n\" : \"\"} user: AuthUser\n /** False until the first schemas snapshot has arrived. */\n synced: boolean\n /** A dead schema listener, surfaced so a stale admin does not look like a working one. */\n liveError: Error | null\n}\n\nconst HuskContext = createContext<HuskContextValue | null>(null)\n\nexport function HuskProvider({ user, children }: { user: AuthUser; children: ReactNode }) {\n // Created once per mount. The code half never changes for the lifetime of the process;\n // only the database half moves, which is exactly what the store is for.\n const [store] = useState(() => createRegistryStore(codeContentTypes))\n\n const adapter = useMemo(\n () =>\n createFirestoreAdapter({\n db: huskFirestore(),\n // A getter, not a value: the adapter has to see the registry as it is now, or a\n // write against a type created a moment ago would be refused as unknown.\n registry: () => store.registry,\n currentUser: () => user,\n // The admin declares its session, so a read of a content type this account may not\n // read is refused by the adapter and not only hidden by the sidebar.\n session: () => user,\n // Without the field types the adapter falls back to the default converters and\n // each field type's own validate never runs on the write path.\n fieldTypes,\n }),\n [store, user],\n )\n\n // Memoized, or the listener would be closed and reopened on every render.\n const watch = useMemo(() => adapter.schemas.watch, [adapter])\n const { registry, synced, error } = useLiveRegistry(store, watch)\n${\n media\n ? `\n const uploader = useMemo(\n () =>\n createMediaUploader({\n storage: huskStorage(),\n media: adapter.media,\n currentUser: () => user,\n }),\n [adapter, user],\n )\n`\n : \"\"\n}\n const value = useMemo(\n () => ({ registry, adapter,${media ? \" uploader,\" : \"\"} user, synced, liveError: error }),\n [registry, adapter,${media ? \" uploader,\" : \"\"} user, synced, error],\n )\n\n return <HuskContext.Provider value={value}>{children}</HuskContext.Provider>\n}\n\n/** Everything an admin screen needs. Only callable under \\`HuskProvider\\`. */\nexport function useHusk(): HuskContextValue {\n const value = useContext(HuskContext)\n if (value === null) {\n throw new Error(\"useHusk was called outside HuskProvider.\")\n }\n return value\n}\n\n/** Names a content type for a usage list. Falls back to the slug for an unknown one. */\nexport function useContentTypeLabel(): (slug: string) => string {\n const { registry } = useHusk()\n return (slug) => registry.get(slug)?.type.label ?? slug\n}\n`\n}\n\nfunction liveAdminShell(providerImport: string): string {\n return `\"use client\"\n\nimport { buildAdminNavigation } from \"@nonext/husk/admin\"\nimport { AdminShell } from \"@nonext/husk/admin/ui\"\nimport type { AuthUser } from \"@nonext/husk/types\"\nimport { useMemo } from \"react\"\nimport type { ReactNode } from \"react\"\n\nimport { useHusk } from \"${providerImport}\"\n\n/**\n * The shell, with a sidebar that follows the live registry.\n *\n * The navigation is computed in the browser rather than on the server, because a\n * database-defined content type exists only in Firestore and the server has no Firebase\n * session to read \\`schemas\\` with. Same \\`buildAdminNavigation\\`, same \\`can()\\`, over a\n * registry that is a subscription.\n */\nexport function LiveAdminShell({\n user,\n accountMenu,\n children,\n}: {\n user: AuthUser\n accountMenu: ReactNode\n children: ReactNode\n}) {\n const { registry } = useHusk()\n const navigation = useMemo(\n () => buildAdminNavigation({ registry, user }),\n [registry, user],\n )\n\n return (\n <AdminShell navigation={navigation} user={user} accountMenu={accountMenu}>\n {children}\n </AdminShell>\n )\n}\n`\n}\n\nfunction adminChrome(\n _context: GenerateContext,\n imports: {\n provider: string\n shell: string\n accountMenu: string\n navigationGuard: string\n },\n): string {\n return `import { ErrorCapture, ToastProvider } from \"@nonext/husk/admin/ui\"\nimport type { AuthUser } from \"@nonext/husk/types\"\n\nimport { AccountMenu } from \"${imports.accountMenu}\"\nimport { NavigationGuard } from \"${imports.navigationGuard}\"\nimport { HuskProvider } from \"${imports.provider}\"\nimport { LiveAdminShell } from \"${imports.shell}\"\n\n/**\n * The admin chrome: the adapter, the live registry, the sidebar and the account menu around\n * a screen.\n *\n * It is a component rather than only a layout because two entries need it. The shell layout\n * wraps every admin route in it, and the not-found boundary wraps itself in it, and a\n * boundary cannot reuse a layout it did not render under.\n */\nexport function AdminChrome({\n user,\n children,\n}: {\n user: AuthUser\n children: React.ReactNode\n}) {\n return (\n <HuskProvider user={user}>\n <LiveAdminShell user={user} accountMenu={<AccountMenu />}>\n {/* One toast stack for the whole admin. A screen raises a toast through useToast\n from three levels down; threading the state up is how stacks get duplicated. */}\n <ToastProvider>\n {/* What a React error boundary cannot see: a rejected promise. An adapter call\n whose catch somebody forgets would otherwise fail into a console line. */}\n <ErrorCapture />\n {/* Around every screen rather than inside one, because the navigation it has to\n intercept is the sidebar, which lives out here. */}\n <NavigationGuard>{children}</NavigationGuard>\n </ToastProvider>\n </LiveAdminShell>\n </HuskProvider>\n )\n}\n`\n}\n\nfunction boundary(bodyImport: string, area: string, why: string): string {\n return `\"use client\"\n\nimport { AdminRouteError } from \"${bodyImport}\"\n\n/**\n * ${why}\n */\nexport default function Boundary({\n error,\n reset,\n}: {\n error: Error & { digest?: string }\n reset: () => void\n}) {\n return <AdminRouteError error={error} reset={reset} area=\"${area}\" />\n}\n`\n}\n\nfunction huskNotFound(\n context: GenerateContext,\n imports: { session: string; chrome: string; notFound: string },\n): string {\n return `import type { Metadata } from \"next\"\n\nimport { adminAccess } from \"${imports.session}\"\nimport { AdminChrome } from \"${imports.chrome}\"\nimport { AdminNotFoundScreen } from \"${imports.notFound}\"\n\nexport const metadata: Metadata = { title: \"Not found\" }\n\n/**\n * The boundary for a \\`notFound()\\` thrown anywhere under the admin.\n *\n * It draws the chrome itself rather than relying on the shell layout, because a boundary\n * does not render under a layout it did not itself enter. A visitor with no session gets\n * the bare screen: a 404 must not become a second way to find out what a project contains.\n */\nexport default async function HuskNotFound() {\n const access = await adminAccess(\"${context.options.adminRoute}\")\n if (access.outcome !== \"allow\") return <AdminNotFoundScreen />\n\n return (\n <AdminChrome user={access.user}>\n <AdminNotFoundScreen />\n </AdminChrome>\n )\n}\n`\n}\n\nfunction shellLayout(\n context: GenerateContext,\n imports: { session: string; chrome: string },\n): string {\n return `import { redirect } from \"next/navigation\"\n\nimport { adminAccess } from \"${imports.session}\"\nimport { AdminChrome } from \"${imports.chrome}\"\n\n/**\n * Everything behind the guard renders inside the shell. The sign-in screen sits outside\n * this route group on purpose: a sidebar on a sign-in page would offer navigation to\n * someone who has no session.\n */\nexport default async function ShellLayout({ children }: { children: React.ReactNode }) {\n const access = await adminAccess(\"${context.options.adminRoute}\")\n if (access.outcome === \"sign-in\") redirect(access.location)\n\n return <AdminChrome user={access.user}>{children}</AdminChrome>\n}\n`\n}\n\nfunction dashboardPage(\n context: GenerateContext,\n imports: { session: string; metadata: string; panel: string },\n): string {\n return `import { UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport type { Metadata } from \"next\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { adminAccess } from \"${imports.session}\"\n\nimport { DashboardPanel } from \"${imports.panel}\"\n\nexport function generateMetadata(): Promise<Metadata> {\n return adminMetadata(\"${context.options.adminRoute}\")\n}\n\n/**\n * The dashboard. It resolves who the visitor is and hands that to the client panel, which\n * owns the adapter. The screen itself is generated from the registry, so this file names no\n * content type and no figure.\n */\nexport default async function DashboardPage() {\n const access = await adminAccess(\"${context.options.adminRoute}\")\n if (access.outcome === \"sign-in\") redirect(access.location)\n if (access.outcome === \"unauthorized\") {\n return <UnauthorizedScreen action={access.action} role={access.user.role} />\n }\n\n return <DashboardPanel user={access.user} />\n}\n`\n}\n\nfunction dashboardPanel(providerImport: string): string {\n return `\"use client\"\n\nimport { Dashboard } from \"@nonext/husk/admin/ui\"\nimport { can } from \"@nonext/husk/permissions\"\nimport type { AuthUser } from \"@nonext/husk/types\"\nimport { useMemo } from \"react\"\n\nimport { useHusk } from \"${providerImport}\"\n\n/**\n * The host's half of the dashboard: an adapter and the content types this account may read.\n *\n * It runs in the browser because that is where the Firebase session lives, so every read is\n * subject to the same security rules as every other read. The tiles are filtered with\n * \\`can()\\` rather than by hand; hiding a tile is presentation only, and the adapter and the\n * rules refuse the same reads on their own.\n */\nexport function DashboardPanel({ user }: { user: AuthUser }) {\n const { adapter, registry } = useHusk()\n const types = useMemo(\n () =>\n registry.types\n .filter((entry) => can(user, \"read\", { type: entry.type }))\n .map((entry) => entry.type),\n [registry, user],\n )\n\n return <Dashboard adapter={adapter} types={types} />\n}\n`\n}\n\nfunction accountMenu(context: GenerateContext, firebaseClientImport: string): string {\n const { auth, adminRoute } = context.options\n\n if (!auth) {\n return `\"use client\"\n\nimport { Menu } from \"@nonext/husk/admin/ui\"\n\n/**\n * The sidebar footer's menu trigger.\n *\n * This app was scaffolded with --no-auth, so there is no generated sign-out. Wire this to\n * whatever ends a session in your app, and delete the Husk session cookie along with it, or\n * the server will keep treating the visitor as signed in.\n */\nexport function AccountMenu() {\n return <Menu label=\"Account\" items={[]} onSelect={() => undefined} triggerSize=\"sm\" />\n}\n`\n }\n\n return `\"use client\"\n\nimport { Menu } from \"@nonext/husk/admin/ui\"\nimport { useRouter } from \"next/navigation\"\n\nimport { huskAuth } from \"${firebaseClientImport}\"\n\n/**\n * The sidebar footer's menu trigger.\n *\n * The host owns it because the session is the host's: the package knows nothing about this\n * app's Firebase client or its session route. The sidebar only asks for a node.\n */\nexport function AccountMenu() {\n const router = useRouter()\n\n return (\n <Menu\n label=\"Account\"\n items={[{ kind: \"item\", id: \"sign-out\", label: \"Sign out\" }]}\n onSelect={async (id) => {\n if (id !== \"sign-out\") return\n\n // Cookie first: if the browser session were dropped first and this failed, the\n // server would keep treating the visitor as signed in.\n await fetch(\"/api/husk/session\", { method: \"DELETE\" })\n await huskAuth().signOut()\n router.replace(\"${adminRoute}/login\")\n router.refresh()\n }}\n triggerSize=\"sm\"\n />\n )\n}\n`\n}\n\nfunction navigationGuard(): string {\n return `\"use client\"\n\nimport { UnsavedChangesProvider } from \"@nonext/husk/admin/ui\"\nimport { useRouter } from \"next/navigation\"\n\n/**\n * The host half of the unsaved-changes guard.\n *\n * The package intercepts the click and asks the question; it cannot know how this app\n * navigates. Handing it \\`router.push\\` is what lets a confirmed \"discard\" resume the\n * intercepted link as a client navigation instead of a full document load.\n */\nexport function NavigationGuard({ children }: { children: React.ReactNode }) {\n const router = useRouter()\n\n return (\n <UnsavedChangesProvider navigate={(href) => router.push(href)}>\n {children}\n </UnsavedChangesProvider>\n )\n}\n`\n}\n\nfunction catchAll(notFoundImport: string): string {\n return `import type { Metadata } from \"next\"\n\nimport { AdminNotFoundScreen } from \"${notFoundImport}\"\n\nexport const metadata: Metadata = { title: \"Not found\" }\n\n/**\n * Every unmatched path under the admin, answered inside the shell.\n *\n * It renders the 404 itself instead of calling \\`notFound()\\`. Next serves a thrown\n * not-found as the framework's own error document, so the nested boundary only appears\n * after hydration, behind a flash of a bare page with no way back.\n *\n * The cost is the status code: this route answers 200 where 404 would be truthful. The\n * admin sits behind a session and is not crawled, and an editor who mistypes a URL is\n * better served by the sidebar than by a correct header nobody sees.\n */\nexport default function AdminCatchAll() {\n return <AdminNotFoundScreen />\n}\n`\n}\n\nfunction contentListPage(\n context: GenerateContext,\n imports: { session: string; metadata: string; route: string },\n): string {\n return `import { UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport type { Metadata } from \"next\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { adminAccess } from \"${imports.session}\"\n\nimport { ContentRoute } from \"${imports.route}\"\n\ntype ContentParams = { params: Promise<{ type: string }> }\n\nexport async function generateMetadata({ params }: ContentParams): Promise<Metadata> {\n const { type } = await params\n return adminMetadata(\\`${context.options.adminRoute}/content/\\${type}\\`)\n}\n\n/**\n * One route for every content type, which is the whole point of a generated admin.\n *\n * The route checks the session; which screen it is comes from the live registry in the\n * browser, because a database-defined content type exists only in Firestore and the server\n * has no session to read it with. A singleton is not a list, so the same route is its\n * editor instead. Nothing here is per content type.\n */\nexport default async function ContentTypePage({ params }: ContentParams) {\n const { type: slug } = await params\n const access = await adminAccess(\\`${context.options.adminRoute}/content/\\${slug}\\`)\n if (access.outcome === \"sign-in\") redirect(access.location)\n if (access.outcome === \"unauthorized\") {\n return <UnauthorizedScreen action={access.action} role={access.user.role} />\n }\n\n return <ContentRoute user={access.user} slug={slug} />\n}\n`\n}\n\nfunction contentCreatePage(\n context: GenerateContext,\n imports: { session: string; metadata: string; route: string },\n): string {\n return `import { UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport type { Metadata } from \"next\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { adminAccess } from \"${imports.session}\"\n\nimport { ContentRoute } from \"${imports.route}\"\n\ntype ContentParams = { params: Promise<{ type: string }> }\n\nexport async function generateMetadata({ params }: ContentParams): Promise<Metadata> {\n const { type } = await params\n return adminMetadata(\\`${context.options.adminRoute}/content/\\${type}\\`)\n}\n\n/**\n * The create screen, for every collection at once.\n *\n * A singleton has no create route, because it always exists exactly once. The permission to\n * create is checked here as well as by the adapter and by the security rules.\n */\nexport default async function NewEntryPage({ params }: ContentParams) {\n const { type: slug } = await params\n const access = await adminAccess(\\`${context.options.adminRoute}/content/\\${slug}/new\\`)\n if (access.outcome === \"sign-in\") redirect(access.location)\n if (access.outcome === \"unauthorized\") {\n return <UnauthorizedScreen action={access.action} role={access.user.role} />\n }\n\n return <ContentRoute user={access.user} slug={slug} creating />\n}\n`\n}\n\nfunction contentEditPage(\n context: GenerateContext,\n imports: { session: string; metadata: string; route: string },\n): string {\n return `import { UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport type { Metadata } from \"next\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { adminAccess } from \"${imports.session}\"\n\nimport { ContentRoute } from \"${imports.route}\"\n\ntype EntryParams = { params: Promise<{ type: string; entry: string }> }\n\nexport async function generateMetadata({ params }: EntryParams): Promise<Metadata> {\n const { type } = await params\n return adminMetadata(\\`${context.options.adminRoute}/content/\\${type}\\`)\n}\n\n/**\n * The edit screen, for every collection at once.\n *\n * Whether this user may change this entry depends on who created it, which only the entry\n * can answer, so the screen reads it and renders read-only rather than refusing the route.\n */\nexport default async function EditEntryPage({ params }: EntryParams) {\n const { type: slug, entry: entryId } = await params\n const access = await adminAccess(\n \\`${context.options.adminRoute}/content/\\${slug}/\\${entryId}\\`,\n )\n if (access.outcome === \"sign-in\") redirect(access.location)\n if (access.outcome === \"unauthorized\") {\n return <UnauthorizedScreen action={access.action} role={access.user.role} />\n }\n\n return <ContentRoute user={access.user} slug={slug} entryId={entryId} />\n}\n`\n}\n\nfunction contentRouteFile(\n _context: GenerateContext,\n imports: {\n notFound: string\n provider: string\n collection: string\n entry: string\n },\n): string {\n return `\"use client\"\n\nimport { Skeleton, UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport { can } from \"@nonext/husk/permissions\"\nimport type { AuthUser } from \"@nonext/husk/types\"\n\nimport { AdminNotFoundScreen } from \"${imports.notFound}\"\nimport { useHusk } from \"${imports.provider}\"\n\nimport { CollectionPanel } from \"${imports.collection}\"\nimport { EntryPanel } from \"${imports.entry}\"\n\n/**\n * Which content screen this route is, decided in the browser.\n *\n * The server has no Firebase session, cannot read \\`schemas\\`, and would answer 404 for a\n * content type that exists but was created in the Schema Builder. The session lives in the\n * browser, so the resolution lives there too, over the live registry.\n *\n * \\`synced\\` is why an unknown slug does not immediately 404: until the first schemas\n * snapshot lands, \"not in the registry\" and \"not read yet\" are the same observation, and\n * only one of them is a missing page.\n */\nexport function ContentRoute({\n user,\n slug,\n entryId,\n creating = false,\n}: {\n user: AuthUser\n slug: string\n entryId?: string\n creating?: boolean\n}) {\n const { registry, synced } = useHusk()\n const entry = registry.get(slug)\n\n if (entry === undefined) {\n return synced ? (\n <AdminNotFoundScreen />\n ) : (\n <Skeleton line=\"heading-md\" className=\"w-64\" label=\"Reading the content types\" />\n )\n }\n\n const { type } = entry\n\n // A singleton has one entry at a derived id, so it has no list, no create route and no\n // id in its URL.\n if ((creating || entryId !== undefined) && type.kind !== \"collection\") {\n return <AdminNotFoundScreen />\n }\n\n // The adapter and the security rules refuse the write too; this only keeps the screen\n // from opening a form nobody may submit.\n if (creating && !can(user, \"create\", { type })) {\n return <UnauthorizedScreen action=\"create\" role={user.role} />\n }\n\n if (creating || entryId !== undefined || type.kind === \"singleton\") {\n return (\n <EntryPanel\n user={user}\n slug={slug}\n {...(entryId === undefined ? {} : { entryId })}\n />\n )\n }\n\n return <CollectionPanel user={user} slug={slug} />\n}\n`\n}\n\nfunction collectionPanel(context: GenerateContext, providerImport: string): string {\n return `\"use client\"\n\nimport { CollectionList, DeleteEntriesDialog } from \"@nonext/husk/admin/ui\"\nimport type { AuthUser } from \"@nonext/husk/types\"\nimport { useRouter } from \"next/navigation\"\nimport { useState } from \"react\"\n\nimport { useHusk } from \"${providerImport}\"\n\n/**\n * The host's half of a collection list: an adapter, the content type, and where a row leads.\n *\n * It reads in the browser through the signed-in Firebase session, so every query is subject\n * to the same security rules as any other read. The screen is the package's, and it is the\n * same screen for every content type.\n */\nexport function CollectionPanel({ user, slug }: { user: AuthUser; slug: string }) {\n const router = useRouter()\n /** The ids the confirmation dialog is pointed at. Empty means it is closed. */\n const [deleting, setDeleting] = useState<readonly string[]>([])\n /** Bumped after a delete, so the list re-reads the page it is on. */\n const [refreshToken, setRefreshToken] = useState(0)\n\n const { adapter, registry } = useHusk()\n const type = registry.require(slug).type\n\n return (\n <>\n <CollectionList\n adapter={adapter}\n type={type}\n user={user}\n refreshToken={refreshToken}\n onOpen={(entryId) => router.push(\\`${context.options.adminRoute}/content/\\${slug}/\\${entryId}\\`)}\n onCreate={() => router.push(\\`${context.options.adminRoute}/content/\\${slug}/new\\`)}\n onDelete={(entryIds) => setDeleting(entryIds)}\n />\n\n <DeleteEntriesDialog\n adapter={adapter}\n type={type}\n role={user.role}\n entryIds={deleting}\n onCancel={() => setDeleting([])}\n onDeleted={(_entryIds, complete) => {\n // A batch that failed halfway keeps the dialog open to say so, and the list still\n // re-reads, because the rows that did go are gone.\n if (complete) setDeleting([])\n setRefreshToken((token) => token + 1)\n }}\n />\n </>\n )\n}\n`\n}\n\nfunction entryPanel(\n context: GenerateContext,\n imports: { provider: string; firebaseClient: string },\n): string {\n const { media, adminRoute } = context.options\n\n const uiImports = media\n ? `import {\n AdminMediaPickerProvider,\n DeleteEntriesDialog,\n EntryEditor,\n} from \"@nonext/husk/admin/ui\"`\n : `import { DeleteEntriesDialog, EntryEditor } from \"@nonext/husk/admin/ui\"`\n\n const huskImport = media\n ? `import { useContentTypeLabel, useHusk } from \"${imports.provider}\"`\n : `import { useHusk } from \"${imports.provider}\"`\n\n const uploaderLine = media\n ? ` const { adapter, uploader, registry } = useHusk()\n const labelOf = useContentTypeLabel()`\n : ` const { adapter, registry } = useHusk()`\n\n const open = media\n ? ` // The real media picker, over the same adapter the editor writes through. Without it\n // the image and file controls render their \"library not available\" state.\n <AdminMediaPickerProvider\n adapter={adapter}\n user={user}\n uploader={uploader}\n contentTypeLabel={labelOf}\n >`\n : ` <>`\n const close = media ? ` </AdminMediaPickerProvider>` : ` </>`\n\n return `\"use client\"\n\n${uiImports}\nimport { isSingleton } from \"@nonext/husk/core\"\nimport { readUser } from \"@nonext/husk/firebase\"\nimport type { AuthUser } from \"@nonext/husk/types\"\nimport { useRouter } from \"next/navigation\"\nimport { useMemo, useState } from \"react\"\n\nimport { huskFirestore } from \"${imports.firebaseClient}\"\n${huskImport}\n\n/**\n * The host's half of the entry editor: an adapter, the content type, and the routes.\n *\n * The screen is the package's and it is the same screen for every content type, for both\n * create and edit, and for a collection entry and a singleton. This file supplies what only\n * a host can know: where \"back\" goes, where a freshly created entry lives, who can turn a\n * uid into a name, and where the reader lands once an entry is gone.\n *\n * A singleton differs in configuration only. It has no list to cancel back to, no id in its\n * URL and no delete, so those props are simply not passed.\n */\nexport function EntryPanel({\n user,\n slug,\n entryId,\n}: {\n user: AuthUser\n slug: string\n entryId?: string\n}) {\n const router = useRouter()\n /** The entry the confirmation dialog is pointed at. Empty means it is closed. */\n const [deleting, setDeleting] = useState<readonly string[]>([])\n\n${uploaderLine}\n const type = registry.require(slug).type\n const getType = useMemo(\n () => (candidate: string) => registry.get(candidate)?.type,\n [registry],\n )\n\n const list = \\`${adminRoute}/content/\\${slug}\\`\n const single = isSingleton(type)\n\n return (\n${open}\n <EntryEditor\n adapter={adapter}\n type={type}\n user={user}\n getType={getType}\n {...(entryId === undefined ? {} : { entryId })}\n {...(single ? {} : { onCancel: () => router.push(list) })}\n {...(single ? {} : { onDelete: (id: string) => setDeleting([id]) })}\n onSaved={(entry, created) => {\n // A created entry stops being a create screen the moment it exists, and the URL\n // has to say so, or a reload would start a second one. A singleton is already at\n // its own URL, because the derived id never appears in a route.\n if (created && !single) router.replace(\\`\\${list}/\\${entry.id}\\`)\n }}\n resolveAuthor={async (uid) => {\n const stored = await readUser(huskFirestore(), uid)\n return stored === null\n ? null\n : { uid, displayName: stored.displayName, email: stored.email }\n }}\n />\n\n <DeleteEntriesDialog\n adapter={adapter}\n type={type}\n role={user.role}\n entryIds={deleting}\n onCancel={() => setDeleting([])}\n onDeleted={(_entryIds, complete) => {\n // Back to the list. Staying on the editor would leave the reader looking at a\n // form for a document that no longer exists.\n if (!complete) return\n setDeleting([])\n router.push(list)\n }}\n />\n${close}\n )\n}\n`\n}\n\nfunction mediaPage(\n context: GenerateContext,\n imports: { session: string; metadata: string; panel: string },\n): string {\n return `import type { Metadata } from \"next\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { adminAccess } from \"${imports.session}\"\n\nimport { MediaPanel } from \"${imports.panel}\"\n\nexport async function generateMetadata(): Promise<Metadata> {\n return adminMetadata(\"${context.options.adminRoute}/media\")\n}\n\n/**\n * The media library.\n *\n * Every signed-in role reaches it. What each of them may then do differs, and it differs in\n * the library itself rather than here: an author may upload and may replace their own\n * files, and only an editor may delete. The rules enforce the same split a second time.\n */\nexport default async function MediaPage() {\n const access = await adminAccess(\"${context.options.adminRoute}/media\")\n if (access.outcome === \"sign-in\") redirect(access.location)\n\n return <MediaPanel user={access.user} />\n}\n`\n}\n\nfunction mediaPanel(providerImport: string): string {\n return `\"use client\"\n\nimport { MediaLibrary } from \"@nonext/husk/admin/ui\"\nimport type { AuthUser } from \"@nonext/husk/types\"\n\nimport { useContentTypeLabel, useHusk } from \"${providerImport}\"\n\n/**\n * The host's half of the media library: an adapter, an uploader, and a way to name a\n * content type in a usage list. The screen is the package's, and it is the same screen the\n * media picker renders inside its modal.\n */\nexport function MediaPanel({ user }: { user: AuthUser }) {\n const { adapter, uploader } = useHusk()\n const contentTypeLabel = useContentTypeLabel()\n\n return (\n <MediaLibrary\n adapter={adapter}\n user={user}\n uploader={uploader}\n contentTypeLabel={contentTypeLabel}\n />\n )\n}\n`\n}\n\nfunction developerPage(\n context: GenerateContext,\n imports: { session: string; metadata: string; config: string },\n): string {\n const route = context.options.adminRoute\n\n return `import { adminPageTitle, buildAdminNavigation } from \"@nonext/husk/admin\"\nimport { PageHeader, UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport { buildRegistry } from \"@nonext/husk/core\"\nimport type { Metadata } from \"next\"\nimport Link from \"next/link\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { codeContentTypes } from \"${imports.config}\"\nimport { adminAccess } from \"${imports.session}\"\n\ntype DeveloperParams = { params: Promise<{ section?: string[] }> }\n\n/** Derived from the navigation, so each Developer screen titles its own tab. */\nexport async function generateMetadata({ params }: DeveloperParams): Promise<Metadata> {\n const { section } = await params\n return adminMetadata(developerPath(section))\n}\n\nfunction developerPath(section: string[] | undefined): string {\n return [\"${route}/developer\", ...(section ?? [])].join(\"/\")\n}\n\n/**\n * The Developer area boundary: an editor or author who navigates straight here gets the\n * unauthorized screen rather than a redirect loop. The adapter and the security rules\n * refuse the same actions independently.\n *\n * One optional catch-all stands in for the Developer screens that have no page of their\n * own, so every item the sidebar generates leads somewhere. Content Types has its own\n * route and takes precedence, because a static segment beats a catch-all.\n */\nexport default async function DeveloperPage({ params }: DeveloperParams) {\n const { section } = await params\n const access = await adminAccess(developerPath(section), \"manage-schema\")\n if (access.outcome === \"sign-in\") redirect(access.location)\n\n if (access.outcome === \"unauthorized\") {\n return (\n <UnauthorizedScreen action={access.action} role={access.user.role}>\n <Link href=\"${route}\" className=\"text-body-sm text-text-primary underline\">\n Back to the admin\n </Link>\n </UnauthorizedScreen>\n )\n }\n\n // The heading and the trail come from the same navigation data as the sidebar and the\n // document title, so a Developer screen never names itself in three places.\n const navigation = buildAdminNavigation({\n registry: buildRegistry(codeContentTypes, []),\n user: access.user,\n })\n const label = adminPageTitle(navigation, developerPath(section))\n\n return (\n <PageHeader\n title={label ?? \"Developer\"}\n {...(label === undefined\n ? {}\n : {\n breadcrumb: [\n { label: \"Developer\", href: \"${route}/developer\" },\n { label },\n ],\n })}\n />\n )\n}\n`\n}\n\nfunction contentTypesPage(\n context: GenerateContext,\n imports: { session: string; metadata: string; panel: string },\n): string {\n const route = context.options.adminRoute\n\n return `import { UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport type { Metadata } from \"next\"\nimport Link from \"next/link\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { adminAccess } from \"${imports.session}\"\n\nimport { ContentTypesPanel } from \"${imports.panel}\"\n\nconst PATH = \"${route}/developer/content-types\"\n\nexport function generateMetadata(): Promise<Metadata> {\n return adminMetadata(PATH)\n}\n\n/**\n * The Schema Builder's Content Types list.\n *\n * The route guards \\`manage-schema\\`, which is admin only. Hiding the Developer group in the\n * sidebar is presentation; this is the check that stops an editor who types the URL, and\n * the adapter and the security rules refuse the same actions again below it.\n */\nexport default async function ContentTypesPage() {\n const access = await adminAccess(PATH, \"manage-schema\")\n if (access.outcome === \"sign-in\") redirect(access.location)\n\n if (access.outcome === \"unauthorized\") {\n return (\n <UnauthorizedScreen action={access.action} role={access.user.role}>\n <Link href=\"${route}\" className=\"text-body-sm text-text-primary underline\">\n Back to the admin\n </Link>\n </UnauthorizedScreen>\n )\n }\n\n return <ContentTypesPanel user={access.user} />\n}\n`\n}\n\nfunction contentTypesPanel(context: GenerateContext, providerImport: string): string {\n const route = context.options.adminRoute\n\n return `\"use client\"\n\nimport { ContentTypesScreen } from \"@nonext/husk/admin/ui\"\nimport type { AuthUser } from \"@nonext/husk/types\"\nimport { useRouter } from \"next/navigation\"\n\nimport { useHusk } from \"${providerImport}\"\n\n/**\n * The host's half of the Content Types list: the live registry, the adapter, and where a\n * row leads. A type created here reaches the sidebar through the same registry this screen\n * renders from, so there is nothing to refresh and nothing to invalidate.\n */\nexport function ContentTypesPanel({ user }: { user: AuthUser }) {\n const router = useRouter()\n const { registry, adapter, liveError } = useHusk()\n\n return (\n <ContentTypesScreen\n registry={registry}\n adapter={adapter}\n user={user}\n liveError={liveError}\n onOpen={(slug) => router.push(\\`${route}/developer/content-types/\\${slug}\\`)}\n onCreated={(slug) => router.push(\\`${route}/developer/content-types/\\${slug}\\`)}\n />\n )\n}\n`\n}\n\nfunction contentTypePage(\n context: GenerateContext,\n imports: { session: string; metadata: string; panel: string },\n): string {\n const route = context.options.adminRoute\n\n return `import { UnauthorizedScreen } from \"@nonext/husk/admin/ui\"\nimport type { Metadata } from \"next\"\nimport Link from \"next/link\"\nimport { redirect } from \"next/navigation\"\n\nimport { adminMetadata } from \"${imports.metadata}\"\nimport { adminAccess } from \"${imports.session}\"\n\nimport { ContentTypePanel } from \"${imports.panel}\"\n\ntype TypeParams = { params: Promise<{ slug: string }> }\n\nexport function generateMetadata(): Promise<Metadata> {\n return adminMetadata(\"${route}/developer/content-types\")\n}\n\n/**\n * One content type in the Schema Builder.\n *\n * The slug is resolved in the browser rather than here, for the same reason the content\n * routes are: a database-defined type lives in Firestore and the server has no session to\n * read it with.\n */\nexport default async function ContentTypePage({ params }: TypeParams) {\n const { slug } = await params\n const access = await adminAccess(\n \\`${route}/developer/content-types/\\${slug}\\`,\n \"manage-schema\",\n )\n if (access.outcome === \"sign-in\") redirect(access.location)\n\n if (access.outcome === \"unauthorized\") {\n return (\n <UnauthorizedScreen action={access.action} role={access.user.role}>\n <Link href=\"${route}\" className=\"text-body-sm text-text-primary underline\">\n Back to the admin\n </Link>\n </UnauthorizedScreen>\n )\n }\n\n return <ContentTypePanel user={access.user} slug={slug} />\n}\n`\n}\n\nfunction contentTypePanel(\n context: GenerateContext,\n imports: { provider: string; notFound: string },\n): string {\n return `\"use client\"\n\nimport { ContentTypeEditor, Skeleton } from \"@nonext/husk/admin/ui\"\nimport type { AuthUser } from \"@nonext/husk/types\"\n\nimport { AdminNotFoundScreen } from \"${imports.notFound}\"\nimport { useHusk } from \"${imports.provider}\"\n\n/**\n * The host's half of the Schema Builder editor.\n *\n * Whether the type may be edited is not decided here. \\`ContentTypeEditor\\` asks\n * \\`isSchemaReadOnly\\`, which answers for a locked type and for every code-defined one, and\n * the adapter refuses the write independently of both.\n */\nexport function ContentTypePanel({ user, slug }: { user: AuthUser; slug: string }) {\n const { registry, adapter, synced } = useHusk()\n const entry = registry.get(slug)\n\n if (entry === undefined) {\n // Until the first schemas snapshot lands, \"unknown\" and \"not read yet\" look the same.\n return synced ? (\n <AdminNotFoundScreen />\n ) : (\n <Skeleton line=\"heading-md\" className=\"w-64\" label=\"Reading the content types\" />\n )\n }\n\n return (\n <ContentTypeEditor\n adapter={adapter}\n registry={registry}\n entry={entry}\n user={user}\n backHref=\"${context.options.adminRoute}/developer/content-types\"\n />\n )\n}\n`\n}\n\nfunction loginPage(panelImport: string): string {\n return `import type { Metadata } from \"next\"\n\nimport { LoginPanel } from \"${panelImport}\"\n\nexport const metadata: Metadata = { title: \"Sign in\" }\n\nexport default async function LoginPage({\n searchParams,\n}: {\n searchParams: Promise<{ next?: string; reason?: string }>\n}) {\n const { next, reason } = await searchParams\n\n return <LoginPanel next={next ?? null} expired={reason === \"expired\"} />\n}\n`\n}\n\nfunction loginPanel(_context: GenerateContext, firebaseClientImport: string): string {\n return `\"use client\"\n\nimport { safeReturnPath } from \"@nonext/husk/admin\"\nimport { LoginScreen } from \"@nonext/husk/admin/ui\"\nimport { useRouter } from \"next/navigation\"\n\nimport { currentIdToken, huskAuth } from \"${firebaseClientImport}\"\n\nexport function LoginPanel({ next, expired }: { next: string | null; expired: boolean }) {\n const router = useRouter()\n const destination = safeReturnPath(next)\n\n return (\n <LoginScreen\n expired={expired}\n onSignIn={async ({ email, password }) => {\n await huskAuth().signIn(email, password)\n\n // The Firebase session alone is invisible to the server, so the cookie is what\n // actually ends the sign-in. A failure here has to surface as a failed sign-in.\n const response = await fetch(\"/api/husk/session\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ idToken: await currentIdToken() }),\n })\n if (!response.ok) throw new Error(\"Could not start a server session.\")\n\n router.replace(destination)\n router.refresh()\n }}\n />\n )\n}\n`\n}\n\nfunction sessionRoute(context: GenerateContext): string {\n const { emulators, projectId } = context.options\n\n const verifierBody = emulators\n ? ` try {\n return createEmulatorTokenVerifier({ projectId: PROJECT_ID })\n } catch {\n return createUnavailableTokenVerifier()\n }`\n : ` void PROJECT_ID\n return createUnavailableTokenVerifier()`\n\n return `import {\n ${emulators ? \"createEmulatorTokenVerifier,\\n \" : \"\"}createSessionRoutes,\n createUnavailableTokenVerifier,\n} from \"@nonext/husk/admin\"\nimport type { TokenVerifier } from \"@nonext/husk/admin\"\n\n/**\n * The browser signs in with the Firebase SDK, then posts the fresh ID token here so the\n * server has something to read on the next navigation. Sign-out deletes the cookie.\n */\nconst PROJECT_ID = process.env[\"NEXT_PUBLIC_FIREBASE_PROJECT_ID\"] ?? \"${projectId}\"\n\nfunction buildVerifier(): TokenVerifier {\n${verifierBody}\n}\n\n// ${\n emulators\n ? \"Development runs on plain-HTTP localhost, which is the only case where the cookie may\\n// be non-Secure. Flip this to true, or drop the option, before deploying.\"\n : \"The cookie is Secure, so it is only sent over HTTPS.\"\n }\nconst routes = createSessionRoutes({\n verifier: buildVerifier(),\n secure: ${emulators ? \"false\" : \"true\"},\n})\n\nexport const POST = routes.POST\nexport const DELETE = routes.DELETE\n`\n}\n\nfunction globalError(): string {\n return `\"use client\"\n\nimport \"@nonext/husk/admin.css\"\n\nimport { RouteError } from \"@nonext/husk/admin/ui\"\n\n/**\n * The global fallback: the one failure a route boundary cannot catch.\n *\n * A segment's \\`error.tsx\\` renders inside its layout, which means it cannot catch a throw in\n * the root layout itself, and this app has two root layouts. When one of them fails there is\n * no chrome, no font and no stylesheet unless this file brings them, so it renders its own\n * \\`<html>\\` and \\`<body>\\` and imports the admin stylesheet.\n *\n * Importing that stylesheet here is not a leak into the public site. Next scopes a CSS\n * import to the segment that makes it, and this segment only ever renders in place of the\n * entire document, after the document that was meant to render has already failed.\n */\nexport default function GlobalError({\n error,\n reset,\n}: {\n error: Error & { digest?: string }\n reset: () => void\n}) {\n return (\n <html lang=\"en\">\n <body className=\"min-h-dvh bg-surface-base font-sans text-body text-text-secondary antialiased\">\n <main className=\"mx-auto max-w-reading px-6 py-12\">\n <RouteError error={error} reset={reset} title=\"Husk\" />\n </main>\n </body>\n </html>\n )\n}\n`\n}\n","import { projectJoin } from \"../fs.js\"\n\nimport { specifier } from \"./context.js\"\nimport type { GenerateContext } from \"./context.js\"\n\n/**\n * The four files that hold the host's half of the wiring.\n *\n * Husk deliberately does not ship these. Only the app can hold a Firebase handle, only the\n * app knows whether it talks to an emulator, and only the app owns its cookies. The package\n * supplies the session reader, the guard, the adapter factory and the screens; these four\n * files connect them, and they are generated rather than imported so a developer can read\n * and change them.\n */\n\nexport function libFiles(context: GenerateContext): Record<string, string> {\n const { libDir } = context\n const session = projectJoin(libDir, \"session.ts\")\n const cmsServer = projectJoin(libDir, \"cms-server.ts\")\n const metadata = projectJoin(libDir, \"admin-metadata.ts\")\n const config = \"cms.config.ts\"\n\n return {\n [projectJoin(libDir, \"firebase-client.ts\")]: firebaseClientFile(context),\n [session]: sessionFile(context),\n [cmsServer]: cmsServerFile(context, specifier(cmsServer, config)),\n [metadata]: adminMetadataFile(\n specifier(metadata, config),\n specifier(metadata, session),\n ),\n }\n}\n\nfunction firebaseClientFile(context: GenerateContext): string {\n const { emulators, media, projectId, storageBucket } = context.options\n\n const storageImport = media\n ? `import { ${emulators ? \"connectStorageEmulator, \" : \"\"}getStorage } from \"firebase/storage\"\nimport type { FirebaseStorage } from \"firebase/storage\"\n`\n : \"\"\n\n const storageAccessor = media\n ? `\nlet cachedStorage: FirebaseStorage | undefined\n\n/** The one Storage instance in the browser. Media uploads go through it. */\nexport function huskStorage(): FirebaseStorage {\n if (cachedStorage !== undefined) return cachedStorage\n\n const storage = getStorage(huskApp())${\n emulators\n ? `\n const [host, port] = STORAGE_HOST.split(\":\")\n connectStorageEmulator(storage, host ?? \"127.0.0.1\", Number(port ?? \"9199\"))`\n : \"\"\n }\n\n cachedStorage = storage\n return storage\n}\n`\n : \"\"\n\n const hosts = emulators\n ? `const AUTH_HOST = process.env[\"NEXT_PUBLIC_AUTH_EMULATOR_HOST\"] ?? \"127.0.0.1:9099\"\nconst FIRESTORE_HOST =\n process.env[\"NEXT_PUBLIC_FIRESTORE_EMULATOR_HOST\"] ?? \"127.0.0.1:8085\"\n${media ? 'const STORAGE_HOST = process.env[\"NEXT_PUBLIC_STORAGE_EMULATOR_HOST\"] ?? \"127.0.0.1:9199\"\\n' : \"\"}`\n : \"\"\n\n const claimSync = emulators\n ? ` // The emulator claim writer. A real project needs a privileged one instead: a Cloud\n // Function or an Admin SDK service that re-checks settings/bootstrap and users/{uid}\n // itself rather than trusting a role sent from a browser.\n claimSync: createEmulatorClaimSync({ authHost: AUTH_HOST, projectId: PROJECT_ID }),`\n : ` // Supply the privileged claim writer for this project. Without one, sign-in cannot\n // mint the role claim firestore.rules reads, and every read is denied.\n claimSync: createUnavailableClaimSync(),`\n\n const authImport = emulators\n ? \"createEmulatorClaimSync, createHuskAuth\"\n : \"createHuskAuth, createUnavailableClaimSync\"\n\n return `\"use client\"\n\nimport { ${authImport} } from \"@nonext/husk/auth\"\nimport type { HuskAuth } from \"@nonext/husk/auth\"\nimport { getApp, getApps, initializeApp } from \"firebase/app\"\nimport type { FirebaseApp } from \"firebase/app\"\nimport { ${emulators ? \"connectAuthEmulator, \" : \"\"}getAuth } from \"firebase/auth\"\nimport { ${emulators ? \"connectFirestoreEmulator, \" : \"\"}getFirestore } from \"firebase/firestore\"\nimport type { Firestore } from \"firebase/firestore\"\n${storageImport}\n/**\n * This app's browser-side Firebase handles, and the Husk auth layer over them.\n *\n * Every instance is cached at module scope. The admin, the auth layer and the media\n * uploader need the same Firestore instance, and the emulator connectors may only run\n * before the client is used and only once.\n${\n emulators\n ? ` *\n * Emulator wiring. Drop the connect calls, and the emulator variables in .env.local, when\n * this app points at a real Firebase project.`\n : \"\"\n}\n */\nconst PROJECT_ID = process.env[\"NEXT_PUBLIC_FIREBASE_PROJECT_ID\"] ?? \"${projectId}\"\nconst API_KEY =\n process.env[\"NEXT_PUBLIC_FIREBASE_API_KEY\"] ?? \"emulator-placeholder-key\"\nconst STORAGE_BUCKET =\n process.env[\"NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET\"] ?? \"${storageBucket}\"\n${hosts}\nlet cachedAuth: HuskAuth | undefined\nlet cachedDb: Firestore | undefined\n\nfunction huskApp(): FirebaseApp {\n return getApps().length > 0\n ? getApp()\n : initializeApp({\n projectId: PROJECT_ID,\n apiKey: API_KEY,\n storageBucket: STORAGE_BUCKET,\n })\n}\n\n/** The one Firestore instance in the browser. */\nexport function huskFirestore(): Firestore {\n if (cachedDb !== undefined) return cachedDb\n\n const db = getFirestore(huskApp())${\n emulators\n ? `\n const [host, port] = FIRESTORE_HOST.split(\":\")\n connectFirestoreEmulator(db, host ?? \"127.0.0.1\", Number(port ?? \"8085\"))`\n : \"\"\n }\n\n cachedDb = db\n return db\n}\n${storageAccessor}\n/** Sign-in, sign-up, roles and claim sync, over the handles above. */\nexport function huskAuth(): HuskAuth {\n if (cachedAuth !== undefined) return cachedAuth\n\n const auth = getAuth(huskApp())${\n emulators\n ? `\n connectAuthEmulator(auth, \\`http://\\${AUTH_HOST}\\`, { disableWarnings: true })`\n : \"\"\n }\n\n cachedAuth = createHuskAuth({\n auth,\n db: huskFirestore(),\n${claimSync}\n })\n\n return cachedAuth\n}\n\n/** The raw ID token. This is what the server session cookie carries. */\nexport async function currentIdToken(): Promise<string> {\n const auth = getAuth(getApp())\n const user = auth.currentUser\n if (user === null) throw new Error(\"Signed in without a Firebase session.\")\n return user.getIdToken(true)\n}\n`\n}\n\nfunction sessionFile(context: GenerateContext): string {\n const { emulators, projectId } = context.options\n\n const verifierBody = emulators\n ? ` try {\n return createEmulatorTokenVerifier({ projectId: PROJECT_ID })\n } catch {\n return createUnavailableTokenVerifier()\n }`\n : ` // Replace this with a verifier that checks the token signature against Google's\n // public keys before this app leaves development. Until then every admin route\n // redirects to the sign-in screen, which is the fail-closed half of the same seam.\n void PROJECT_ID\n return createUnavailableTokenVerifier()`\n\n const explanation = emulators\n ? \"The emulator one checks issuer, audience, subject, expiry and the role claim, and\\n * cannot check a signature, because emulator tokens are unsigned. It refuses to be\\n * constructed unless FIREBASE_AUTH_EMULATOR_HOST points at loopback.\"\n : \"No verifier is wired up for this project yet.\"\n\n return `import {\n ${emulators ? \"createEmulatorTokenVerifier,\\n \" : \"\"}createUnavailableTokenVerifier,\n readSession,\n resolveAdminAccess,\n SESSION_COOKIE,\n} from \"@nonext/husk/admin\"\nimport type { AdminAccess, TokenVerifier } from \"@nonext/husk/admin\"\nimport type { Action } from \"@nonext/husk/types\"\nimport { cookies } from \"next/headers\"\n\n/**\n * How this app answers \"who is the visitor\" on the server.\n *\n * The verifier is a seam. ${explanation}\n * The fallback trusts nothing, so a misconfigured deployment locks everyone out instead of\n * letting everyone in.\n *\n * Read the result as a presentation guard. What protects data is \\`can()\\` inside the\n * adapter and firestore.rules, both of which run against a real Firebase session that a\n * forged cookie does not produce.\n */\nconst PROJECT_ID = process.env[\"NEXT_PUBLIC_FIREBASE_PROJECT_ID\"] ?? \"${projectId}\"\n\nfunction buildVerifier(): TokenVerifier {\n${verifierBody}\n}\n\nconst verifier = buildVerifier()\n\nexport async function adminAccess(\n pathname: string,\n action?: Action,\n): Promise<AdminAccess> {\n const store = await cookies()\n const session = await readSession(store, verifier, SESSION_COOKIE)\n\n return resolveAdminAccess({\n session,\n pathname,\n ...(action === undefined ? {} : { action }),\n })\n}\n`\n}\n\nfunction cmsServerFile(context: GenerateContext, configImport: string): string {\n const { emulators, projectId } = context.options\n\n return `import { buildRegistry } from \"@nonext/husk/core\"\nimport { createFirestoreAdapter } from \"@nonext/husk/firebase\"\nimport { createServerCMS } from \"@nonext/husk/server\"\nimport type { ServerCms } from \"@nonext/husk/server\"\nimport type { AuthUser } from \"@nonext/husk/types\"\nimport { initializeApp } from \"firebase/app\"\nimport type { FirebaseApp } from \"firebase/app\"\nimport { ${emulators ? \"connectFirestoreEmulator, \" : \"\"}getFirestore } from \"firebase/firestore\"\nimport type { Firestore } from \"firebase/firestore\"\n\nimport { codeContentTypes, siteContentTypes } from \"${configImport}\"\nimport type { SiteContentTypes } from \"${configImport}\"\n\n/**\n * The public website's reader, and the only file in your own code that knows Husk stores\n * content in Firebase.\n *\n * Everything above it is written against \\`cms.collection()\\` and \\`cms.single()\\`. That is\n * the trade the adapter boundary exists to make: a change to the internal Firestore layout\n * costs a new version of the package, not a rewrite of your website.\n *\n * Three things are deliberate. A visitor has no session, so the connection is\n * unauthenticated and \\`currentUser\\` throws; nothing on the read path calls it. There is no\n * preview, so a draft cannot leak through a forwarded query or a URL parameter. And the\n * Firebase app is a named second one, so it never shares an instance with the admin's.\n */\nconst PROJECT_ID = process.env[\"NEXT_PUBLIC_FIREBASE_PROJECT_ID\"] ?? \"${projectId}\"\n${\n emulators\n ? `const FIRESTORE_HOST =\n process.env[\"NEXT_PUBLIC_FIRESTORE_EMULATOR_HOST\"] ?? \"127.0.0.1:8085\"\n`\n : \"\"\n}const SITE_APP = \"husk-site-reader\"\n\nlet cachedDb: Firestore | undefined\nlet cachedCms: ServerCms<SiteContentTypes> | undefined\n\n/**\n * Code-defined types only. The admin also merges the database half from a live Firestore\n * listener, which a website neither needs nor should pay for: a page renders the content\n * types its own code names, and a type created in the Schema Builder has no page until\n * somebody writes one.\n */\nconst registry = buildRegistry(codeContentTypes, [])\n\nfunction refuseUser(): AuthUser {\n throw new Error(\n \"A public page asked Husk who the visitor is. Reads on this path are anonymous and published-only.\",\n )\n}\n\nfunction siteFirestore(): Firestore {\n if (cachedDb !== undefined) return cachedDb\n\n const app: FirebaseApp = initializeApp({ projectId: PROJECT_ID }, SITE_APP)\n const db = getFirestore(app)${\n emulators\n ? `\n\n const [host, port] = FIRESTORE_HOST.split(\":\")\n connectFirestoreEmulator(db, host ?? \"127.0.0.1\", Number(port ?? \"8085\"))`\n : \"\"\n }\n\n cachedDb = db\n return db\n}\n\n/** The reader every public page uses. One instance per server process. */\nexport function siteCms(): ServerCms<SiteContentTypes> {\n if (typeof window !== \"undefined\") {\n throw new Error(\n \"The site reader was built in a browser. It belongs to server components; a client component reads through an API route or props.\",\n )\n }\n\n if (cachedCms !== undefined) return cachedCms\n\n cachedCms = createServerCMS({\n adapter: createFirestoreAdapter({\n db: siteFirestore(),\n registry: () => registry,\n currentUser: refuseUser,\n }),\n contentTypes: siteContentTypes,\n })\n\n return cachedCms\n}\n`\n}\n\nfunction adminMetadataFile(configImport: string, sessionImport: string): string {\n return `import { adminPageTitle, buildAdminNavigation } from \"@nonext/husk/admin\"\nimport { buildRegistry } from \"@nonext/husk/core\"\nimport type { Metadata } from \"next\"\n\nimport { codeContentTypes } from \"${configImport}\"\nimport { adminAccess } from \"${sessionImport}\"\n\n/**\n * The document title for an admin route, taken from the navigation the registry generated.\n *\n * No route writes its own title string, so the sidebar and the browser tab read the same\n * data and cannot drift. An unknown route, or a visitor with no session, falls back to the\n * root layout's default: a 404 must not leak the label of a page the visitor may not see.\n */\nexport async function adminMetadata(pathname: string): Promise<Metadata> {\n const access = await adminAccess(pathname)\n if (access.outcome !== \"allow\") return {}\n\n const registry = buildRegistry(codeContentTypes, [])\n const navigation = buildAdminNavigation({ registry, user: access.user })\n const title = adminPageTitle(navigation, pathname)\n\n return title === undefined ? {} : { title }\n}\n`\n}\n","import type { ContentTypeLike } from \"../../types/content-type.js\"\n\n/**\n * The content types a freshly scaffolded project starts with.\n *\n * One collection and one singleton, which is the smallest pair that shows both halves of\n * the model: many entries with a list, a form and a slug, and exactly one entry that is\n * edited in place. Without them `/admin` would come up with an empty sidebar and nothing to\n * click, and a developer's first impression of a CMS should not be a blank screen.\n *\n * They live here as data rather than as a string of TypeScript because `init` needs them\n * twice: once printed into `cms.config.ts` for the app to import, and once as values, to\n * work out which composite indexes `firestore.indexes.json` has to contain. Printing the\n * same objects both times is what keeps the generated schema and the generated indexes\n * from disagreeing on the day somebody edits this file.\n */\n\nexport function starterContentTypes(media: boolean): readonly ContentTypeLike[] {\n const pages: ContentTypeLike = {\n slug: \"pages\",\n kind: \"collection\",\n label: \"Page\",\n labelPlural: \"Pages\",\n icon: \"file-text\",\n titleField: \"title\",\n defaultSort: { field: \"title\", direction: \"asc\" },\n fields: {\n title: {\n type: \"text\",\n label: \"Title\",\n validation: { required: true, maxLength: 120 },\n },\n slug: {\n type: \"slug\",\n label: \"Slug\",\n from: \"title\",\n validation: { required: true, unique: true },\n },\n section: {\n type: \"select\",\n label: \"Section\",\n options: [\n { value: \"main\", label: \"Main\" },\n { value: \"legal\", label: \"Legal\" },\n ],\n validation: { required: true },\n },\n summary: { type: \"textarea\", label: \"Summary\", rows: 3 },\n body: { type: \"richtext\", label: \"Body\" },\n ...(media\n ? {\n cover: {\n type: \"image\" as const,\n label: \"Cover image\",\n accept: [\"image/*\"],\n placeholder: \"No cover chosen\",\n },\n }\n : {}),\n },\n }\n\n const siteSettings: ContentTypeLike = {\n slug: \"site-settings\",\n kind: \"singleton\",\n label: \"Site settings\",\n icon: \"settings\",\n fields: {\n siteName: { type: \"text\", label: \"Site name\", validation: { required: true } },\n tagline: { type: \"text\", label: \"Tagline\" },\n },\n }\n\n return [pages, siteSettings]\n}\n\n/**\n * Prints a plain value as TypeScript source.\n *\n * Narrow on purpose: it handles exactly what a content type contains, and throws on\n * anything else rather than emitting something that looks like source and is not. A\n * generator that silently writes `[object Object]` into a customer's config is a bug\n * report that arrives as \"the admin is blank\".\n */\nexport function printValue(value: unknown, indent = 0): string {\n const pad = \" \".repeat(indent)\n const inner = \" \".repeat(indent + 1)\n\n if (value === null) return \"null\"\n if (typeof value === \"string\") return JSON.stringify(value)\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value)\n\n if (Array.isArray(value)) {\n if (value.length === 0) return \"[]\"\n const items = value.map((item) => `${inner}${printValue(item, indent + 1)},`)\n return `[\\n${items.join(\"\\n\")}\\n${pad}]`\n }\n\n if (typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>).filter(\n ([, item]) => item !== undefined,\n )\n if (entries.length === 0) return \"{}\"\n const lines = entries.map(\n ([key, item]) => `${inner}${printKey(key)}: ${printValue(item, indent + 1)},`,\n )\n return `{\\n${lines.join(\"\\n\")}\\n${pad}}`\n }\n\n throw new Error(`Cannot print a ${typeof value} into generated TypeScript.`)\n}\n\n/** Quotes a key only when it needs quoting, so the generated file reads like handwriting. */\nfunction printKey(key: string): string {\n return /^[A-Za-z_$][\\w$]*$/.test(key) ? key : JSON.stringify(key)\n}\n\n/** The identifier a content type is exported under in `cms.config.ts`. */\nexport function exportName(slug: string): string {\n const camel = slug.replace(/[-_](.)/g, (_match, letter: string) => letter.toUpperCase())\n return /^[a-z]/.test(camel) ? camel : `type${camel}`\n}\n","import { requiredEntryIndexes } from \"../../core/indexes.js\"\n\nimport type { GenerateContext } from \"./context.js\"\nimport { exportName, printValue, starterContentTypes } from \"./starter.js\"\n\n/**\n * The files that sit at the root of a customer project rather than inside `app/`.\n *\n * Two of them are the reason this whole command exists. `cms.config.ts` is where a\n * developer declares content types, and `firestore.indexes.json` is generated from those\n * same declarations, because the collection list builds its queries from the schema and\n * Firestore refuses a composite query with no index behind it. Writing the second by hand\n * from the first is exactly the kind of bookkeeping a scaffold should absorb.\n */\n\n/** Files Husk owns: an existing copy that differs is a conflict, not something to overwrite. */\nexport function ownedProjectFiles(context: GenerateContext): Record<string, string> {\n const files: Record<string, string> = { \"cms.config.ts\": cmsConfigFile(context) }\n\n if (context.options.auth && context.options.emulators) {\n files[\"scripts/husk-first-admin.mjs\"] = firstAdminScript(context)\n }\n\n return files\n}\n\n/**\n * The first account, which is the one thing a fresh project cannot do from the admin.\n *\n * Husk has no sign-up screen on purpose: an admin interface that lets a stranger create an\n * account is not an admin interface. The first account instead comes from `signUp`, which\n * claims the create-once `settings/bootstrap` marker and becomes admin, and everyone after\n * it is created by that admin.\n *\n * A script is the honest shape for that against the emulators, because minting the role\n * claim needs a privileged writer and the emulator's is reachable from here. A real Firebase\n * project has no such writer in this product yet: see the claim-sync seam in the package\n * documentation. That is why the script is only generated for an emulator scaffold rather\n * than pretending to work everywhere.\n */\nfunction firstAdminScript(context: GenerateContext): string {\n return `/**\n * Creates the first Husk account in the Auth emulator and makes it the admin.\n *\n * node scripts/husk-first-admin.mjs you@example.com a-password\n *\n * The first account in an empty project claims settings/bootstrap and becomes admin; every\n * account after it is created by that admin. The marker can be written exactly once, so\n * running this twice does not mint a second admin.\n */\nimport { createEmulatorClaimSync, createHuskAuth } from \"@nonext/husk/auth\"\nimport { initializeApp } from \"firebase/app\"\nimport { connectAuthEmulator, getAuth } from \"firebase/auth\"\nimport { connectFirestoreEmulator, getFirestore } from \"firebase/firestore\"\n\nconst PROJECT_ID = process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID ?? \"${context.options.projectId}\"\nconst AUTH_HOST = process.env.NEXT_PUBLIC_AUTH_EMULATOR_HOST ?? \"127.0.0.1:9099\"\nconst FIRESTORE_HOST = process.env.NEXT_PUBLIC_FIRESTORE_EMULATOR_HOST ?? \"127.0.0.1:8085\"\n\nconst [email, password] = process.argv.slice(2)\nif (!email || !password) {\n console.error(\"Usage: node scripts/husk-first-admin.mjs <email> <password>\")\n process.exit(2)\n}\n\nconst app = initializeApp({ projectId: PROJECT_ID, apiKey: \"emulator-placeholder-key\" })\n\nconst auth = getAuth(app)\nconnectAuthEmulator(auth, \\`http://\\${AUTH_HOST}\\`, { disableWarnings: true })\n\nconst db = getFirestore(app)\nconst [host, port] = FIRESTORE_HOST.split(\":\")\nconnectFirestoreEmulator(db, host, Number(port))\n\nconst husk = createHuskAuth({\n auth,\n db,\n claimSync: createEmulatorClaimSync({ authHost: AUTH_HOST, projectId: PROJECT_ID }),\n})\n\nconst user = await husk.signUp({ email, password, displayName: email })\nconsole.log(\\`Created \\${user.email} with role \\${user.role}.\\`)\nprocess.exit(0)\n`\n}\n\n/**\n * Files written only when they are absent.\n *\n * Each of them belongs to something older than Husk. A project may already have a\n * `firebase.json` with hosting in it, an `.env.local` with half a dozen unrelated secrets,\n * a `firestore.indexes.json` covering queries Husk never generated. Replacing any of those\n * would be destructive and diffing them would be noise, so `init` writes them only when\n * there is nothing there and `doctor` checks afterwards that the content is right.\n */\nexport function createOnlyProjectFiles(context: GenerateContext): Record<string, string> {\n const files: Record<string, string> = {\n \"firebase.json\": firebaseJson(context),\n \".firebaserc\": firebaserc(context),\n \"firestore.indexes.json\": indexesJson(context),\n \".env.local\": envLocal(context),\n }\n\n if (!context.project.hasPostcssConfig) {\n files[\"postcss.config.mjs\"] = postcssConfig()\n }\n\n return files\n}\n\nfunction cmsConfigFile(context: GenerateContext): string {\n const types = starterContentTypes(context.options.media)\n const definitions = types\n .map(\n (type) =>\n `export const ${exportName(type.slug)} = defineContentType(${printValue(type)})`,\n )\n .join(\"\\n\\n\")\n\n const list = types.map((type) => exportName(type.slug)).join(\", \")\n const map = types\n .map((type) => {\n const name = exportName(type.slug)\n return type.slug === name\n ? ` ${name},`\n : ` ${JSON.stringify(type.slug)}: ${name},`\n })\n .join(\"\\n\")\n\n return `import { defineContentType } from \"@nonext/husk\"\nimport type { HuskConfig } from \"@nonext/husk/types\"\n\n/**\n * Husk configuration and this project's code-defined content types.\n *\n * A type declared here lives in git, is reviewed like any other code, and always wins a\n * slug conflict against one created in the admin Schema Builder. Add \\`locked: true\\` to a\n * type whose structure your page components depend on and the Schema Builder will refuse to\n * touch it, at the domain layer and in firestore.rules, not only in the interface.\n *\n * After changing a content type here, regenerate the composite indexes that its list view\n * needs and check the installation:\n *\n * npx nonext-husk doctor\n */\n\n${definitions}\n\n/** Every code-defined type, as the registry and the admin sidebar read them. */\nexport const codeContentTypes = [${list}]\n\n/**\n * The same types keyed by slug, which is what carries inference into a read.\n *\n * \\`cms.collection(\"${types[0]?.slug ?? \"pages\"}\").findBySlug(slug)\\` types its fields because the key is what\n * the lookup is indexed by. The array above is what the registry merges; this map is what\n * the SDK reads.\n */\nexport const siteContentTypes = {\n${map}\n} as const\n\nexport type SiteContentTypes = typeof siteContentTypes\n\n/** Read by \\`nonext-husk doctor\\`. Everything else here is read by your own code. */\nexport const huskConfig: HuskConfig = {\n generatedWith: ${JSON.stringify(context.version)},\n adminRoute: ${JSON.stringify(context.options.adminRoute)},\n projectId: ${JSON.stringify(context.options.projectId)},\n storageBucket: ${JSON.stringify(context.options.storageBucket)},\n media: ${String(context.options.media)},\n auth: ${String(context.options.auth)},\n emulators: ${String(context.options.emulators)},\n}\n`\n}\n\nfunction firebaseJson(context: GenerateContext): string {\n const storage = context.options.media\n ? `,\n \"storage\": {\n \"rules\": \"storage.rules\"\n }`\n : \"\"\n\n const emulators = context.options.emulators\n ? `,\n \"emulators\": {\n \"auth\": { \"port\": 9099 },\n \"firestore\": { \"port\": 8085 },${\n context.options.media\n ? `\n \"storage\": { \"port\": 9199 },`\n : \"\"\n }\n \"ui\": { \"enabled\": true, \"port\": 4000 },\n \"hub\": { \"port\": 4400 },\n \"singleProjectMode\": true\n }`\n : \"\"\n\n return `{\n \"firestore\": {\n \"rules\": \"firestore.rules\",\n \"indexes\": \"firestore.indexes.json\"\n }${storage}${emulators}\n}\n`\n}\n\nfunction firebaserc(context: GenerateContext): string {\n return `{\n \"projects\": {\n \"default\": \"${context.options.projectId}\"\n }\n}\n`\n}\n\nfunction indexesJson(context: GenerateContext): string {\n const indexes = requiredEntryIndexes(starterContentTypes(context.options.media))\n\n return `${JSON.stringify({ indexes, fieldOverrides: [] }, null, 2)}\\n`\n}\n\n/**\n * The environment the generated app needs to run against the Emulator Suite.\n *\n * `FIREBASE_AUTH_EMULATOR_HOST` is the one that is not obvious and the one that costs an\n * afternoon when it is missing. Without it the server-side token verifier refuses to be\n * constructed, the app falls back to the verifier that trusts nothing, and every admin\n * route redirects to the sign-in screen that the sign-in then fails to leave. That is\n * correct fail-closed behaviour and it looks exactly like a broken login, which is why it\n * is written into a file rather than into a paragraph of a README.\n */\nfunction envLocal(context: GenerateContext): string {\n const { projectId, storageBucket, emulators, media } = context.options\n\n const emulatorBlock = emulators\n ? `\n# The Firebase Emulator Suite. Delete this block when this app points at a real project.\n#\n# FIREBASE_AUTH_EMULATOR_HOST is read on the server. Without it the token verifier falls\n# back to the one that trusts nothing and every admin route redirects to sign-in.\nFIREBASE_AUTH_EMULATOR_HOST=127.0.0.1:9099\nNEXT_PUBLIC_AUTH_EMULATOR_HOST=127.0.0.1:9099\nNEXT_PUBLIC_FIRESTORE_EMULATOR_HOST=127.0.0.1:8085${\n media\n ? `\nNEXT_PUBLIC_STORAGE_EMULATOR_HOST=127.0.0.1:9199`\n : \"\"\n }\n`\n : \"\"\n\n return `# Husk. Generated by nonext-husk init.\n#\n# Not committed: .env.local is in the Next.js .gitignore. A web API key is not a secret,\n# but the emulator hosts below are local to this machine.\n\nNEXT_PUBLIC_FIREBASE_PROJECT_ID=${projectId}\nNEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=${storageBucket}\nNEXT_PUBLIC_FIREBASE_API_KEY=${emulators ? \"emulator-placeholder-key\" : \"\"}\n${emulatorBlock}`\n}\n\nfunction postcssConfig(): string {\n return `const config = {\n plugins: {\n \"@tailwindcss/postcss\": {},\n },\n}\n\nexport default config\n`\n}\n","import { projectJoin } from \"./fs.js\"\nimport type { ProjectFs } from \"./fs.js\"\nimport { adminFiles } from \"./generate/admin-files.js\"\nimport { createContext } from \"./generate/context.js\"\nimport { libFiles } from \"./generate/lib-files.js\"\nimport { createOnlyProjectFiles, ownedProjectFiles } from \"./generate/project-files.js\"\nimport type { InitOptions } from \"./options.js\"\nimport type { NextProject } from \"./project.js\"\nimport { stampRules } from \"./rules-template.js\"\nimport type { RulesTemplates } from \"./rules-template.js\"\n\n/**\n * Everything `init` would do, worked out before it does any of it.\n *\n * The plan is computed in full and only then applied, so that a collision in the last file\n * cannot leave a project half scaffolded. A scaffold that fails at file 28 of 33 is worse\n * than one that refuses at file 1: the developer now has to work out which of the 27 are\n * theirs to keep.\n */\n\n/** A file Husk generates, and what happens when one is already there. */\nexport type FileKind =\n /** Husk's own. An existing copy that differs is a conflict, and `init` refuses. */\n | \"owned\"\n /**\n * Somebody else's, possibly. Written only when nothing is there; never diffed, never\n * replaced. `doctor` checks the content afterwards instead.\n */\n | \"create-only\"\n\nexport interface PlannedFile {\n path: string\n contents: string\n kind: FileKind\n}\n\nexport interface PlannedMove {\n from: string\n to: string\n /** The file's text with its relative imports rewritten for its new depth. */\n contents: string\n}\n\nexport interface InitPlan {\n files: readonly PlannedFile[]\n moves: readonly PlannedMove[]\n}\n\nexport function buildPlan(\n fs: ProjectFs,\n project: NextProject,\n options: InitOptions,\n version: string,\n templates: RulesTemplates,\n): InitPlan {\n const context = createContext(options, project, version)\n\n const owned: Record<string, string> = {\n ...ownedProjectFiles(context),\n ...libFiles(context),\n ...adminFiles(context),\n \"firestore.rules\": stampRules(\"firestore\", templates.read(\"firestore\")),\n ...(options.media\n ? { \"storage.rules\": stampRules(\"storage\", templates.read(\"storage\")) }\n : {}),\n }\n\n const files: PlannedFile[] = [\n ...Object.entries(owned).map(([path, contents]) => ({\n path,\n contents,\n kind: \"owned\" as const,\n })),\n ...Object.entries(createOnlyProjectFiles(context)).map(([path, contents]) => ({\n path,\n contents,\n kind: \"create-only\" as const,\n })),\n ].sort((left, right) => left.path.localeCompare(right.path))\n\n return { files, moves: planSiteMove(fs, project) }\n}\n\n/**\n * Moving the website's root layout into `app/(site)/`.\n *\n * Husk's admin needs its own root layout, and Next allows one per rendered route, so both\n * have to live inside route groups. A freshly created Next app puts its root layout at\n * `app/layout.tsx`, which is the one place it cannot stay.\n *\n * The move is a move and not a rewrite: the file's own text is preserved apart from its\n * relative imports, which are one directory further from everything they point at. A\n * layout that imports `./globals.css` would otherwise fail to resolve, and the developer\n * would meet Husk through a build error in a file they wrote and did not touch.\n */\nfunction planSiteMove(fs: ProjectFs, project: NextProject): readonly PlannedMove[] {\n return project.siteFilesToMove.map((name) => {\n const from = projectJoin(project.appDir, name)\n return {\n from,\n to: projectJoin(project.appDir, \"(site)\", name),\n contents: deepenRelativeImports(fs.read(from)),\n }\n })\n}\n\nconst SPECIFIER = /(\\bfrom\\s*|\\bimport\\s*|\\brequire\\(\\s*)([\"'])(\\.{1,2}\\/[^\"']*)\\2/g\n\n/** `./x` becomes `../x` and `../x` becomes `../../x`. Aliases and bare names are untouched. */\nexport function deepenRelativeImports(source: string): string {\n return source.replace(\n SPECIFIER,\n (_match, lead: string, quote: string, path: string) => {\n const deepened = path.startsWith(\"./\") ? `../${path.slice(2)}` : `../${path}`\n return `${lead}${quote}${deepened}${quote}`\n },\n )\n}\n\nexport interface Conflict {\n path: string\n existing: string\n generated: string\n}\n\nexport interface PlanOutcome {\n /** Nothing is there yet. */\n create: readonly PlannedFile[]\n /** Already exactly what Husk would write, so a second run is a no-op. */\n unchanged: readonly PlannedFile[]\n /** Present, not Husk's to replace, left alone. */\n kept: readonly PlannedFile[]\n /** Present, Husk's, and different. This is what makes `init` refuse. */\n conflicts: readonly Conflict[]\n moves: readonly PlannedMove[]\n /** A move whose destination already exists. Also a refusal. */\n moveConflicts: readonly string[]\n}\n\nexport function classifyPlan(fs: ProjectFs, plan: InitPlan): PlanOutcome {\n const create: PlannedFile[] = []\n const unchanged: PlannedFile[] = []\n const kept: PlannedFile[] = []\n const conflicts: Conflict[] = []\n\n for (const file of plan.files) {\n if (!fs.exists(file.path)) {\n create.push(file)\n continue\n }\n if (file.kind === \"create-only\") {\n kept.push(file)\n continue\n }\n\n const existing = fs.read(file.path)\n if (existing === file.contents) unchanged.push(file)\n else conflicts.push({ path: file.path, existing, generated: file.contents })\n }\n\n return {\n create,\n unchanged,\n kept,\n conflicts,\n moves: plan.moves,\n moveConflicts: plan.moves.filter((move) => fs.exists(move.to)).map((move) => move.to),\n }\n}\n\n/**\n * Applies a classified plan.\n *\n * Called once nothing conflicts, or with `force` when the developer has said in as many\n * words that they want their version replaced. `force` is the only path that overwrites a\n * file, and it exists so that the refusal has a way out that is a decision rather than a\n * workaround.\n */\nexport function applyPlan(fs: ProjectFs, outcome: PlanOutcome, force = false): void {\n for (const move of outcome.moves) {\n fs.move(move.from, move.to)\n fs.write(move.to, move.contents)\n }\n for (const file of outcome.create) {\n fs.write(file.path, file.contents)\n }\n if (force) {\n for (const conflict of outcome.conflicts) {\n fs.write(conflict.path, conflict.generated)\n }\n }\n}\n","import { projectJoin } from \"./fs.js\"\nimport type { ProjectFs } from \"./fs.js\"\n\n/**\n * What `init` is allowed to assume about the app it is being run inside.\n *\n * The scaffold needs three answers before it can write anything: where the App Router\n * lives, whether the package is installed, and whether the admin can get its own root\n * layout. The third one is the hard one, and it is the reason this file exists rather than\n * a pair of `existsSync` calls at the top of `init`.\n *\n * Husk's admin renders its own `<html>` and `<body>` through a second **root** layout, so\n * the website's stylesheet, fonts, providers and body classes never reach it and nothing\n * the admin loads leaks back out. Next allows exactly one root layout per rendered route,\n * which means `app/layout.tsx` must not exist: both layouts have to sit inside route\n * groups. A freshly created Next app has its root layout at `app/layout.tsx`, so `init`\n * moves it into `app/(site)/` as part of scaffolding.\n *\n * That move is only safe while nothing else under `app/` depends on that layout. If the\n * project already has route segments, moving the root layout into a group would leave them\n * with no root layout at all and Next would refuse to build. In that case `init` refuses\n * and says exactly what to do, which is the honest outcome: a tool that reorganises a\n * developer's routes on a guess is worse than a tool that stops.\n */\n\n/** Next's file conventions that make a directory a route the root layout wraps. */\nconst ROUTE_FILES = new Set([\n \"page.tsx\",\n \"page.ts\",\n \"page.jsx\",\n \"page.js\",\n \"route.tsx\",\n \"route.ts\",\n \"route.jsx\",\n \"route.js\",\n \"default.tsx\",\n \"default.ts\",\n])\n\n/** Files directly under `app/` that belong to the root layout and move with it. */\nconst ROOT_SEGMENT_FILES = [\n \"layout.tsx\",\n \"page.tsx\",\n \"loading.tsx\",\n \"error.tsx\",\n \"not-found.tsx\",\n \"template.tsx\",\n \"default.tsx\",\n]\n\nexport interface NextProject {\n /** `app` or `src/app`. */\n appDir: string\n /** `lib` or `src/lib`, matching wherever the app directory lives. */\n libDir: string\n /** True when the app uses the `src/` layout. */\n srcDir: boolean\n /** Root-layout files directly under `app/` that have to move into `app/(site)/`. */\n siteFilesToMove: readonly string[]\n /** Route segments under `app/` that would be orphaned by moving the root layout. */\n orphanedSegments: readonly string[]\n /** The admin can be given its own root layout without moving anything. */\n rootLayoutAlreadyGrouped: boolean\n /** A PostCSS config is already present, so init must not write a second one. */\n hasPostcssConfig: boolean\n}\n\nexport class ProjectError extends Error {\n override readonly name = \"ProjectError\"\n}\n\nfunction readManifest(fs: ProjectFs): Record<string, unknown> {\n if (!fs.exists(\"package.json\")) {\n throw new ProjectError(\n \"No package.json here. Run `nonext-husk init` inside a Next.js project, or pass --cwd.\",\n )\n }\n try {\n return JSON.parse(fs.read(\"package.json\")) as Record<string, unknown>\n } catch {\n throw new ProjectError(\"package.json is not valid JSON.\")\n }\n}\n\nfunction dependencyNames(manifest: Record<string, unknown>): readonly string[] {\n return [\"dependencies\", \"devDependencies\", \"peerDependencies\"].flatMap((key) => {\n const block = manifest[key]\n return block === null || typeof block !== \"object\" ? [] : Object.keys(block)\n })\n}\n\n/** Directories under `app/` that contain a routable file, at any depth. */\nfunction routeSegments(fs: ProjectFs, appDir: string): readonly string[] {\n const found: string[] = []\n\n const walk = (relativeDir: string): boolean => {\n const dir = projectJoin(appDir, relativeDir)\n let routable = false\n for (const name of fs.list(dir)) {\n const path = projectJoin(dir, name)\n if (fs.isDirectory(path)) {\n if (walk(projectJoin(relativeDir, name))) routable = true\n } else if (ROUTE_FILES.has(name)) {\n routable = true\n }\n }\n return routable\n }\n\n for (const name of fs.list(appDir)) {\n if (!fs.isDirectory(projectJoin(appDir, name))) continue\n if (walk(name)) found.push(name)\n }\n\n return found\n}\n\nexport function inspectProject(fs: ProjectFs): NextProject {\n const manifest = readManifest(fs)\n const dependencies = dependencyNames(manifest)\n\n if (!dependencies.includes(\"next\")) {\n throw new ProjectError(\n \"This project does not depend on next. Husk mounts inside a Next.js App Router app; create one with `npx create-next-app@latest` first.\",\n )\n }\n if (!dependencies.includes(\"@nonext/husk\")) {\n throw new ProjectError(\n \"@nonext/husk is not installed here. Install the package first, then run `npx nonext-husk init`.\",\n )\n }\n // The admin stylesheet is a Tailwind v4 stylesheet: it opens with `@import \"tailwindcss\"`\n // and scans the built package with `@source`. Without the PostCSS plugin the import is\n // passed through untouched and the admin renders as unstyled HTML, which looks like a\n // broken package rather than a missing dependency.\n const missingTailwind = [\"tailwindcss\", \"@tailwindcss/postcss\"].filter(\n (name) => !dependencies.includes(name),\n )\n if (missingTailwind.length > 0) {\n throw new ProjectError(\n `Husk's admin stylesheet needs Tailwind CSS v4. Install the missing package${missingTailwind.length === 1 ? \"\" : \"s\"} first:\n\n npm install -D ${missingTailwind.join(\" \")}\n`,\n )\n }\n\n const srcDir = fs.isDirectory(\"src/app\")\n const appDir = srcDir ? \"src/app\" : \"app\"\n if (!fs.isDirectory(appDir)) {\n throw new ProjectError(\n \"No app directory found. Husk needs the App Router; the Pages Router is not supported.\",\n )\n }\n\n const rootLayout = fs.exists(projectJoin(appDir, \"layout.tsx\"))\n\n const segments = routeSegments(fs, appDir).filter(\n // A route group is not a segment with its own root layout requirement: whatever is\n // inside it already sits under a group, so it is either already served by a grouped\n // root layout or it is the group this run is about to create.\n (name) => !name.startsWith(\"(\"),\n )\n\n return {\n appDir,\n libDir: srcDir ? \"src/lib\" : \"lib\",\n srcDir,\n siteFilesToMove: rootLayout\n ? ROOT_SEGMENT_FILES.filter((name) => fs.exists(projectJoin(appDir, name)))\n : [],\n orphanedSegments: rootLayout ? segments : [],\n rootLayoutAlreadyGrouped: !rootLayout,\n hasPostcssConfig: [\n \"postcss.config.mjs\",\n \"postcss.config.js\",\n \"postcss.config.ts\",\n ].some((name) => fs.exists(name)),\n }\n}\n\n/**\n * The refusal that is not about a file collision.\n *\n * Returns the explanation when the project cannot be given a second root layout without a\n * decision only the developer can make, and `null` when it can.\n */\nexport function rootLayoutBlocker(project: NextProject): string | null {\n if (project.orphanedSegments.length === 0) return null\n\n const list = project.orphanedSegments\n .map((name) => `${project.appDir}/${name}/`)\n .join(\", \")\n return [\n \"Husk needs its own root layout, so the website's root layout has to sit inside a route group.\",\n `Moving ${project.appDir}/layout.tsx into ${project.appDir}/(site)/ would leave these route segments with no root layout: ${list}.`,\n \"\",\n \"Do this once by hand, then run init again:\",\n ` 1. create ${project.appDir}/(site)/`,\n ` 2. move layout.tsx, page.tsx and every route segment of your website into it`,\n ` 3. fix the relative imports in the files you moved (../ instead of ./)`,\n \"\",\n \"Why: the admin renders its own <html> and <body> so your stylesheet, fonts and providers never reach it, and nothing it loads leaks back into your site. Next allows one root layout per route, so both have to live in a group.\",\n ].join(\"\\n\")\n}\n","import { createInterface } from \"node:readline/promises\"\nimport { stdin, stdout } from \"node:process\"\n\nimport type { ParsedArgs } from \"./args.js\"\nimport {\n DEFAULT_ADMIN_ROUTE,\n defaultStorageBucket,\n normalizeAdminRoute,\n validateProjectId,\n validateStorageBucket,\n} from \"./options.js\"\nimport type { InitOptions } from \"./options.js\"\n\n/**\n * The four questions the concept asked for, plus the one it could not have known about.\n *\n * Firebase project, admin route, media library and authentication are the concept's list.\n * The fifth is the emulators, and it earns its place because it changes every generated\n * Firebase call and the environment file: a scaffold that guessed wrong here produces an\n * app that starts, compiles and cannot sign anybody in.\n *\n * A flag already given is not asked about again. `--yes` asks nothing at all.\n *\n * Authentication is a real question and not a formality, but answering \"no\" is a narrower\n * choice than it sounds: the guard, the session cookie and the permission model stay. What\n * goes is the generated sign-in screen and the route handler that turns a Firebase ID token\n * into that cookie, which is the right answer only for a project that already has its own\n * sign-in and wants to wire the cookie itself.\n */\n\nexport interface Prompter {\n ask(question: string, fallback: string): Promise<string>\n confirm(question: string, fallback: boolean): Promise<boolean>\n close(): void\n}\n\nexport function createReadlinePrompter(): Prompter {\n const rl = createInterface({ input: stdin, output: stdout })\n\n return {\n async ask(question, fallback) {\n const answer = (await rl.question(`${question} (${fallback}) `)).trim()\n return answer.length === 0 ? fallback : answer\n },\n async confirm(question, fallback) {\n const answer = (await rl.question(`${question} (${fallback ? \"Y/n\" : \"y/N\"}) `))\n .trim()\n .toLowerCase()\n if (answer.length === 0) return fallback\n return answer.startsWith(\"y\")\n },\n close() {\n rl.close()\n },\n }\n}\n\nexport async function promptForOptions(\n prompter: Prompter,\n args: ParsedArgs,\n): Promise<InitOptions> {\n const projectId = validateProjectId(\n args.projectId ?? (await prompter.ask(\"Firebase project id?\", \"husk-dev-project\")),\n )\n\n const adminRoute = normalizeAdminRoute(\n args.adminRoute ?? (await prompter.ask(\"Admin route?\", DEFAULT_ADMIN_ROUTE)),\n )\n\n const media = args.media ?? (await prompter.confirm(\"Enable the media library?\", true))\n\n const auth = args.auth ?? (await prompter.confirm(\"Generate the sign-in screen?\", true))\n\n const emulators =\n args.emulators ??\n (await prompter.confirm(\"Develop against the Firebase emulators?\", true))\n\n // Only asked when it is used, and only when it cannot be derived. A project with no media\n // library never builds a Storage reference, so a bucket name would be dead configuration.\n const storageBucket =\n args.storageBucket !== undefined\n ? validateStorageBucket(args.storageBucket)\n : media\n ? validateStorageBucket(\n await prompter.ask(\"Storage bucket?\", defaultStorageBucket(projectId)),\n )\n : defaultStorageBucket(projectId)\n\n return { projectId, storageBucket, adminRoute, media, auth, emulators }\n}\n","/**\n * The diff `init` shows instead of overwriting a file.\n *\n * It exists because the alternative is a wall of red. When a scaffold collides with a file\n * a developer wrote, dumping every line of both versions is technically a diff and\n * practically unreadable, and an unreadable refusal gets resolved by deleting the file or\n * by passing --force, which is the outcome the refusal was supposed to prevent.\n *\n * So: a line-level longest-common-subsequence diff, grouped into hunks with three lines of\n * context, truncated per file, with `yours` on the left and `Husk` on the right. Only the\n * lines that actually differ are shown, and the header says how many there are.\n *\n * No dependency. The inputs are two versions of one generated file, a few hundred lines at\n * most, so the quadratic table is measured in kilobytes and the honest cost of a `diff`\n * package on a customer install is higher.\n */\n\nexport interface DiffLine {\n kind: \"context\" | \"yours\" | \"husk\"\n text: string\n}\n\nexport interface DiffHunk {\n /** 1-based line number in the existing file where the hunk starts. */\n start: number\n lines: readonly DiffLine[]\n}\n\nexport interface FileDiff {\n hunks: readonly DiffHunk[]\n /** Changed lines across the whole file, before truncation. */\n changed: number\n /** Hunks dropped to keep the output readable. */\n omittedHunks: number\n}\n\nconst CONTEXT = 3\nconst MAX_HUNKS = 4\nconst MAX_HUNK_LINES = 24\n\nfunction lines(text: string): readonly string[] {\n const split = text.replace(/\\r\\n/g, \"\\n\").split(\"\\n\")\n // A trailing newline is not an empty last line for diff purposes; it would show up as a\n // spurious change on every file that has one and every file here has one.\n return split.length > 0 && split[split.length - 1] === \"\" ? split.slice(0, -1) : split\n}\n\n/** Classic LCS table. Both inputs are one file, so the table is small by construction. */\nfunction commonSubsequence(\n left: readonly string[],\n right: readonly string[],\n): readonly DiffLine[] {\n const rows = left.length\n const columns = right.length\n const table: number[][] = Array.from({ length: rows + 1 }, () =>\n new Array<number>(columns + 1).fill(0),\n )\n\n for (let row = rows - 1; row >= 0; row -= 1) {\n for (let column = columns - 1; column >= 0; column -= 1) {\n const current = table[row] ?? []\n const next = table[row + 1] ?? []\n current[column] =\n left[row] === right[column]\n ? (next[column + 1] ?? 0) + 1\n : Math.max(next[column] ?? 0, current[column + 1] ?? 0)\n }\n }\n\n const result: DiffLine[] = []\n let row = 0\n let column = 0\n\n while (row < rows && column < columns) {\n if (left[row] === right[column]) {\n result.push({ kind: \"context\", text: left[row] ?? \"\" })\n row += 1\n column += 1\n continue\n }\n const down = table[row + 1]?.[column] ?? 0\n const across = table[row]?.[column + 1] ?? 0\n if (down >= across) {\n result.push({ kind: \"yours\", text: left[row] ?? \"\" })\n row += 1\n } else {\n result.push({ kind: \"husk\", text: right[column] ?? \"\" })\n column += 1\n }\n }\n while (row < rows) {\n result.push({ kind: \"yours\", text: left[row] ?? \"\" })\n row += 1\n }\n while (column < columns) {\n result.push({ kind: \"husk\", text: right[column] ?? \"\" })\n column += 1\n }\n\n return result\n}\n\n/** `existing` is what is on disk, `generated` is what Husk would write. */\nexport function diffFiles(existing: string, generated: string): FileDiff {\n const script = commonSubsequence(lines(existing), lines(generated))\n const changed = script.filter((line) => line.kind !== \"context\").length\n\n if (changed === 0) return { hunks: [], changed: 0, omittedHunks: 0 }\n\n // Indices of changed entries, widened by the context window and merged where they touch.\n const interesting = script.flatMap((line, index) =>\n line.kind === \"context\" ? [] : [index],\n )\n const ranges: { from: number; to: number }[] = []\n for (const index of interesting) {\n const from = Math.max(0, index - CONTEXT)\n const to = Math.min(script.length - 1, index + CONTEXT)\n const last = ranges[ranges.length - 1]\n if (last !== undefined && from <= last.to + 1) last.to = Math.max(last.to, to)\n else ranges.push({ from, to })\n }\n\n // The hunk header counts lines of the existing file, which is what the reader has open.\n let existingLine = 0\n const startAt = new Map<number, number>()\n script.forEach((line, index) => {\n startAt.set(index, existingLine + 1)\n if (line.kind !== \"husk\") existingLine += 1\n })\n\n const hunks = ranges.slice(0, MAX_HUNKS).map((range) => ({\n start: startAt.get(range.from) ?? 1,\n lines: script.slice(range.from, range.to + 1).slice(0, MAX_HUNK_LINES),\n }))\n\n return { hunks, changed, omittedHunks: Math.max(0, ranges.length - MAX_HUNKS) }\n}\n\nconst MARK: Record<DiffLine[\"kind\"], string> = {\n context: \" \",\n yours: \"- \",\n husk: \"+ \",\n}\n\n/** The diff as the terminal shows it. `-` is the file on disk, `+` is what Husk would write. */\nexport function renderDiff(path: string, diff: FileDiff): string {\n const header = `${path} (${String(diff.changed)} line${diff.changed === 1 ? \"\" : \"s\"} differ, - yours / + Husk)`\n const body = diff.hunks.map(\n (hunk) =>\n ` @@ line ${String(hunk.start)}\\n` +\n hunk.lines.map((line) => ` ${MARK[line.kind]}${line.text}`).join(\"\\n\"),\n )\n const tail =\n diff.omittedHunks > 0\n ? [\n ` ... and ${String(diff.omittedHunks)} further change${diff.omittedHunks === 1 ? \"\" : \"s\"} in this file`,\n ]\n : []\n\n return [header, ...body, ...tail].join(\"\\n\")\n}\n","import { diffFiles, renderDiff } from \"./diff.js\"\nimport type { Check } from \"./doctor.js\"\nimport type { InitOptions } from \"./options.js\"\nimport type { PlanOutcome } from \"./plan.js\"\n\n/**\n * What the two commands print.\n *\n * Kept apart from the logic so that a test can assert on the decision rather than on the\n * wording, and so that the refusal in particular can be checked for the one thing that\n * makes it useful: it names every file it will not touch, and shows what differs.\n */\n\nexport function renderPlan(outcome: PlanOutcome, options: InitOptions): string {\n const lines: string[] = []\n\n for (const move of outcome.moves) {\n lines.push(` move ${move.from} -> ${move.to}`)\n }\n for (const file of outcome.create) {\n lines.push(` create ${file.path}`)\n }\n for (const file of outcome.kept) {\n lines.push(` keep ${file.path} (already here, left alone)`)\n }\n if (outcome.unchanged.length > 0) {\n lines.push(\n ` same ${String(outcome.unchanged.length)} file(s) already match what Husk would write`,\n )\n }\n\n lines.push(\"\")\n lines.push(\n ` admin route ${options.adminRoute}, project ${options.projectId}, media ${options.media ? \"on\" : \"off\"}, auth ${options.auth ? \"on\" : \"off\"}, emulators ${options.emulators ? \"on\" : \"off\"}`,\n )\n\n return lines.join(\"\\n\")\n}\n\n/**\n * The refusal.\n *\n * It refuses the whole run rather than the colliding files, because a half-applied scaffold\n * is a worse place to be than an unchanged project: the developer would then have to work\n * out which of the written files are theirs to keep. Nothing has been written when this is\n * printed.\n */\nexport function renderConflicts(outcome: PlanOutcome): string {\n const lines: string[] = [\n \"Husk did not write anything.\",\n \"\",\n `${String(outcome.conflicts.length + outcome.moveConflicts.length)} file(s) already exist here and differ from what init would write:`,\n \"\",\n ]\n\n for (const conflict of outcome.conflicts) {\n lines.push(\n renderDiff(conflict.path, diffFiles(conflict.existing, conflict.generated)),\n )\n lines.push(\"\")\n }\n for (const path of outcome.moveConflicts) {\n lines.push(\n `${path} (init wanted to move your root layout here and something is already there)`,\n )\n lines.push(\"\")\n }\n\n lines.push(\"What to do, in order of how much you want to keep:\")\n lines.push(\"\")\n lines.push(\" - Keep yours: nothing to do. Husk has changed nothing.\")\n lines.push(\" - Take Husk's for one file: delete that file and run init again.\")\n lines.push(\" - Take Husk's for all of them: run `npx nonext-husk init --force`.\")\n lines.push(\"\")\n lines.push(\"`--dry-run` lists everything init would write without writing any of it.\")\n\n return lines.join(\"\\n\")\n}\n\nconst MARK: Record<Check[\"status\"], string> = {\n ok: \" ok \",\n problem: \" PROBLEM \",\n unchecked: \" unchecked\",\n}\n\nexport function renderChecks(checks: readonly Check[]): string {\n const lines: string[] = []\n\n for (const check of checks) {\n lines.push(`${MARK[check.status]} ${check.name}`)\n for (const line of check.detail.split(\"\\n\")) lines.push(` ${line}`)\n if (check.fix !== undefined && check.status !== \"ok\") {\n lines.push(` -> ${check.fix}`)\n }\n lines.push(\"\")\n }\n\n const problems = checks.filter((check) => check.status === \"problem\").length\n const unchecked = checks.filter((check) => check.status === \"unchecked\").length\n\n lines.push(\n problems === 0\n ? `Nothing wrong that this can see. ${String(unchecked)} thing(s) it cannot check from files alone are listed above.`\n : `${String(problems)} problem(s). ${String(unchecked)} thing(s) it cannot check from files alone are listed above.`,\n )\n\n return lines.join(\"\\n\")\n}\n\n/** What a developer does next, in the order they have to do it. */\nexport function renderNextSteps(options: InitOptions): string {\n const steps: string[] = []\n\n if (options.emulators) {\n steps.push(\" 1. npx firebase emulators:start --only auth,firestore,storage\")\n if (options.auth) {\n steps.push(\" 2. node scripts/husk-first-admin.mjs you@example.com a-password\")\n steps.push(\" 3. npm run dev\")\n steps.push(` 4. open http://localhost:3000${options.adminRoute} and sign in`)\n } else {\n steps.push(\" 2. npm run dev\")\n steps.push(` 3. open http://localhost:3000${options.adminRoute}`)\n }\n } else {\n steps.push(\n \" 1. npx firebase deploy --only firestore:rules,firestore:indexes\" +\n (options.media ? \",storage\" : \"\"),\n )\n steps.push(\" 2. create the first admin account in your Firebase project\")\n steps.push(\" 3. npm run dev\")\n steps.push(` 4. open http://localhost:3000${options.adminRoute}`)\n }\n\n return [\n \"Next:\",\n \"\",\n ...steps,\n \"\",\n \"Content types live in cms.config.ts. Change one, then run `npx nonext-husk doctor`\",\n \"to see whether firestore.indexes.json still covers the queries the list views make.\",\n ].join(\"\\n\")\n}\n","import { readFileSync } from \"node:fs\"\nimport { join, resolve } from \"node:path\"\n\nimport { ArgumentError, HELP, parseArgs } from \"./args.js\"\nimport type { ParsedArgs } from \"./args.js\"\nimport { exitCode, runChecks } from \"./doctor.js\"\nimport { createNodeFs } from \"./fs.js\"\nimport { installedVersion, loadHuskConfig } from \"./load-config.js\"\nimport { optionsFromArgs } from \"./options.js\"\nimport type { InitOptions } from \"./options.js\"\nimport { applyPlan, buildPlan, classifyPlan } from \"./plan.js\"\nimport { inspectProject, ProjectError, rootLayoutBlocker } from \"./project.js\"\nimport { createReadlinePrompter, promptForOptions } from \"./prompt.js\"\nimport { renderChecks, renderConflicts, renderNextSteps, renderPlan } from \"./report.js\"\nimport { createFileTemplates } from \"./rules-template.js\"\n\n/**\n * `nonext-husk`: the binary.\n *\n * Everything it decides lives in the modules beside it, which is why this file is short.\n * What is here is the only part that cannot be unit tested without a process: reading argv,\n * touching a real filesystem, prompting a real terminal, and choosing an exit code.\n *\n * The exit code is part of the contract. `init` exits non-zero when it refuses, and\n * `doctor` exits non-zero when it finds a problem, so both are usable in a CI step without\n * anybody having to grep the output.\n */\n\n/**\n * Where the package's own files are.\n *\n * `dist/cli/nonext-husk.js` and `src/cli/nonext-husk.ts` are both two directories below the\n * package root, so the same expression finds `package.json` from the build and from source.\n * The rules templates only exist beside the build, which is the only place this file runs.\n */\nconst HERE = import.meta.dirname\nconst PACKAGE_ROOT = resolve(HERE, \"..\", \"..\")\n\nfunction packageVersion(): string {\n try {\n const manifest = JSON.parse(\n readFileSync(join(PACKAGE_ROOT, \"package.json\"), \"utf8\"),\n ) as { version?: unknown }\n return typeof manifest.version === \"string\" ? manifest.version : \"0.0.0\"\n } catch {\n return \"0.0.0\"\n }\n}\n\nfunction out(text: string): void {\n process.stdout.write(`${text}\\n`)\n}\n\nfunction fail(text: string): void {\n process.stderr.write(`${text}\\n`)\n}\n\nasync function initCommand(args: ParsedArgs): Promise<number> {\n const root = resolve(args.cwd ?? process.cwd())\n const fs = createNodeFs(root)\n const project = inspectProject(fs)\n\n const blocker = rootLayoutBlocker(project)\n if (blocker !== null) {\n fail(blocker)\n return 1\n }\n\n const options = await collectOptions(args)\n const templates = createFileTemplates(join(HERE, \"templates\"))\n const version = packageVersion()\n\n const plan = buildPlan(fs, project, options, version, templates)\n const outcome = classifyPlan(fs, plan)\n\n if (args.dryRun) {\n out(`Husk ${version} would write, in ${root}:\\n`)\n out(renderPlan(outcome, options))\n if (outcome.conflicts.length > 0 || outcome.moveConflicts.length > 0) {\n out(\"\")\n out(renderConflicts(outcome))\n return 1\n }\n return 0\n }\n\n const blocked = outcome.conflicts.length > 0 || outcome.moveConflicts.length > 0\n if (blocked && !args.force) {\n fail(renderConflicts(outcome))\n return 1\n }\n\n applyPlan(fs, outcome, args.force)\n\n out(`Husk ${version} scaffolded into ${root}:\\n`)\n out(renderPlan(outcome, options))\n out(\"\")\n out(renderNextSteps(options))\n\n return 0\n}\n\nasync function collectOptions(args: ParsedArgs): Promise<InitOptions> {\n if (args.yes) return optionsFromArgs(args)\n\n const prompter = createReadlinePrompter()\n try {\n return await promptForOptions(prompter, args)\n } finally {\n prompter.close()\n }\n}\n\nasync function doctorCommand(args: ParsedArgs): Promise<number> {\n const root = resolve(args.cwd ?? process.cwd())\n const fs = createNodeFs(root)\n const loaded = await loadHuskConfig(root)\n\n const checks = runChecks({\n fs,\n templates: createFileTemplates(join(HERE, \"templates\")),\n installedVersion: installedVersion(root, packageVersion()),\n config: loaded.config,\n contentTypes: loaded.contentTypes,\n })\n\n out(`Husk doctor, in ${root}:\\n`)\n out(renderChecks(checks))\n\n return exitCode(checks)\n}\n\nexport async function main(argv: readonly string[]): Promise<number> {\n let args: ParsedArgs\n try {\n args = parseArgs(argv)\n } catch (thrown) {\n fail(thrown instanceof ArgumentError ? thrown.message : String(thrown))\n fail(\"\")\n fail(HELP)\n return 2\n }\n\n try {\n switch (args.command) {\n case \"init\":\n return await initCommand(args)\n case \"doctor\":\n return await doctorCommand(args)\n case \"version\":\n out(packageVersion())\n return 0\n case \"help\":\n out(HELP)\n return 0\n }\n } catch (thrown) {\n if (thrown instanceof ProjectError || thrown instanceof ArgumentError) {\n fail(thrown.message)\n return 1\n }\n throw thrown\n }\n}\n\nprocess.exitCode = await main(process.argv.slice(2))\n"]}