@tsdoctor/seo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts ADDED
@@ -0,0 +1,543 @@
1
+ import { PackageManifest } from "@effected/package-json";
2
+ import { Result, Schema } from "effect";
3
+ import { ConflictingTermError, DuplicateNodeIdError, InvalidNodeIdError, JsonLdDocument, JsonLdNode } from "@effected/schema-org";
4
+ //#region src/Attribution.d.ts
5
+ /**
6
+ * The attribution a page can credit, derived from one package manifest.
7
+ *
8
+ * @remarks
9
+ * `licenseIds` and `primaryLicenseId` are deliberately both present.
10
+ * `primaryLicenseId` is absent for an `AND` expression, where every term binds
11
+ * at once and naming one would silently drop a license that legally applies;
12
+ * `licenseIds` always lists every license named. A consumer wanting one value
13
+ * reads `primaryLicenseId` and degrades when it is absent; one that can carry
14
+ * several (schema.org's `license` accepts an array) reads `licenseIds`.
15
+ *
16
+ * @public
17
+ */
18
+ interface AttributionFacts {
19
+ /** The `author` field's name. */
20
+ readonly authorName?: string;
21
+ /** The `author` field's homepage, when it carried one. */
22
+ readonly authorUrl?: string;
23
+ /** Every `maintainers` entry's name, in manifest order. */
24
+ readonly maintainerNames: ReadonlyArray<string>;
25
+ /**
26
+ * The browsable URL of **this package** within its repository.
27
+ *
28
+ * @remarks
29
+ * `Repository.directoryUrl` when the host's subdirectory convention is
30
+ * known, falling back to `Repository.browseUrl` otherwise — see
31
+ * {@link attributionFacts} for why the fallback is deliberate.
32
+ */
33
+ readonly repositoryUrl?: string;
34
+ /** The `homepage` field, verbatim. */
35
+ readonly homepage?: string;
36
+ /**
37
+ * Every SPDX identifier the `license` expression names, in written order.
38
+ * Empty when the manifest carries no license, or one that is not SPDX.
39
+ */
40
+ readonly licenseIds: ReadonlyArray<string>;
41
+ /**
42
+ * The single identifier the expression can be said to be under, absent for
43
+ * an `AND`.
44
+ */
45
+ readonly primaryLicenseId?: string;
46
+ /**
47
+ * The canonical SPDX page for {@link AttributionFacts.primaryLicenseId},
48
+ * absent for a `LicenseRef` or any id outside the catalog.
49
+ */
50
+ readonly licenseUrl?: string;
51
+ /**
52
+ * The canonical SPDX page for EVERY license the expression names, in
53
+ * written order, skipping any that has none.
54
+ *
55
+ * @remarks
56
+ * The plural counterpart to {@link AttributionFacts.licenseUrl}, and the
57
+ * one a consumer wants when the field it is filling accepts several —
58
+ * schema.org's `license` does. `licenseUrl` names only the primary, and an
59
+ * `AND` expression has no primary, so a dual-licensed package reading only
60
+ * the singular gets nothing at all.
61
+ *
62
+ * Shorter than `licenseIds` whenever a named license is outside the SPDX
63
+ * catalog (a `LicenseRef`), which is why the two are not index-aligned.
64
+ */
65
+ readonly licenseUrls: ReadonlyArray<string>;
66
+ /** The `keywords` field, verbatim. */
67
+ readonly keywords: ReadonlyArray<string>;
68
+ }
69
+ /**
70
+ * Derive the attribution facts a documentation page can credit from a decoded
71
+ * package manifest.
72
+ *
73
+ * @remarks
74
+ * Total: a manifest carrying none of these fields yields empty arrays and no
75
+ * optional properties, never a failure. Per-field degradation is the contract —
76
+ * an unparseable license drops only the license facts, an unrecognized
77
+ * repository reference drops only the repository URL.
78
+ *
79
+ * @param manifest - the decoded manifest to read
80
+ * @returns the facts, with every underivable field absent
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { PackageManifest } from "@effected/package-json";
85
+ * import { attributionFacts } from "@tsdoctor/seo";
86
+ * import { Effect } from "effect";
87
+ *
88
+ * const program = Effect.gen(function* () {
89
+ * const manifest = yield* PackageManifest.decode({
90
+ * name: "@scope/pkg",
91
+ * version: "1.0.0",
92
+ * license: "MIT",
93
+ * repository: { url: "github:owner/repo", directory: "packages/pkg" },
94
+ * });
95
+ * const facts = attributionFacts(manifest);
96
+ * console.log(facts.primaryLicenseId, facts.repositoryUrl);
97
+ * // => "MIT" "https://github.com/owner/repo/tree/HEAD/packages/pkg"
98
+ * });
99
+ * ```
100
+ *
101
+ * @public
102
+ */
103
+ declare function attributionFacts(manifest: PackageManifest): AttributionFacts;
104
+ //#endregion
105
+ //#region src/Canonical.d.ts
106
+ /**
107
+ * URL derivation for a documentation page: the site URL prefix, the canonical
108
+ * page URL, absolute image URLs and image MIME mapping.
109
+ *
110
+ * @remarks
111
+ * `imageMimeType`, `resolveUrl` and `deriveSiteUrl` moved here verbatim from
112
+ * the RSPress adapter's `og-resolver.ts`. `resolveUrl` was `resolveOgUrl`
113
+ * there; canonical links resolve through the same function, so the
114
+ * OG-specific name no longer fits. Everything here is total and synchronous —
115
+ * no Effect, no filesystem.
116
+ *
117
+ * @packageDocumentation
118
+ */
119
+ /**
120
+ * The `og:image:type` value for a detected image format, or `undefined` for a
121
+ * format with no mapping.
122
+ *
123
+ * @public
124
+ */
125
+ declare function imageMimeType(type: string | undefined): string | undefined;
126
+ /**
127
+ * Turn a configured URL into an absolute one.
128
+ *
129
+ * @returns The absolute URL, or `undefined` when the input is neither an
130
+ * absolute `http(s)` URL nor a site-root-relative path. A bare relative path
131
+ * is deliberately rejected rather than guessed at — there is no base to
132
+ * resolve it against that would not silently produce a broken link.
133
+ *
134
+ * @public
135
+ */
136
+ declare function resolveUrl(siteUrl: string, url: string): string | undefined;
137
+ /**
138
+ * Derive the site URL prefix from the framework's own config.
139
+ *
140
+ * @remarks
141
+ * Replaces the RSPress plugin's former `siteUrl` option. RSPress already knows
142
+ * where a site is deployed — {@link https://rspress.rs/api/config/config-basic#siteorigin | `siteOrigin`}
143
+ * plus `base` — so asking for it a second time invited the two to disagree, and
144
+ * a plugin-level answer that contradicted the site's own would silently emit
145
+ * canonical and `og:url` tags pointing at a host the site is not served from.
146
+ *
147
+ * RSPress concatenates as `siteOrigin + base + routePath`, and **this follows
148
+ * its documented fallback exactly**: with no `siteOrigin`, RSPress uses
149
+ * `base + routePath`. So an unset origin yields a ROOT-RELATIVE prefix rather
150
+ * than nothing.
151
+ *
152
+ * That fallback is what makes the tags inspectable in `rspress dev`, where the
153
+ * site is served from `localhost` and no configured origin could be correct
154
+ * anyway. A root-relative `/images/og.png` resolves against the page's own
155
+ * origin in the browser; it is a *relative* path (`images/og.png`, no leading
156
+ * slash) that has no base to resolve against, and this never emits one.
157
+ *
158
+ * @returns The prefix to put in front of a route that already begins with `/`.
159
+ * `""` when the site declares neither `siteOrigin` nor a non-root `base`, which
160
+ * leaves every URL root-relative. Never has a trailing slash, since every
161
+ * caller appends a route starting with `/`.
162
+ *
163
+ * @public
164
+ */
165
+ declare function deriveSiteUrl(siteOrigin: string | undefined, base: string | undefined): string;
166
+ /**
167
+ * The canonical URL for a page.
168
+ *
169
+ * @remarks
170
+ * With no configured origin the prefix is `""`, so the result is
171
+ * root-relative (`/api/class/foo`) rather than absent. That matches RSPress's
172
+ * own documented `base + routePath` fallback and keeps the tag inspectable
173
+ * under a dev server, where no configured origin could be correct.
174
+ *
175
+ * @public
176
+ */
177
+ declare function canonicalUrl(siteUrl: string, pageRoute: string): string;
178
+ //#endregion
179
+ //#region src/HeadTag.d.ts
180
+ /**
181
+ * The neutral head-tag vocabulary every adapter renders.
182
+ *
183
+ * @remarks
184
+ * A `HeadTag` is deliberately not a framework element. RSPress renders one
185
+ * into a frontmatter `head` pair; VitePress renders the same value into a
186
+ * `transformHead` entry. Keeping the type this dumb is what makes the second
187
+ * adapter cheap.
188
+ *
189
+ * @packageDocumentation
190
+ */
191
+ /**
192
+ * One tag destined for a page's `<head>`.
193
+ *
194
+ * @public
195
+ */
196
+ interface HeadTag {
197
+ readonly tag: "meta" | "link" | "script";
198
+ readonly attrs: Readonly<Record<string, string>>;
199
+ /** Element content. Only meaningful for `script`. */
200
+ readonly body?: string;
201
+ }
202
+ /**
203
+ * Escape a JSON string so it cannot terminate the `<script>` element that
204
+ * carries it.
205
+ *
206
+ * @remarks
207
+ * Every string in a JSON-LD graph originates in author-written TSDoc, so a
208
+ * summary containing the literal `</script>` would close the element early and
209
+ * inject markup into the page. `JSON.stringify` does not escape it.
210
+ *
211
+ * Escaping both angle brackets as `<` / `>` is valid JSON that
212
+ * parses back to the original characters, so the graph a consumer reads is
213
+ * unchanged while the element becomes unclosable from inside.
214
+ *
215
+ * `&` is escaped for the same reason at a different layer: XHTML parses
216
+ * script content as ordinary element content, where a bare `&` is a
217
+ * well-formedness error. An HTML-parsed page tolerates it; an XHTML-served one
218
+ * does not, and nothing in a docs pipeline guarantees which a consumer serves.
219
+ *
220
+ * The escape is idempotent — no escape sequence it emits contains `<`, `>` or
221
+ * `&` — so a body that arrives already escaped by an upstream serializer
222
+ * survives a second pass unchanged.
223
+ *
224
+ * @public
225
+ */
226
+ declare function escapeScriptBody(json: string): string;
227
+ /** An Open Graph style `<meta property=… content=…>`. @public */
228
+ declare function meta(property: string, content: string): HeadTag;
229
+ /** A Twitter/standard style `<meta name=… content=…>`. @public */
230
+ declare function metaNamed(name: string, content: string): HeadTag;
231
+ /** A `<link rel=… href=…>`. @public */
232
+ declare function link(rel: string, href: string): HeadTag;
233
+ /** A `<script type="application/ld+json">` carrying an escaped body. @public */
234
+ declare function jsonLd(json: string): HeadTag;
235
+ //#endregion
236
+ //#region src/OpenGraph.d.ts
237
+ /**
238
+ * Structured Open Graph image metadata (alternative to a plain URL string).
239
+ *
240
+ * @public
241
+ */
242
+ declare const OpenGraphImageMetadata: Schema.Struct<{
243
+ /** Absolute URL of the image. */
244
+ readonly url: Schema.String;
245
+ /** HTTPS URL of the image (for secure contexts). */
246
+ readonly secureUrl: Schema.optional<Schema.String>;
247
+ /** MIME type of the image (e.g. `"image/png"`). */
248
+ readonly type: Schema.optional<Schema.String>;
249
+ /** Image width in pixels. */
250
+ readonly width: Schema.optional<Schema.Number>;
251
+ /** Image height in pixels. */
252
+ readonly height: Schema.optional<Schema.Number>;
253
+ /** Alt text for the image. */
254
+ readonly alt: Schema.optional<Schema.String>;
255
+ }>;
256
+ /** @public */
257
+ type OpenGraphImageMetadata = typeof OpenGraphImageMetadata.Type;
258
+ /**
259
+ * Open Graph image: either a plain URL string or structured `OpenGraphImageMetadata`.
260
+ *
261
+ * @public
262
+ */
263
+ declare const OpenGraphImageConfig: Schema.Union<readonly [Schema.String, Schema.Struct<{
264
+ /** Absolute URL of the image. */
265
+ readonly url: Schema.String;
266
+ /** HTTPS URL of the image (for secure contexts). */
267
+ readonly secureUrl: Schema.optional<Schema.String>;
268
+ /** MIME type of the image (e.g. `"image/png"`). */
269
+ readonly type: Schema.optional<Schema.String>;
270
+ /** Image width in pixels. */
271
+ readonly width: Schema.optional<Schema.Number>;
272
+ /** Image height in pixels. */
273
+ readonly height: Schema.optional<Schema.Number>;
274
+ /** Alt text for the image. */
275
+ readonly alt: Schema.optional<Schema.String>;
276
+ }>]>;
277
+ /** @public */
278
+ type OpenGraphImageConfig = typeof OpenGraphImageConfig.Type;
279
+ /**
280
+ * Resolved Open Graph metadata for one documentation page.
281
+ *
282
+ * @public
283
+ */
284
+ declare const OpenGraphMetadata: Schema.Struct<{
285
+ /** Canonical site base URL. */
286
+ readonly siteUrl: Schema.String;
287
+ /** Page route path (e.g. `/api/classes/myclass`). */
288
+ readonly pageRoute: Schema.String;
289
+ /** Page description for the `og:description` tag. */
290
+ readonly description: Schema.String;
291
+ /** ISO 8601 date string for `article:published_time`. */
292
+ readonly publishedTime: Schema.String;
293
+ /** ISO 8601 date string for `article:modified_time`. */
294
+ readonly modifiedTime: Schema.String;
295
+ /** Article section label (e.g. `"API"`). */
296
+ readonly section: Schema.String;
297
+ /** Article tag keywords. */
298
+ readonly tags: Schema.mutable<Schema.$Array<Schema.String>>;
299
+ /** Optional structured image metadata. */
300
+ readonly ogImage: Schema.optional<Schema.Struct<{
301
+ /** Absolute URL of the image. */
302
+ readonly url: Schema.String;
303
+ /** HTTPS URL of the image (for secure contexts). */
304
+ readonly secureUrl: Schema.optional<Schema.String>;
305
+ /** MIME type of the image (e.g. `"image/png"`). */
306
+ readonly type: Schema.optional<Schema.String>;
307
+ /** Image width in pixels. */
308
+ readonly width: Schema.optional<Schema.Number>;
309
+ /** Image height in pixels. */
310
+ readonly height: Schema.optional<Schema.Number>;
311
+ /** Alt text for the image. */
312
+ readonly alt: Schema.optional<Schema.String>;
313
+ }>>;
314
+ /** Open Graph object type (e.g. `"article"`). */
315
+ readonly ogType: Schema.String;
316
+ }>;
317
+ /** @public */
318
+ type OpenGraphMetadata = typeof OpenGraphMetadata.Type;
319
+ /**
320
+ * Descriptive alt text for a package's (or one API's) OG image.
321
+ *
322
+ * @public
323
+ */
324
+ declare function ogAltText(packageName: string, apiName?: string): string;
325
+ /**
326
+ * Assemble the complete Open Graph metadata for one documentation page.
327
+ *
328
+ * @public
329
+ */
330
+ declare function createPageMetadata(options: {
331
+ siteUrl: string;
332
+ pageRoute: string;
333
+ description: string;
334
+ publishedTime: string;
335
+ modifiedTime: string;
336
+ section: string;
337
+ packageName: string;
338
+ ogImage?: OpenGraphImageMetadata;
339
+ }): OpenGraphMetadata;
340
+ /**
341
+ * The Open Graph block for a page.
342
+ *
343
+ * @remarks
344
+ * Each optional image sub-tag is emitted only when the resolved image actually
345
+ * carries it — an `og:image:width` with no width is a tag a crawler reads as a
346
+ * declared-but-empty dimension rather than an absent one.
347
+ *
348
+ * @public
349
+ */
350
+ declare function openGraphTags(metadata: OpenGraphMetadata): ReadonlyArray<HeadTag>;
351
+ /**
352
+ * Twitter card tags derived from the same metadata as the Open Graph block.
353
+ *
354
+ * @remarks
355
+ * Twitter reads most `og:` tags directly, so only what it does not infer is
356
+ * emitted here. The card type is a function of whether an image exists —
357
+ * `summary_large_image` with one, `summary` without — because declaring the
358
+ * large card with no image renders as a broken preview rather than degrading
359
+ * to the small one.
360
+ *
361
+ * Twitter's tags use `name`, not `property`.
362
+ *
363
+ * @public
364
+ */
365
+ declare function twitterTags(metadata: OpenGraphMetadata, site?: string): ReadonlyArray<HeadTag>;
366
+ //#endregion
367
+ //#region src/Seo.d.ts
368
+ /**
369
+ * Everything {@link headTags} needs about one documentation page.
370
+ *
371
+ * @public
372
+ */
373
+ interface SeoPageInput {
374
+ /** Site URL prefix, from `deriveSiteUrl`. `""` leaves every URL root-relative. */
375
+ readonly siteUrl: string;
376
+ /** Page route path, beginning with `/`. */
377
+ readonly pageRoute: string;
378
+ /** Page description, used for both `og:description` and `twitter:description`. */
379
+ readonly description: string;
380
+ /** ISO 8601 date string for `article:published_time`. */
381
+ readonly publishedTime: string;
382
+ /** ISO 8601 date string for `article:modified_time`. */
383
+ readonly modifiedTime: string;
384
+ /** Article section label (e.g. `"Classes"`). */
385
+ readonly section: string;
386
+ /** The documented package's npm name. */
387
+ readonly packageName: string;
388
+ /** Resolved Open Graph image, when the API declares one. */
389
+ readonly ogImage?: OpenGraphImageMetadata;
390
+ /** The `twitter:site` handle, when the site declares one. */
391
+ readonly twitterSite?: string;
392
+ /** A serialized JSON-LD graph. Absent until structured data is wired. */
393
+ readonly structuredData?: string;
394
+ }
395
+ /**
396
+ * Every `<head>` tag for one documentation page.
397
+ *
398
+ * @remarks
399
+ * The order — canonical link, Open Graph block, Twitter block, then the
400
+ * JSON-LD script — is fixed. It carries no semantics: a crawler reads the
401
+ * tags as a set. It is fixed so that a page's emitted head is stable
402
+ * build-to-build and a diff over generated pages stays readable.
403
+ *
404
+ * @public
405
+ */
406
+ declare function headTags(input: SeoPageInput): ReadonlyArray<HeadTag>;
407
+ //#endregion
408
+ //#region src/StructuredData.d.ts
409
+ /**
410
+ * Every way {@link derive} can fail, all of them identity problems raised by
411
+ * `JsonLdDocument.buildResult`.
412
+ *
413
+ * @public
414
+ */
415
+ type StructuredDataError = ConflictingTermError | DuplicateNodeIdError | InvalidNodeIdError;
416
+ /**
417
+ * The facts about a documented package that every page in it shares.
418
+ *
419
+ * @remarks
420
+ * A value of this shape is derived **once per API** and carried across the
421
+ * page pipeline. Deriving it per page would build several hundred identical
422
+ * nodes in a build, and re-run the attribution derivation behind each one.
423
+ *
424
+ * @public
425
+ */
426
+ interface PackageNodeInput {
427
+ /** Site URL prefix, from `deriveSiteUrl`. `""` leaves every id root-relative. */
428
+ readonly siteUrl: string;
429
+ /** The route the package's documentation is mounted at, beginning with `/`. */
430
+ readonly baseRoute: string;
431
+ /** The documented package's npm name. */
432
+ readonly packageName: string;
433
+ /** The package version, when the manifest carried one. */
434
+ readonly version?: string;
435
+ /** The package description, when the manifest carried one. */
436
+ readonly description?: string;
437
+ /** Attribution derived from the manifest via {@link attributionFacts}. */
438
+ readonly attribution: AttributionFacts;
439
+ }
440
+ /**
441
+ * The facts about one documentation page.
442
+ *
443
+ * @public
444
+ */
445
+ interface PageNodeInput {
446
+ /** Page route path, beginning with `/`. */
447
+ readonly pageRoute: string;
448
+ /** The documented symbol's display name (e.g. `"Pipeline"`). */
449
+ readonly symbolName: string;
450
+ /** Page description, reused as both nodes' `description`. */
451
+ readonly description: string;
452
+ /** Article section label (e.g. `"Classes"`). */
453
+ readonly section: string;
454
+ /** ISO 8601 date string for `datePublished`. */
455
+ readonly publishedTime: string;
456
+ /** ISO 8601 date string for `dateModified`. */
457
+ readonly modifiedTime: string;
458
+ }
459
+ /**
460
+ * The per-API package facts, resolved once into the nodes every page reuses.
461
+ *
462
+ * @remarks
463
+ * Opaque by intent: build it with {@link packageContext} and carry it, don't
464
+ * assemble one by hand. The whole reason it exists is that the nodes inside it
465
+ * are identical on every page of an API, and rebuilding them per page would
466
+ * mint several hundred copies per build.
467
+ *
468
+ * @public
469
+ */
470
+ interface PackageContext {
471
+ /** The `@id` every page's nodes reference the package by. */
472
+ readonly id: string;
473
+ /** The package node and every person node it credits. */
474
+ readonly nodes: ReadonlyArray<JsonLdNode>;
475
+ /** Carried through so page ids resolve against the same prefix. */
476
+ readonly siteUrl: string;
477
+ /** Carried through for the symbol node's `assemblyVersion`. */
478
+ readonly version?: string;
479
+ }
480
+ /**
481
+ * The `SoftwareSourceCode` node for a documented package, plus the people it
482
+ * credits.
483
+ *
484
+ * @remarks
485
+ * Note `version`, not `softwareVersion` — the latter reads like the right name
486
+ * and is defined on `SoftwareApplication`, not here. It would serialize fine
487
+ * and be silently ignored; the conformance validator is what catches it.
488
+ *
489
+ * `license` carries the canonical SPDX page for EVERY license the expression
490
+ * names — schema.org's `license` accepts an array, and an `AND` expression has
491
+ * no single answer to give. The URLs come from each catalog entry's own
492
+ * `referenceUrl`, never from concatenating an id onto
493
+ * `https://spdx.org/licenses/`: that is the string-building the catalog exists
494
+ * to prevent, and it is wrong for a `LicenseRef`, which has no such page. A
495
+ * license outside the catalog drops out of the array rather than appearing as
496
+ * a fabricated URL.
497
+ *
498
+ * @param input - the per-API facts
499
+ * @returns the reusable context every page in the API derives against
500
+ *
501
+ * @public
502
+ */
503
+ declare function packageContext(input: PackageNodeInput): PackageContext;
504
+ /**
505
+ * Derive the schema.org graph for one documentation page.
506
+ *
507
+ * @remarks
508
+ * Three linked nodes plus the package's people: a `SoftwareSourceCode` for the
509
+ * package, a `TechArticle` for the page, and an `APIReference` for the symbol
510
+ * the page documents. The article `isPartOf` the package and its `mainEntity`
511
+ * is the symbol, so a crawler reading any one node can reach the other two.
512
+ *
513
+ * Serialize the result with `JsonLdDocument.toScriptBody()`, never with
514
+ * `JSON.stringify(graph.toJsonLd())` — `toScriptBody` is the only serializer
515
+ * that escapes the sequences that would close the surrounding `<script>`
516
+ * element, and it is idempotent, so an adapter layering its own escaping over
517
+ * it is a no-op rather than a double-escape.
518
+ *
519
+ * @param pkg - the per-API context from {@link packageContext}
520
+ * @param page - the per-page facts
521
+ * @returns the assembled graph, or the identity failure that stopped it
522
+ *
523
+ * @public
524
+ */
525
+ declare function derive(pkg: PackageContext, page: PageNodeInput): Result.Result<JsonLdDocument, StructuredDataError>;
526
+ /**
527
+ * {@link derive}, serialized to the text an adapter embeds in a `<script>`.
528
+ *
529
+ * @remarks
530
+ * The convenience the adapter actually wants: a page's structured data as a
531
+ * string. Callers that need the graph itself — a conformance check in a test,
532
+ * say — use {@link derive}.
533
+ *
534
+ * @param pkg - the per-API context from {@link packageContext}
535
+ * @param page - the per-page facts
536
+ * @returns the script body, or the identity failure
537
+ *
538
+ * @public
539
+ */
540
+ declare function deriveScriptBody(pkg: PackageContext, page: PageNodeInput): Result.Result<string, StructuredDataError>;
541
+ //#endregion
542
+ export { type AttributionFacts, type HeadTag, OpenGraphImageConfig, OpenGraphImageMetadata, OpenGraphMetadata, type PackageContext, type PackageNodeInput, type PageNodeInput, type SeoPageInput, type StructuredDataError, attributionFacts, canonicalUrl, createPageMetadata, derive, deriveScriptBody, deriveSiteUrl, escapeScriptBody, headTags, imageMimeType, jsonLd, link, meta, metaNamed, ogAltText, openGraphTags, packageContext, resolveUrl, twitterTags };
543
+ //# sourceMappingURL=index.d.ts.map
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { attributionFacts } from "./Attribution.js";
2
+ import { canonicalUrl, deriveSiteUrl, imageMimeType, resolveUrl } from "./Canonical.js";
3
+ import { escapeScriptBody, jsonLd, link, meta, metaNamed } from "./HeadTag.js";
4
+ import { OpenGraphImageConfig, OpenGraphImageMetadata, OpenGraphMetadata, createPageMetadata, ogAltText, openGraphTags, twitterTags } from "./OpenGraph.js";
5
+ import { headTags } from "./Seo.js";
6
+ import { derive, deriveScriptBody, packageContext } from "./StructuredData.js";
7
+
8
+ export { OpenGraphImageConfig, OpenGraphImageMetadata, OpenGraphMetadata, attributionFacts, canonicalUrl, createPageMetadata, derive, deriveScriptBody, deriveSiteUrl, escapeScriptBody, headTags, imageMimeType, jsonLd, link, meta, metaNamed, ogAltText, openGraphTags, packageContext, resolveUrl, twitterTags };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@tsdoctor/seo",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Framework-neutral head metadata for static TypeScript API documentation: schema.org JSON-LD, Open Graph and Twitter card vocabulary, canonical URLs, and package attribution derived from an api.json model plus its manifest.",
6
+ "keywords": [
7
+ "seo",
8
+ "schema.org",
9
+ "json-ld",
10
+ "open-graph",
11
+ "documentation",
12
+ "effect"
13
+ ],
14
+ "homepage": "https://github.com/spencerbeggs/tsdoctor#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/spencerbeggs/tsdoctor/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/spencerbeggs/tsdoctor.git",
21
+ "directory": "packages/seo"
22
+ },
23
+ "license": "MIT",
24
+ "author": {
25
+ "name": "C. Spencer Beggs",
26
+ "email": "spencer@beggs.codes",
27
+ "url": "https://spencerbeg.gs"
28
+ },
29
+ "sideEffects": false,
30
+ "type": "module",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./index.d.ts",
34
+ "import": "./index.js",
35
+ "default": "./index.js"
36
+ },
37
+ "./package.json": "./package.json"
38
+ },
39
+ "peerDependencies": {
40
+ "@effected/package-json": "^0.13.0",
41
+ "@effected/schema-org": "^0.1.0",
42
+ "@effected/spdx": "^0.5.0",
43
+ "effect": "4.0.0-rc.109"
44
+ },
45
+ "engines": {
46
+ "node": ">=24.11.0"
47
+ }
48
+ }
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.59.0"
9
+ }
10
+ ]
11
+ }