@adkinn/astro-ai-readiness 0.0.7 → 0.0.9
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/README.md +29 -13
- package/dist/{chunk-CADNETPK.js → chunk-4PXOR6CE.js} +11 -2
- package/dist/{chunk-THSVWYUX.js → chunk-4VPGX5IP.js} +3 -2
- package/dist/{chunk-WP6D4L7O.js → chunk-WYKIPW5J.js} +3 -2
- package/dist/{chunk-CO2DB4IE.js → chunk-XYQNZKKD.js} +56 -3
- package/dist/components/OrganizationSchema.astro +37 -20
- package/dist/components/PersonSchema.astro +26 -0
- package/dist/components/SoftwareApplicationSchema.astro +64 -0
- package/dist/components/WebSiteSchema.astro +8 -2
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +14 -10
- package/dist/components/types.d.ts +2 -0
- package/dist/config.d.ts +756 -38
- package/dist/config.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -4
- package/dist/outputs/agents-md.js +1 -1
- package/dist/outputs/llms-full.js +1 -1
- package/dist/outputs/llms-txt.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# @adkinn/astro-ai-readiness
|
|
2
2
|
|
|
3
|
-
> AI Readiness toolkit for Astro —
|
|
3
|
+
> AI Readiness toolkit for Astro — eight JSON-LD helper components plus `dist/llms.txt`, `dist/llms-full.txt`, `dist/agents.md`, `dist/robots.txt`, and `dist/.well-known/mcp.json`.
|
|
4
4
|
|
|
5
|
-
**Status:** v0.0.
|
|
5
|
+
**Status:** v0.0.9 — app/product support. Adds a `<SoftwareApplicationSchema />` component (screenshots, feature list, offers), Organization `description` + `contactPoint`, `founder.url`, WebSite `inLanguage`, and custom `## sections` for `agents.md` — everything an app/product site needs at full fidelity.
|
|
6
6
|
|
|
7
|
-
## What ships in v0.0.
|
|
7
|
+
## What ships in v0.0.9
|
|
8
8
|
|
|
9
|
-
**
|
|
10
|
-
- `<OrganizationSchema />` — Organization block. Config-driven; place in your `BaseLayout` so it ships site-wide and the `#organization` `@id` reference resolves on every page.
|
|
11
|
-
- `<
|
|
9
|
+
**Eight JSON-LD components**:
|
|
10
|
+
- `<OrganizationSchema />` — Organization block. Config-driven from `organization` (optional; now supports `description`, `contactPoint`, and `founder.url`); place in your `BaseLayout` so it ships site-wide and the `#organization` `@id` reference resolves on every page. Renders nothing if `organization` is unset.
|
|
11
|
+
- `<PersonSchema />` — Person block. Config-driven from `person` (optional); for sites whose primary identity is an individual. Place in your `BaseLayout`; declares `#person`, and becomes the WebSite publisher when set. Renders nothing if `person` is unset.
|
|
12
|
+
- `<SoftwareApplicationSchema />` — SoftwareApplication block. Config-driven from `softwareApplication` (optional); for app/product sites. Emits `#app` with `operatingSystem`, `applicationCategory`, `screenshot[]`, `featureList[]`, `offers[]`, and links `publisher`/`author` to the site's Organization (or Person). Renders nothing if unset.
|
|
13
|
+
- `<WebSiteSchema />` — WebSite block. Config-driven (now supports `inLanguage`); alongside `<OrganizationSchema />` or `<PersonSchema />` in `BaseLayout`.
|
|
12
14
|
- `<CollectionSchema name url description? />` — CollectionPage block. Props-driven; place on collection-index pages (`/articles/`, `/tags/[tag]/`, etc.).
|
|
13
15
|
- `<BreadcrumbSchema items={[{ name, url }, ...]} />` — BreadcrumbList. Items-array prop; place on multi-level pages where the navigation hierarchy isn't already declared inline. Empty `items` skips emission.
|
|
14
16
|
- `<FAQPageSchema items={[{ question, answer }, ...]} />` — FAQPage. Items-array prop; place on pages with FAQ data. Long-form answers escape `</script>` and U+2028 / U+2029 automatically. Empty `items` skips emission.
|
|
@@ -17,13 +19,13 @@
|
|
|
17
19
|
**Five file outputs**:
|
|
18
20
|
- `dist/llms.txt` — [llmstxt.org](https://llmstxt.org/) format from the `llmsTxt` config block. H1 / blockquote summary / optional body / H2 sections with bulleted links / optional canonical-reference footer. Opt-in.
|
|
19
21
|
- `dist/llms-full.txt` — manual full-context markdown from the `llmsFull` config block. This is config-driven in v0.0.7; content-collection introspection is a later layer.
|
|
20
|
-
- `dist/agents.md` — Markdown discovery file for AI-agent crawlers from the `agentsMd` config block. H1 / blockquote description / optional `## Audience`, `## Contact`, `## Links
|
|
22
|
+
- `dist/agents.md` — Markdown discovery file for AI-agent crawlers from the `agentsMd` config block. H1 / blockquote description / optional `## Audience`, arbitrary `## sections` (title + markdown content), `## Contact`, and `## Links`. Opt-in.
|
|
21
23
|
- `dist/.well-known/mcp.json` — [Model Context Protocol](https://modelcontextprotocol.io/) discovery file from the `mcp` config block. Pretty-printed JSON with `$schema` reference to the toolkit-published v1 shape (per D-22). Supports `status: 'active'` (requires `url` + `tools[]`) and `status: 'planned'` (requires `planned_tools[]`; `url` forbidden by schema). Opt-in.
|
|
22
24
|
- `dist/robots.txt` — robots policy composition from the `robotsTxt` config block. Presets: `search-visible`, `training-opt-out` (default), and `private`; supports explicit bot rules, `Sitemap`, and optional `Content-Signal` directives.
|
|
23
25
|
|
|
24
26
|
**`@astrojs/sitemap` detection per D-8.** When you call `aiReadiness({...})` and `@astrojs/sitemap` isn't in your integrations list, the toolkit logs a build-time warning. Sitemap is an AI-Readiness baseline; when `robotsTxt` is enabled, the generated `robots.txt` includes a `Sitemap` line by default.
|
|
25
27
|
|
|
26
|
-
All
|
|
28
|
+
All eight components emit canonical Schema.org JSON-LD with cross-component `@id` references (`#organization`, `#person`, `#app`, `#website`) so search and AI consumers can resolve the entity graph without redeclaring shared fields.
|
|
27
29
|
|
|
28
30
|
URL config fields (`site`, `organization.url`, `organization.logo`, `founder.sameAs`, `llmsTxt.*.url`) require `https://` (or `http://localhost` for dev).
|
|
29
31
|
|
|
@@ -31,7 +33,7 @@ Config validation errors are formatted with one issue per line — `path: messag
|
|
|
31
33
|
|
|
32
34
|
## On the roadmap (toward v0.1.0)
|
|
33
35
|
|
|
34
|
-
- v0.0.8 — fixture Astro app validation against
|
|
36
|
+
- v0.0.8 — fixture Astro app validation against adamkinney.com install paths; refine robots presets from real traffic policy.
|
|
35
37
|
- v0.0.9 — content-collection assisted `llms-full.txt` generation.
|
|
36
38
|
- v0.1.0 — polish, docs, release hardening; full install on adamkinney.com
|
|
37
39
|
|
|
@@ -45,7 +47,7 @@ npm install @adkinn/astro-ai-readiness
|
|
|
45
47
|
|
|
46
48
|
## Quick start
|
|
47
49
|
|
|
48
|
-
Configure (v0.0.
|
|
50
|
+
Configure (v0.0.9 accepts `site`, optional `person`, optional `organization`, optional `softwareApplication`, optional `webSite`, optional `llmsTxt`, optional `llmsFull`, optional `agentsMd`, optional `mcp`, and optional `robotsTxt` blocks — provide at least one of `person`/`organization` as the site identity. The Zod schema rejects unknown keys, and URL fields must use `https://` or `http://localhost`):
|
|
49
51
|
|
|
50
52
|
```ts
|
|
51
53
|
// astro.config.mjs
|
|
@@ -185,7 +187,22 @@ import { OrganizationSchema, WebSiteSchema } from '@adkinn/astro-ai-readiness/co
|
|
|
185
187
|
</head>
|
|
186
188
|
```
|
|
187
189
|
|
|
188
|
-
`<OrganizationSchema />` declares `@id: '<site>#organization'`; `<WebSiteSchema />` references it. Order matters in the head —
|
|
190
|
+
`<OrganizationSchema />` declares `@id: '<site>#organization'`; `<WebSiteSchema />` references it. Order matters in the head — identity component first.
|
|
191
|
+
|
|
192
|
+
**Personal-brand sites** lead with a `Person` instead. Configure a `person` block (and omit `organization` if you don't need it), then:
|
|
193
|
+
|
|
194
|
+
```astro
|
|
195
|
+
---
|
|
196
|
+
// src/layouts/BaseLayout.astro — site-wide
|
|
197
|
+
import { PersonSchema, WebSiteSchema } from '@adkinn/astro-ai-readiness/components'
|
|
198
|
+
---
|
|
199
|
+
<head>
|
|
200
|
+
<PersonSchema />
|
|
201
|
+
<WebSiteSchema />
|
|
202
|
+
</head>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`<PersonSchema />` declares `@id: '<site>#person'`; when `person` is set, `<WebSiteSchema />` publisher and the `llms.txt` heading follow it.
|
|
189
206
|
|
|
190
207
|
```astro
|
|
191
208
|
---
|
|
@@ -273,8 +290,7 @@ The v0.1 line is content → artifacts: components and files. Build-time AI-read
|
|
|
273
290
|
|
|
274
291
|
## Shipped on
|
|
275
292
|
|
|
276
|
-
- [
|
|
277
|
-
- [adamkinney.com](https://adamkinney.com) — Adam Kinney's site. Next SSR reference-implementation target once v0.1.0 publishes.
|
|
293
|
+
- [adamkinney.com](https://adamkinney.com) — the reference implementation. Runs `OrganizationSchema` + `WebSiteSchema` site-wide and emits `dist/llms.txt` at <https://adamkinney.com/llms.txt> and `dist/agents.md` at <https://adamkinney.com/agents.md>.
|
|
278
294
|
|
|
279
295
|
## Contributing
|
|
280
296
|
|
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/outputs/agents-md.ts
|
|
6
6
|
function composeAgentsMd(config) {
|
|
7
|
-
const { agentsMd
|
|
7
|
+
const { agentsMd } = config;
|
|
8
|
+
const name = config.person?.name ?? config.organization?.name ?? config.webSite?.name ?? config.site;
|
|
8
9
|
const lines = [];
|
|
9
|
-
lines.push(`# ${
|
|
10
|
+
lines.push(`# ${name}`);
|
|
10
11
|
lines.push("");
|
|
11
12
|
lines.push(`> ${agentsMd.description}`);
|
|
12
13
|
lines.push("");
|
|
@@ -16,6 +17,14 @@ function composeAgentsMd(config) {
|
|
|
16
17
|
lines.push(agentsMd.audience.trimEnd());
|
|
17
18
|
lines.push("");
|
|
18
19
|
}
|
|
20
|
+
if (agentsMd.sections) {
|
|
21
|
+
for (const section of agentsMd.sections) {
|
|
22
|
+
lines.push(`## ${section.title}`);
|
|
23
|
+
lines.push("");
|
|
24
|
+
lines.push(section.content.trimEnd());
|
|
25
|
+
lines.push("");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
19
28
|
if (agentsMd.contact) {
|
|
20
29
|
lines.push("## Contact");
|
|
21
30
|
lines.push("");
|
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/outputs/llms-full.ts
|
|
6
6
|
function composeLlmsFullTxt(config) {
|
|
7
|
-
const { llmsFull, llmsTxt
|
|
7
|
+
const { llmsFull, llmsTxt } = config;
|
|
8
|
+
const name = llmsFull.title ?? config.person?.name ?? config.organization?.name ?? config.site;
|
|
8
9
|
const lines = [];
|
|
9
|
-
lines.push(`# ${
|
|
10
|
+
lines.push(`# ${name}`);
|
|
10
11
|
lines.push("");
|
|
11
12
|
const summary = llmsFull.summary ?? llmsTxt?.summary;
|
|
12
13
|
if (summary) {
|
|
@@ -4,9 +4,10 @@ import {
|
|
|
4
4
|
|
|
5
5
|
// src/outputs/llms-txt.ts
|
|
6
6
|
function composeLlmsTxt(config) {
|
|
7
|
-
const { llmsTxt
|
|
7
|
+
const { llmsTxt } = config;
|
|
8
|
+
const name = config.person?.name ?? config.organization?.name ?? config.webSite?.name ?? config.site;
|
|
8
9
|
const lines = [];
|
|
9
|
-
lines.push(`# ${
|
|
10
|
+
lines.push(`# ${name}`);
|
|
10
11
|
lines.push("");
|
|
11
12
|
lines.push(`> ${llmsTxt.summary}`);
|
|
12
13
|
lines.push("");
|
|
@@ -8,21 +8,64 @@ var founderSchema = z.object({
|
|
|
8
8
|
name: z.string().min(1),
|
|
9
9
|
jobTitle: z.string().optional(),
|
|
10
10
|
description: z.string().optional(),
|
|
11
|
+
url: httpsUrl.optional(),
|
|
11
12
|
sameAs: z.array(httpsUrl).optional()
|
|
12
13
|
});
|
|
14
|
+
var contactPointSchema = z.object({
|
|
15
|
+
contactType: z.string().min(1),
|
|
16
|
+
email: z.string().email().optional(),
|
|
17
|
+
telephone: z.string().min(1).optional(),
|
|
18
|
+
url: httpsUrl.optional()
|
|
19
|
+
}).strict();
|
|
13
20
|
var organizationSchema = z.object({
|
|
14
21
|
name: z.string().min(1),
|
|
15
22
|
url: httpsUrl.optional(),
|
|
16
23
|
logo: httpsUrl.optional(),
|
|
24
|
+
description: z.string().optional(),
|
|
17
25
|
founder: founderSchema.optional(),
|
|
18
26
|
foundingDate: z.string().optional(),
|
|
19
27
|
knowsAbout: z.array(z.string()).optional(),
|
|
20
|
-
areaServed: z.string().optional()
|
|
28
|
+
areaServed: z.string().optional(),
|
|
29
|
+
contactPoint: contactPointSchema.optional()
|
|
30
|
+
}).strict();
|
|
31
|
+
var personSchema = z.object({
|
|
32
|
+
name: z.string().min(1),
|
|
33
|
+
url: httpsUrl.optional(),
|
|
34
|
+
jobTitle: z.string().optional(),
|
|
35
|
+
description: z.string().optional(),
|
|
36
|
+
image: httpsUrl.optional(),
|
|
37
|
+
sameAs: z.array(httpsUrl).optional(),
|
|
38
|
+
knowsAbout: z.array(z.string()).optional()
|
|
21
39
|
}).strict();
|
|
22
40
|
var webSiteSchema = z.object({
|
|
23
41
|
name: z.string().optional(),
|
|
42
|
+
description: z.string().optional(),
|
|
43
|
+
inLanguage: z.string().optional()
|
|
44
|
+
}).strict();
|
|
45
|
+
var offerSchema = z.object({
|
|
46
|
+
name: z.string().min(1).optional(),
|
|
47
|
+
price: z.string().min(1),
|
|
48
|
+
priceCurrency: z.string().min(1),
|
|
24
49
|
description: z.string().optional()
|
|
25
50
|
}).strict();
|
|
51
|
+
var softwareApplicationSchema = z.object({
|
|
52
|
+
name: z.string().min(1),
|
|
53
|
+
applicationCategory: z.string().optional(),
|
|
54
|
+
applicationSubCategory: z.string().optional(),
|
|
55
|
+
operatingSystem: z.string().optional(),
|
|
56
|
+
description: z.string().optional(),
|
|
57
|
+
url: httpsUrl.optional(),
|
|
58
|
+
image: httpsUrl.optional(),
|
|
59
|
+
screenshot: z.array(httpsUrl).optional(),
|
|
60
|
+
featureList: z.array(z.string()).optional(),
|
|
61
|
+
offers: z.array(offerSchema).optional(),
|
|
62
|
+
installUrl: httpsUrl.optional(),
|
|
63
|
+
downloadUrl: httpsUrl.optional(),
|
|
64
|
+
aggregateRating: z.object({
|
|
65
|
+
ratingValue: z.number(),
|
|
66
|
+
ratingCount: z.number().int().positive()
|
|
67
|
+
}).strict().optional()
|
|
68
|
+
}).strict();
|
|
26
69
|
var llmsTxtLinkSchema = z.object({
|
|
27
70
|
title: z.string().min(1),
|
|
28
71
|
url: httpsUrl,
|
|
@@ -62,12 +105,17 @@ var agentsMdLinkSchema = z.object({
|
|
|
62
105
|
url: httpsUrl,
|
|
63
106
|
description: z.string().optional()
|
|
64
107
|
}).strict();
|
|
108
|
+
var agentsMdSectionSchema = z.object({
|
|
109
|
+
title: z.string().min(1),
|
|
110
|
+
content: z.string().min(1)
|
|
111
|
+
}).strict();
|
|
65
112
|
var agentsMdSchema = z.object({
|
|
66
113
|
description: z.string().min(1).refine(
|
|
67
114
|
(s) => !s.includes("\n"),
|
|
68
115
|
{ message: "description must be single-line \u2014 use audience/contact fields for additional prose" }
|
|
69
116
|
),
|
|
70
117
|
audience: z.string().optional(),
|
|
118
|
+
sections: z.array(agentsMdSectionSchema).min(1).optional(),
|
|
71
119
|
contact: z.string().optional(),
|
|
72
120
|
links: z.array(agentsMdLinkSchema).min(1).optional()
|
|
73
121
|
}).strict();
|
|
@@ -127,14 +175,19 @@ var robotsTxtSchema = z.object({
|
|
|
127
175
|
}).strict();
|
|
128
176
|
var aiReadinessConfigSchema = z.object({
|
|
129
177
|
site: httpsUrl,
|
|
130
|
-
organization: organizationSchema,
|
|
178
|
+
organization: organizationSchema.optional(),
|
|
179
|
+
person: personSchema.optional(),
|
|
180
|
+
softwareApplication: softwareApplicationSchema.optional(),
|
|
131
181
|
webSite: webSiteSchema.optional(),
|
|
132
182
|
llmsTxt: llmsTxtSchema.optional(),
|
|
133
183
|
llmsFull: llmsFullSchema.optional(),
|
|
134
184
|
agentsMd: agentsMdSchema.optional(),
|
|
135
185
|
mcp: mcpSchema.optional(),
|
|
136
186
|
robotsTxt: robotsTxtSchema.optional()
|
|
137
|
-
}).strict()
|
|
187
|
+
}).strict().refine(
|
|
188
|
+
(value) => Boolean(value.person || value.organization),
|
|
189
|
+
{ message: "Provide `person` and/or `organization` \u2014 the site needs at least one identity for the llms.txt heading and JSON-LD." }
|
|
190
|
+
);
|
|
138
191
|
|
|
139
192
|
export {
|
|
140
193
|
aiReadinessConfigSchema
|
|
@@ -6,28 +6,45 @@ const org = config.organization
|
|
|
6
6
|
|
|
7
7
|
type SchemaJson = Record<string, unknown>
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
let orgLd: SchemaJson | null = null
|
|
10
|
+
|
|
11
|
+
if (org) {
|
|
12
|
+
orgLd = {
|
|
13
|
+
'@context': 'https://schema.org',
|
|
14
|
+
'@type': 'Organization',
|
|
15
|
+
'@id': `${config.site}#organization`,
|
|
16
|
+
name: org.name,
|
|
17
|
+
}
|
|
15
18
|
|
|
16
|
-
if (org.url) orgLd.url = org.url
|
|
17
|
-
if (org.logo) orgLd.logo = org.logo
|
|
18
|
-
if (org.
|
|
19
|
-
if (org.
|
|
20
|
-
if (org.
|
|
19
|
+
if (org.url) orgLd.url = org.url
|
|
20
|
+
if (org.logo) orgLd.logo = org.logo
|
|
21
|
+
if (org.description) orgLd.description = org.description
|
|
22
|
+
if (org.foundingDate) orgLd.foundingDate = org.foundingDate
|
|
23
|
+
if (org.knowsAbout && org.knowsAbout.length > 0) orgLd.knowsAbout = org.knowsAbout
|
|
24
|
+
if (org.areaServed) orgLd.areaServed = org.areaServed
|
|
25
|
+
|
|
26
|
+
if (org.founder) {
|
|
27
|
+
const founder: SchemaJson = {
|
|
28
|
+
'@type': 'Person',
|
|
29
|
+
name: org.founder.name,
|
|
30
|
+
}
|
|
31
|
+
if (org.founder.jobTitle) founder.jobTitle = org.founder.jobTitle
|
|
32
|
+
if (org.founder.description) founder.description = org.founder.description
|
|
33
|
+
if (org.founder.url) founder.url = org.founder.url
|
|
34
|
+
if (org.founder.sameAs && org.founder.sameAs.length > 0) founder.sameAs = org.founder.sameAs
|
|
35
|
+
orgLd.founder = founder
|
|
36
|
+
}
|
|
21
37
|
|
|
22
|
-
if (org.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
if (org.contactPoint) {
|
|
39
|
+
const cp: SchemaJson = {
|
|
40
|
+
'@type': 'ContactPoint',
|
|
41
|
+
contactType: org.contactPoint.contactType,
|
|
42
|
+
}
|
|
43
|
+
if (org.contactPoint.email) cp.email = org.contactPoint.email
|
|
44
|
+
if (org.contactPoint.telephone) cp.telephone = org.contactPoint.telephone
|
|
45
|
+
if (org.contactPoint.url) cp.url = org.contactPoint.url
|
|
46
|
+
orgLd.contactPoint = cp
|
|
26
47
|
}
|
|
27
|
-
if (org.founder.jobTitle) founder.jobTitle = org.founder.jobTitle
|
|
28
|
-
if (org.founder.description) founder.description = org.founder.description
|
|
29
|
-
if (org.founder.sameAs && org.founder.sameAs.length > 0) founder.sameAs = org.founder.sameAs
|
|
30
|
-
orgLd.founder = founder
|
|
31
48
|
}
|
|
32
49
|
---
|
|
33
|
-
<script type="application/ld+json" is:inline set:html={jsonLd(orgLd)}></script>
|
|
50
|
+
{orgLd && <script type="application/ld+json" is:inline set:html={jsonLd(orgLd)}></script>}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { config } from 'virtual:ai-readiness-config'
|
|
3
|
+
import { jsonLd } from '../utils/json-ld'
|
|
4
|
+
|
|
5
|
+
const person = config.person
|
|
6
|
+
|
|
7
|
+
type SchemaJson = Record<string, unknown>
|
|
8
|
+
|
|
9
|
+
let personLd: SchemaJson | null = null
|
|
10
|
+
|
|
11
|
+
if (person) {
|
|
12
|
+
personLd = {
|
|
13
|
+
'@context': 'https://schema.org',
|
|
14
|
+
'@type': 'Person',
|
|
15
|
+
'@id': `${config.site}#person`,
|
|
16
|
+
name: person.name,
|
|
17
|
+
}
|
|
18
|
+
if (person.url) personLd.url = person.url
|
|
19
|
+
if (person.jobTitle) personLd.jobTitle = person.jobTitle
|
|
20
|
+
if (person.description) personLd.description = person.description
|
|
21
|
+
if (person.image) personLd.image = person.image
|
|
22
|
+
if (person.sameAs && person.sameAs.length > 0) personLd.sameAs = person.sameAs
|
|
23
|
+
if (person.knowsAbout && person.knowsAbout.length > 0) personLd.knowsAbout = person.knowsAbout
|
|
24
|
+
}
|
|
25
|
+
---
|
|
26
|
+
{personLd && <script type="application/ld+json" is:inline set:html={jsonLd(personLd)}></script>}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { config } from 'virtual:ai-readiness-config'
|
|
3
|
+
import { jsonLd } from '../utils/json-ld'
|
|
4
|
+
|
|
5
|
+
const app = config.softwareApplication
|
|
6
|
+
|
|
7
|
+
type SchemaJson = Record<string, unknown>
|
|
8
|
+
|
|
9
|
+
// Link publisher/author to the site identity (Organization preferred, else Person).
|
|
10
|
+
const identityId = config.organization
|
|
11
|
+
? `${config.site}#organization`
|
|
12
|
+
: config.person
|
|
13
|
+
? `${config.site}#person`
|
|
14
|
+
: null
|
|
15
|
+
|
|
16
|
+
let appLd: SchemaJson | null = null
|
|
17
|
+
|
|
18
|
+
if (app) {
|
|
19
|
+
appLd = {
|
|
20
|
+
'@context': 'https://schema.org',
|
|
21
|
+
'@type': 'SoftwareApplication',
|
|
22
|
+
'@id': `${config.site}#app`,
|
|
23
|
+
name: app.name,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (app.applicationCategory) appLd.applicationCategory = app.applicationCategory
|
|
27
|
+
if (app.applicationSubCategory) appLd.applicationSubCategory = app.applicationSubCategory
|
|
28
|
+
if (app.operatingSystem) appLd.operatingSystem = app.operatingSystem
|
|
29
|
+
if (app.description) appLd.description = app.description
|
|
30
|
+
if (app.url) appLd.url = app.url
|
|
31
|
+
if (app.image) appLd.image = app.image
|
|
32
|
+
if (app.screenshot && app.screenshot.length > 0) appLd.screenshot = app.screenshot
|
|
33
|
+
if (app.featureList && app.featureList.length > 0) appLd.featureList = app.featureList
|
|
34
|
+
if (app.installUrl) appLd.installUrl = app.installUrl
|
|
35
|
+
if (app.downloadUrl) appLd.downloadUrl = app.downloadUrl
|
|
36
|
+
|
|
37
|
+
if (app.offers && app.offers.length > 0) {
|
|
38
|
+
appLd.offers = app.offers.map((offer) => {
|
|
39
|
+
const o: SchemaJson = {
|
|
40
|
+
'@type': 'Offer',
|
|
41
|
+
price: offer.price,
|
|
42
|
+
priceCurrency: offer.priceCurrency,
|
|
43
|
+
}
|
|
44
|
+
if (offer.name) o.name = offer.name
|
|
45
|
+
if (offer.description) o.description = offer.description
|
|
46
|
+
return o
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (app.aggregateRating) {
|
|
51
|
+
appLd.aggregateRating = {
|
|
52
|
+
'@type': 'AggregateRating',
|
|
53
|
+
ratingValue: app.aggregateRating.ratingValue,
|
|
54
|
+
ratingCount: app.aggregateRating.ratingCount,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (identityId) {
|
|
59
|
+
appLd.publisher = { '@id': identityId }
|
|
60
|
+
appLd.author = { '@id': identityId }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
---
|
|
64
|
+
{appLd && <script type="application/ld+json" is:inline set:html={jsonLd(appLd)}></script>}
|
|
@@ -2,15 +2,21 @@
|
|
|
2
2
|
import { config } from 'virtual:ai-readiness-config'
|
|
3
3
|
import { jsonLd } from '../utils/json-ld'
|
|
4
4
|
|
|
5
|
+
// A person-first site publishes as the person; otherwise the organization.
|
|
6
|
+
const publisherId = config.person
|
|
7
|
+
? `${config.site}#person`
|
|
8
|
+
: `${config.site}#organization`
|
|
9
|
+
|
|
5
10
|
const ws: Record<string, unknown> = {
|
|
6
11
|
'@context': 'https://schema.org',
|
|
7
12
|
'@type': 'WebSite',
|
|
8
13
|
'@id': `${config.site}#website`,
|
|
9
|
-
name: config.webSite?.name ?? config.organization.
|
|
14
|
+
name: config.webSite?.name ?? config.person?.name ?? config.organization?.name ?? config.site,
|
|
10
15
|
url: config.site,
|
|
11
|
-
publisher: { '@id':
|
|
16
|
+
publisher: { '@id': publisherId },
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
if (config.webSite?.description) ws.description = config.webSite.description
|
|
20
|
+
if (config.webSite?.inLanguage) ws.inLanguage = config.webSite.inLanguage
|
|
15
21
|
---
|
|
16
22
|
<script type="application/ld+json" is:inline set:html={jsonLd(ws)}></script>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { default as OrganizationSchema } from './OrganizationSchema.astro';
|
|
2
|
+
export { default as PersonSchema } from './PersonSchema.astro';
|
|
3
|
+
export { default as SoftwareApplicationSchema } from './SoftwareApplicationSchema.astro';
|
|
2
4
|
export { default as WebSiteSchema } from './WebSiteSchema.astro';
|
|
3
5
|
export { default as CollectionSchema } from './CollectionSchema.astro';
|
|
4
6
|
export { default as BreadcrumbSchema } from './BreadcrumbSchema.astro';
|
package/dist/components/index.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
// src/components/index.ts
|
|
2
2
|
import { default as default2 } from "./OrganizationSchema.astro";
|
|
3
|
-
import { default as default3 } from "./
|
|
4
|
-
import { default as default4 } from "./
|
|
5
|
-
import { default as default5 } from "./
|
|
6
|
-
import { default as default6 } from "./
|
|
7
|
-
import { default as default7 } from "./
|
|
3
|
+
import { default as default3 } from "./PersonSchema.astro";
|
|
4
|
+
import { default as default4 } from "./SoftwareApplicationSchema.astro";
|
|
5
|
+
import { default as default5 } from "./WebSiteSchema.astro";
|
|
6
|
+
import { default as default6 } from "./CollectionSchema.astro";
|
|
7
|
+
import { default as default7 } from "./BreadcrumbSchema.astro";
|
|
8
|
+
import { default as default8 } from "./FAQPageSchema.astro";
|
|
9
|
+
import { default as default9 } from "./TechArticleSchema.astro";
|
|
8
10
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
default7 as BreadcrumbSchema,
|
|
12
|
+
default6 as CollectionSchema,
|
|
13
|
+
default8 as FAQPageSchema,
|
|
12
14
|
default2 as OrganizationSchema,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
default3 as PersonSchema,
|
|
16
|
+
default4 as SoftwareApplicationSchema,
|
|
17
|
+
default9 as TechArticleSchema,
|
|
18
|
+
default5 as WebSiteSchema
|
|
15
19
|
};
|