@answerfox/templates 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anuj Ojha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @answerfox/templates
2
+
3
+ Page templates installed into user projects by the [Answerfox](https://github.com/Anuj7411/answerfox) CLI. Each template is a Next.js App Router page (`.tsx`) wired up with `defineSeo()` from `@answerfox/metadata` and the matching JSON-LD generator from `@answerfox/schemas` — drop one in and you have a working, audit-ready page.
4
+
5
+ > **v0.1.0.** Five templates ship today — About, Privacy, Terms, FAQ, Contact — each wired up with `defineSeo()` and the matching JSON-LD generator.
6
+
7
+ ## Public API
8
+
9
+ ```ts
10
+ import { listTemplates, getTemplate, renderTemplate } from '@answerfox/templates';
11
+
12
+ // Enumerate every available template:
13
+ for (const t of listTemplates()) {
14
+ console.log(t.name, t.filename, t.requiredTokens);
15
+ }
16
+
17
+ // Render a template with substituted token values:
18
+ const source = renderTemplate('about', {
19
+ PROJECT_NAME: 'Acme',
20
+ DOMAIN: 'acme.com',
21
+ URL: 'https://acme.com',
22
+ DESCRIPTION: 'The friendliest widget shop on the internet.',
23
+ });
24
+
25
+ // `source` is now a string of TSX, ready to write to app/about/page.tsx.
26
+ ```
27
+
28
+ ## Templates & audit checks
29
+
30
+ | Template | Filename | Drives audit checks |
31
+ |---|---|---|
32
+ | `about` | `app/about/page.tsx` | D1 (About page), D5 (chrome link), C2 (Organization schema) |
33
+ | `privacy` | `app/privacy/page.tsx` | D2 (Privacy policy), D6 (footer link) |
34
+ | `terms` | `app/terms/page.tsx` | D3 (Terms of use), D6 (footer link) |
35
+ | `faq` | `app/faq/page.tsx` | B5 (FAQ section), C4 (FAQPage schema) |
36
+ | `contact` | `app/contact/page.tsx` | D4 (Contact info), D6 (footer link) |
37
+
38
+ ## Validation
39
+
40
+ `renderTemplate()` rejects:
41
+ - **Missing tokens** — every `{{TOKEN}}` referenced in the template must have a value
42
+ - **Unknown tokens** — every key in your `values` object must correspond to a `{{TOKEN}}` referenced in the template (catches typos at install time)
43
+
44
+ Both errors include the full list of offending tokens so the CLI can surface them all in one prompt.
45
+
46
+ ## License
47
+
48
+ [MIT](../../LICENSE) © 2026 Anuj Ojha
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @answerfox/templates — page templates installed by the Answerfox
3
+ * CLI into a user's Next.js project. Each template is a single .tsx
4
+ * file wired up with `defineSeo()` from `@answerfox/metadata` and
5
+ * the matching JSON-LD generator from `@answerfox/schemas` — drop
6
+ * one in and you have a working, audit-ready page.
7
+ */
8
+ export declare const VERSION = "0.0.0";
9
+ export { extractTokens, renderContent } from './render.js';
10
+ export { getTemplate, listTemplates, renderTemplate } from './registry.js';
11
+ export type { Template, TemplateName, TokenValues } from './types.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC3E,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @answerfox/templates — page templates installed by the Answerfox
3
+ * CLI into a user's Next.js project. Each template is a single .tsx
4
+ * file wired up with `defineSeo()` from `@answerfox/metadata` and
5
+ * the matching JSON-LD generator from `@answerfox/schemas` — drop
6
+ * one in and you have a working, audit-ready page.
7
+ */
8
+ export const VERSION = '0.0.0';
9
+ export { extractTokens, renderContent } from './render.js';
10
+ export { getTemplate, listTemplates, renderTemplate } from './registry.js';
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { Template, TemplateName, TokenValues } from './types.js';
2
+ /**
3
+ * Every template registered with the package, in a stable order
4
+ * matching the AUDIT-FRAMEWORK trust-page sequence (D1 → D4).
5
+ * Useful for `answerable add all` flows in the CLI.
6
+ */
7
+ export declare function listTemplates(): readonly Template[];
8
+ /**
9
+ * Look up a single template by name. Returns the same object across
10
+ * calls — safe to compare by reference if you need to.
11
+ */
12
+ export declare function getTemplate(name: TemplateName): Template;
13
+ /**
14
+ * Render a template by name with the supplied token values. Equivalent
15
+ * to `renderContent(getTemplate(name).content, values)` — see
16
+ * `renderContent` for the validation semantics.
17
+ *
18
+ * @throws SchemaValidationError if any required token is missing or
19
+ * any provided key is unknown to the template.
20
+ */
21
+ export declare function renderTemplate(name: TemplateName, values: TokenValues): string;
22
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAUtE;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,SAAS,QAAQ,EAAE,CAEnD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAExD;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAE9E"}
@@ -0,0 +1,40 @@
1
+ import { renderContent } from './render.js';
2
+ import { aboutTemplate } from './templates/about.js';
3
+ import { contactTemplate } from './templates/contact.js';
4
+ import { faqTemplate } from './templates/faq.js';
5
+ import { privacyTemplate } from './templates/privacy.js';
6
+ import { termsTemplate } from './templates/terms.js';
7
+ const REGISTRY = {
8
+ about: aboutTemplate,
9
+ privacy: privacyTemplate,
10
+ terms: termsTemplate,
11
+ faq: faqTemplate,
12
+ contact: contactTemplate,
13
+ };
14
+ /**
15
+ * Every template registered with the package, in a stable order
16
+ * matching the AUDIT-FRAMEWORK trust-page sequence (D1 → D4).
17
+ * Useful for `answerable add all` flows in the CLI.
18
+ */
19
+ export function listTemplates() {
20
+ return Object.values(REGISTRY);
21
+ }
22
+ /**
23
+ * Look up a single template by name. Returns the same object across
24
+ * calls — safe to compare by reference if you need to.
25
+ */
26
+ export function getTemplate(name) {
27
+ return REGISTRY[name];
28
+ }
29
+ /**
30
+ * Render a template by name with the supplied token values. Equivalent
31
+ * to `renderContent(getTemplate(name).content, values)` — see
32
+ * `renderContent` for the validation semantics.
33
+ *
34
+ * @throws SchemaValidationError if any required token is missing or
35
+ * any provided key is unknown to the template.
36
+ */
37
+ export function renderTemplate(name, values) {
38
+ return renderContent(getTemplate(name).content, values);
39
+ }
40
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,MAAM,QAAQ,GAA6C;IACzD,KAAK,EAAE,aAAa;IACpB,OAAO,EAAE,eAAe;IACxB,KAAK,EAAE,aAAa;IACpB,GAAG,EAAE,WAAW;IAChB,OAAO,EAAE,eAAe;CACzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,IAAkB,EAAE,MAAmB;IACpE,OAAO,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { TokenValues } from './types.js';
2
+ /**
3
+ * Extract the set of distinct token names referenced in a template
4
+ * content string. Used by the registry to derive `requiredTokens`
5
+ * and by `renderTemplate` to check for unknown keys at call time.
6
+ */
7
+ export declare function extractTokens(content: string): readonly string[];
8
+ /**
9
+ * Substitute every `{{TOKEN}}` in `content` with the corresponding
10
+ * value from `values`. Strict on both sides:
11
+ *
12
+ * - Every token referenced in `content` MUST have a value (missing
13
+ * tokens are rejected so the CLI can re-prompt).
14
+ * - Every key in `values` MUST correspond to a referenced token
15
+ * (unknown keys are rejected to catch typos at install time).
16
+ *
17
+ * @throws SchemaValidationError batching the missing-token list and
18
+ * the unknown-token list into one error so the CLI can surface
19
+ * both in a single prompt.
20
+ */
21
+ export declare function renderContent(content: string, values: TokenValues): string;
22
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAQ9C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAwB1E"}
package/dist/render.js ADDED
@@ -0,0 +1,60 @@
1
+ import { SchemaValidationError } from '@answerfox/core';
2
+ /**
3
+ * Matches any `{{TOKEN}}` placeholder. Token names are `[A-Z][A-Z0-9_]*`
4
+ * — uppercase + digits + underscore, leading with a letter.
5
+ */
6
+ const TOKEN_PATTERN = /\{\{([A-Z][A-Z0-9_]*)\}\}/g;
7
+ /**
8
+ * Extract the set of distinct token names referenced in a template
9
+ * content string. Used by the registry to derive `requiredTokens`
10
+ * and by `renderTemplate` to check for unknown keys at call time.
11
+ */
12
+ export function extractTokens(content) {
13
+ const seen = new Set();
14
+ // Iterate matches by repeatedly executing the global regex. Each
15
+ // match's capture group 1 is the token name.
16
+ const matches = content.matchAll(TOKEN_PATTERN);
17
+ for (const m of matches) {
18
+ const token = m[1];
19
+ if (token !== undefined)
20
+ seen.add(token);
21
+ }
22
+ return [...seen].sort();
23
+ }
24
+ /**
25
+ * Substitute every `{{TOKEN}}` in `content` with the corresponding
26
+ * value from `values`. Strict on both sides:
27
+ *
28
+ * - Every token referenced in `content` MUST have a value (missing
29
+ * tokens are rejected so the CLI can re-prompt).
30
+ * - Every key in `values` MUST correspond to a referenced token
31
+ * (unknown keys are rejected to catch typos at install time).
32
+ *
33
+ * @throws SchemaValidationError batching the missing-token list and
34
+ * the unknown-token list into one error so the CLI can surface
35
+ * both in a single prompt.
36
+ */
37
+ export function renderContent(content, values) {
38
+ const referenced = new Set(extractTokens(content));
39
+ const provided = new Set(Object.keys(values));
40
+ const issues = [];
41
+ const missing = [...referenced].filter((t) => !provided.has(t));
42
+ if (missing.length > 0) {
43
+ issues.push(`missing token values: ${missing.sort().join(', ')}`);
44
+ }
45
+ const unknown = [...provided].filter((t) => !referenced.has(t));
46
+ if (unknown.length > 0) {
47
+ issues.push(`unknown token keys (not referenced by template): ${unknown.sort().join(', ')}`);
48
+ }
49
+ if (issues.length > 0) {
50
+ throw new SchemaValidationError(issues);
51
+ }
52
+ // Substitute. `replaceAll` would require ES2021+ which we have, but
53
+ // a single replace with the global regex is just as fast.
54
+ return content.replace(TOKEN_PATTERN, (_, token) => {
55
+ // referenced.has(token) guaranteed by the validation above —
56
+ // provided.has(token) follows from the missing check passing.
57
+ return values[token] ?? '';
58
+ });
59
+ }
60
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGxD;;;GAGG;AACH,MAAM,aAAa,GAAG,4BAA4B,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,iEAAiE;IACjE,6CAA6C;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,MAAmB;IAChE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,IAAI,CAAC,yBAAyB,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,IAAI,CAAC,oDAAoD,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC;IAED,oEAAoE;IACpE,0DAA0D;IAC1D,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,KAAa,EAAE,EAAE;QACzD,6DAA6D;QAC7D,8DAA8D;QAC9D,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Template } from '../types.js';
2
+ export declare const aboutTemplate: Template;
3
+ //# sourceMappingURL=about.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"about.d.ts","sourceRoot":"","sources":["../../src/templates/about.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiD5C,eAAO,MAAM,aAAa,EAAE,QAK3B,CAAC"}
@@ -0,0 +1,53 @@
1
+ const CONTENT = `import { defineSeo } from '@answerfox/metadata';
2
+ import { organization } from '@answerfox/schemas';
3
+
4
+ export const metadata = defineSeo({
5
+ title: 'About {{PROJECT_NAME}}',
6
+ description: 'Learn about {{PROJECT_NAME}}, the team behind {{DOMAIN}}.',
7
+ url: '{{URL}}/about',
8
+ });
9
+
10
+ const orgSchema = organization({
11
+ name: '{{PROJECT_NAME}}',
12
+ url: '{{URL}}',
13
+ description: '{{DESCRIPTION}}',
14
+ });
15
+
16
+ export default function AboutPage() {
17
+ return (
18
+ <main>
19
+ <script
20
+ type="application/ld+json"
21
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(orgSchema) }}
22
+ />
23
+ <h1>About {{PROJECT_NAME}}</h1>
24
+ <p>{{DESCRIPTION}}</p>
25
+
26
+ <h2>Who we are</h2>
27
+ <p>
28
+ Edit this section to introduce the team behind {{PROJECT_NAME}} — who
29
+ you are, where you&apos;re based, and why you started.
30
+ </p>
31
+
32
+ <h2>What we believe</h2>
33
+ <p>
34
+ Edit this section to spell out your principles: how you approach the
35
+ problem, what you refuse to do, what we promise users.
36
+ </p>
37
+
38
+ <h2>Get in touch</h2>
39
+ <p>
40
+ Reach the {{PROJECT_NAME}} team via the{' '}
41
+ <a href="{{URL}}/contact">contact page</a>.
42
+ </p>
43
+ </main>
44
+ );
45
+ }
46
+ `;
47
+ export const aboutTemplate = {
48
+ name: 'about',
49
+ filename: 'app/about/page.tsx',
50
+ content: CONTENT,
51
+ requiredTokens: ['DESCRIPTION', 'DOMAIN', 'PROJECT_NAME', 'URL'],
52
+ };
53
+ //# sourceMappingURL=about.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"about.js","sourceRoot":"","sources":["../../src/templates/about.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Cf,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAa;IACrC,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,oBAAoB;IAC9B,OAAO,EAAE,OAAO;IAChB,cAAc,EAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK,CAAC;CACjE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Template } from '../types.js';
2
+ export declare const contactTemplate: Template;
3
+ //# sourceMappingURL=contact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact.d.ts","sourceRoot":"","sources":["../../src/templates/contact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAsC5C,eAAO,MAAM,eAAe,EAAE,QAK7B,CAAC"}
@@ -0,0 +1,42 @@
1
+ const CONTENT = `import { defineSeo } from '@answerfox/metadata';
2
+
3
+ export const metadata = defineSeo({
4
+ title: 'Contact — {{PROJECT_NAME}}',
5
+ description: 'How to reach the {{PROJECT_NAME}} team.',
6
+ url: '{{URL}}/contact',
7
+ });
8
+
9
+ export default function ContactPage() {
10
+ return (
11
+ <main>
12
+ <h1>Contact {{PROJECT_NAME}}</h1>
13
+ <p>
14
+ The fastest way to reach us is by email at{' '}
15
+ <a href="mailto:{{CONTACT_EMAIL}}">{{CONTACT_EMAIL}}</a>. We aim to
16
+ respond within one business day.
17
+ </p>
18
+
19
+ <h2>Support</h2>
20
+ <p>
21
+ Got a bug or a feature request? Send it to{' '}
22
+ <a href="mailto:{{CONTACT_EMAIL}}">{{CONTACT_EMAIL}}</a> with as much
23
+ detail as you can — what you tried, what happened, and what you
24
+ expected to happen instead.
25
+ </p>
26
+
27
+ <h2>Press &amp; partnerships</h2>
28
+ <p>
29
+ Edit this section with the right contact for press, partnerships, or
30
+ anything that isn&apos;t day-to-day support.
31
+ </p>
32
+ </main>
33
+ );
34
+ }
35
+ `;
36
+ export const contactTemplate = {
37
+ name: 'contact',
38
+ filename: 'app/contact/page.tsx',
39
+ content: CONTENT,
40
+ requiredTokens: ['CONTACT_EMAIL', 'PROJECT_NAME', 'URL'],
41
+ };
42
+ //# sourceMappingURL=contact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact.js","sourceRoot":"","sources":["../../src/templates/contact.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCf,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAa;IACvC,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,sBAAsB;IAChC,OAAO,EAAE,OAAO;IAChB,cAAc,EAAE,CAAC,eAAe,EAAE,cAAc,EAAE,KAAK,CAAC;CACzD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Template } from '../types.js';
2
+ export declare const faqTemplate: Template;
3
+ //# sourceMappingURL=faq.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faq.d.ts","sourceRoot":"","sources":["../../src/templates/faq.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAqE5C,eAAO,MAAM,WAAW,EAAE,QAKzB,CAAC"}
@@ -0,0 +1,73 @@
1
+ const CONTENT = `import { defineSeo } from '@answerfox/metadata';
2
+ import { faqPage } from '@answerfox/schemas';
3
+
4
+ export const metadata = defineSeo({
5
+ title: 'FAQ — {{PROJECT_NAME}}',
6
+ description: 'Frequently asked questions about {{PROJECT_NAME}}.',
7
+ url: '{{URL}}/faq',
8
+ });
9
+
10
+ const faqSchema = faqPage({
11
+ questions: [
12
+ {
13
+ question: 'What is {{PROJECT_NAME}}?',
14
+ answer: '{{DESCRIPTION}}',
15
+ },
16
+ {
17
+ question: 'How do I get started?',
18
+ answer:
19
+ 'Visit {{URL}} and follow the prompts. Edit this answer with your real onboarding steps.',
20
+ },
21
+ {
22
+ question: 'How much does {{PROJECT_NAME}} cost?',
23
+ answer:
24
+ 'Edit this answer with your pricing details — or link to a pricing page if you have one.',
25
+ },
26
+ {
27
+ question: 'How do I contact support?',
28
+ answer:
29
+ 'Email us at {{CONTACT_EMAIL}} and we will respond within one business day.',
30
+ },
31
+ ],
32
+ });
33
+
34
+ export default function FaqPage() {
35
+ return (
36
+ <main>
37
+ <script
38
+ type="application/ld+json"
39
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
40
+ />
41
+ <h1>Frequently asked questions</h1>
42
+
43
+ <h2>What is {{PROJECT_NAME}}?</h2>
44
+ <p>{{DESCRIPTION}}</p>
45
+
46
+ <h2>How do I get started?</h2>
47
+ <p>
48
+ Visit <a href="{{URL}}">{{URL}}</a> and follow the prompts. Edit this
49
+ answer with your real onboarding steps.
50
+ </p>
51
+
52
+ <h2>How much does {{PROJECT_NAME}} cost?</h2>
53
+ <p>
54
+ Edit this answer with your pricing details — or link to a pricing page
55
+ if you have one.
56
+ </p>
57
+
58
+ <h2>How do I contact support?</h2>
59
+ <p>
60
+ Email us at <a href="mailto:{{CONTACT_EMAIL}}">{{CONTACT_EMAIL}}</a> and
61
+ we will respond within one business day.
62
+ </p>
63
+ </main>
64
+ );
65
+ }
66
+ `;
67
+ export const faqTemplate = {
68
+ name: 'faq',
69
+ filename: 'app/faq/page.tsx',
70
+ content: CONTENT,
71
+ requiredTokens: ['CONTACT_EMAIL', 'DESCRIPTION', 'PROJECT_NAME', 'URL'],
72
+ };
73
+ //# sourceMappingURL=faq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"faq.js","sourceRoot":"","sources":["../../src/templates/faq.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiEf,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAa;IACnC,IAAI,EAAE,KAAK;IACX,QAAQ,EAAE,kBAAkB;IAC5B,OAAO,EAAE,OAAO;IAChB,cAAc,EAAE,CAAC,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,CAAC;CACxE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Template } from '../types.js';
2
+ export declare const privacyTemplate: Template;
3
+ //# sourceMappingURL=privacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/templates/privacy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAsD5C,eAAO,MAAM,eAAe,EAAE,QAK7B,CAAC"}
@@ -0,0 +1,58 @@
1
+ const CONTENT = `import { defineSeo } from '@answerfox/metadata';
2
+
3
+ export const metadata = defineSeo({
4
+ title: 'Privacy Policy — {{PROJECT_NAME}}',
5
+ description: 'How {{PROJECT_NAME}} collects, uses, and protects your data.',
6
+ url: '{{URL}}/privacy',
7
+ });
8
+
9
+ export default function PrivacyPage() {
10
+ return (
11
+ <main>
12
+ <h1>Privacy Policy</h1>
13
+ <p>
14
+ <em>Effective {{EFFECTIVE_DATE}}.</em>
15
+ </p>
16
+
17
+ <h2>What we collect</h2>
18
+ <p>
19
+ {{PROJECT_NAME}} (operated from {{DOMAIN}}) collects only the data needed
20
+ to provide our service. Edit this section to list every category of
21
+ personal data your application collects from users.
22
+ </p>
23
+
24
+ <h2>How we use it</h2>
25
+ <p>
26
+ We use collected data to operate, maintain, and improve {{PROJECT_NAME}}.
27
+ We never sell your personal data to third parties.
28
+ </p>
29
+
30
+ <h2>Who we share with</h2>
31
+ <p>
32
+ Edit this section to list every third-party processor (analytics,
33
+ payments, transactional email, etc.) and link to their own privacy policy.
34
+ </p>
35
+
36
+ <h2>Your rights</h2>
37
+ <p>
38
+ You may request a copy of your data, ask us to delete it, or correct
39
+ inaccurate records by emailing{' '}
40
+ <a href="mailto:{{CONTACT_EMAIL}}">{{CONTACT_EMAIL}}</a>.
41
+ </p>
42
+
43
+ <h2>Contact</h2>
44
+ <p>
45
+ Questions about this policy? Reach us at{' '}
46
+ <a href="mailto:{{CONTACT_EMAIL}}">{{CONTACT_EMAIL}}</a>.
47
+ </p>
48
+ </main>
49
+ );
50
+ }
51
+ `;
52
+ export const privacyTemplate = {
53
+ name: 'privacy',
54
+ filename: 'app/privacy/page.tsx',
55
+ content: CONTENT,
56
+ requiredTokens: ['CONTACT_EMAIL', 'DOMAIN', 'EFFECTIVE_DATE', 'PROJECT_NAME', 'URL'],
57
+ };
58
+ //# sourceMappingURL=privacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/templates/privacy.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDf,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAa;IACvC,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,sBAAsB;IAChC,OAAO,EAAE,OAAO;IAChB,cAAc,EAAE,CAAC,eAAe,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,KAAK,CAAC;CACrF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Template } from '../types.js';
2
+ export declare const termsTemplate: Template;
3
+ //# sourceMappingURL=terms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terms.d.ts","sourceRoot":"","sources":["../../src/templates/terms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA8D5C,eAAO,MAAM,aAAa,EAAE,QAK3B,CAAC"}
@@ -0,0 +1,66 @@
1
+ const CONTENT = `import { defineSeo } from '@answerfox/metadata';
2
+
3
+ export const metadata = defineSeo({
4
+ title: 'Terms of Use — {{PROJECT_NAME}}',
5
+ description: 'The rules of the road when using {{PROJECT_NAME}}.',
6
+ url: '{{URL}}/terms',
7
+ });
8
+
9
+ export default function TermsPage() {
10
+ return (
11
+ <main>
12
+ <h1>Terms of Use</h1>
13
+ <p>
14
+ <em>Effective {{EFFECTIVE_DATE}}.</em>
15
+ </p>
16
+
17
+ <h2>Agreement</h2>
18
+ <p>
19
+ By accessing or using {{PROJECT_NAME}} (the &quot;Service&quot;, at{' '}
20
+ <a href="https://{{DOMAIN}}">{{DOMAIN}}</a>), you agree to these Terms of
21
+ Use. If you do not agree, do not use the Service.
22
+ </p>
23
+
24
+ <h2>Acceptable use</h2>
25
+ <p>
26
+ You agree not to use the Service to violate any law, infringe anyone&apos;s
27
+ rights, transmit malware, scrape at unreasonable rates, or impersonate
28
+ any person.
29
+ </p>
30
+
31
+ <h2>Intellectual property</h2>
32
+ <p>
33
+ All content, branding, and code that makes up {{PROJECT_NAME}} is owned
34
+ by us or our licensors. You retain ownership of any content you submit.
35
+ </p>
36
+
37
+ <h2>Disclaimer and limitation of liability</h2>
38
+ <p>
39
+ The Service is provided &quot;as is.&quot; To the maximum extent permitted
40
+ by law, {{PROJECT_NAME}} disclaims all warranties and shall not be liable
41
+ for any indirect, incidental, or consequential damages.
42
+ </p>
43
+
44
+ <h2>Governing law</h2>
45
+ <p>
46
+ These Terms are governed by the laws of {{JURISDICTION}}, without regard
47
+ to its conflict-of-laws provisions.
48
+ </p>
49
+
50
+ <h2>Changes</h2>
51
+ <p>
52
+ We may update these Terms; the &quot;Effective&quot; date above will
53
+ always reflect the current version. Continued use of the Service after
54
+ an update constitutes acceptance of the revised Terms.
55
+ </p>
56
+ </main>
57
+ );
58
+ }
59
+ `;
60
+ export const termsTemplate = {
61
+ name: 'terms',
62
+ filename: 'app/terms/page.tsx',
63
+ content: CONTENT,
64
+ requiredTokens: ['DOMAIN', 'EFFECTIVE_DATE', 'JURISDICTION', 'PROJECT_NAME', 'URL'],
65
+ };
66
+ //# sourceMappingURL=terms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terms.js","sourceRoot":"","sources":["../../src/templates/terms.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Df,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAa;IACrC,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,oBAAoB;IAC9B,OAAO,EAAE,OAAO;IAChB,cAAc,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,cAAc,EAAE,KAAK,CAAC;CACpF,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Catalog of templates Answerfox's CLI can install. Stable string
3
+ * literals — these become the values users pass to
4
+ * `answerable add <name>`.
5
+ */
6
+ export type TemplateName = 'about' | 'privacy' | 'terms' | 'faq' | 'contact';
7
+ /**
8
+ * Map of token name → substitution value. Both keys and values are
9
+ * plain strings; the renderer rejects non-string values at runtime.
10
+ */
11
+ export type TokenValues = Readonly<Record<string, string>>;
12
+ /**
13
+ * Describes one installable page template. Each template module
14
+ * exports a `Template` constant via `src/templates/<name>.ts`.
15
+ */
16
+ export interface Template {
17
+ readonly name: TemplateName;
18
+ /**
19
+ * Path relative to the user's project root where the rendered file
20
+ * is written, e.g. `"app/about/page.tsx"`.
21
+ */
22
+ readonly filename: string;
23
+ /** Raw template source with `{{TOKEN}}` placeholders. */
24
+ readonly content: string;
25
+ /**
26
+ * Tokens that must be present in the values map at render time.
27
+ * Auto-derived from `content` when the module is loaded — the
28
+ * registry asserts the explicit list matches the derived list.
29
+ */
30
+ readonly requiredTokens: readonly string[];
31
+ }
32
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;AAE7E;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@answerfox/templates",
3
+ "version": "0.1.1",
4
+ "description": "Page templates (About, Privacy, Terms, FAQ, Contact) for the Answerfox SEO toolkit. Installed into user projects by the CLI.",
5
+ "license": "MIT",
6
+ "author": "Anuj Ojha",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Anuj7411/answerfox.git",
10
+ "directory": "packages/templates"
11
+ },
12
+ "homepage": "https://github.com/Anuj7411/answerfox/tree/main/packages/templates#readme",
13
+ "bugs": "https://github.com/Anuj7411/answerfox/issues",
14
+ "type": "module",
15
+ "main": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md"
26
+ ],
27
+ "sideEffects": false,
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "dependencies": {
32
+ "@answerfox/core": "0.1.1"
33
+ },
34
+ "devDependencies": {
35
+ "rimraf": "^6.0.1",
36
+ "typescript": "^5.7.3",
37
+ "vitest": "^3.0.5"
38
+ },
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.build.json",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "typecheck": "tsc --noEmit",
44
+ "clean": "rimraf dist .tsbuildinfo"
45
+ }
46
+ }