@hollware/core 0.4.0 → 0.5.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/dist/index.d.mts CHANGED
@@ -439,7 +439,7 @@ export declare const DEFAULT_MAX_MARKDOWN_BYTES = 524288;
439
439
  */
440
440
  export declare const MAX_MARKDOWN_BYTES_CEILING = 786432;
441
441
  declare namespace schemas_d_exports {
442
- export { acknowledgedWarning, collectionDescription, collectionName, componentDescription, conflict, contractDescription, document, entry, entryId, entryStatus, entrySummary, format, locale, markdown, phase, publication, review, reviewId, reviewStatus, revisionId, severity, siteDescription, slug, verdict, version, violation };
442
+ export { acknowledgedWarning, collectionDescription, collectionName, componentDescription, conflict, contractDescription, document, entry, entryId, entryStatus, entrySummary, format, locale, markdown, phase, publication, review, reviewId, reviewStatus, revisionId, searchMode, severity, siteDescription, slug, verdict, version, violation };
443
443
  }
444
444
  /**
445
445
  * The pieces the operation schemas share. Their descriptions become the JSON Schema that agents
@@ -703,6 +703,12 @@ declare const format: z.ZodEnum<{
703
703
  html: "html";
704
704
  markdown: "markdown";
705
705
  }>;
706
+ /** How a search is made: by its words, by meaning, or both merged (ADR-030). */
707
+ declare const searchMode: z.ZodEnum<{
708
+ fulltext: "fulltext";
709
+ hybrid: "hybrid";
710
+ semantic: "semantic";
711
+ }>;
706
712
  declare const componentDescription: z.ZodObject<{
707
713
  name: z.ZodString;
708
714
  description: z.ZodNullable<z.ZodString>;
@@ -1048,6 +1054,7 @@ export declare class HollwareError extends Error {
1048
1054
  export declare const ID_PREFIXES: {
1049
1055
  readonly challenge: 'chl';
1050
1056
  readonly delivery: 'dlv';
1057
+ readonly embedding: 'emb';
1051
1058
  readonly entry: 'ent';
1052
1059
  readonly event: 'evt';
1053
1060
  readonly invitation: 'inv';
@@ -1238,6 +1245,48 @@ export declare const listEntries: OperationSpec<"list_entries", z.ZodObject<{
1238
1245
  }, z.core.$strip>>;
1239
1246
  nextCursor: z.ZodNullable<z.ZodString>;
1240
1247
  }, z.core.$strip>>;
1248
+ export declare const search: OperationSpec<"search", z.ZodObject<{
1249
+ query: z.ZodString;
1250
+ collection: z.ZodOptional<z.ZodString>;
1251
+ locale: z.ZodOptional<z.ZodString>;
1252
+ limit: z.ZodDefault<z.ZodNumber>;
1253
+ mode: z.ZodOptional<z.ZodEnum<{
1254
+ fulltext: "fulltext";
1255
+ hybrid: "hybrid";
1256
+ semantic: "semantic";
1257
+ }>>;
1258
+ }, z.core.$strict>, z.ZodObject<{
1259
+ outcome: z.ZodEnum<{
1260
+ ok: "ok";
1261
+ }>;
1262
+ mode: z.ZodEnum<{
1263
+ fulltext: "fulltext";
1264
+ hybrid: "hybrid";
1265
+ semantic: "semantic";
1266
+ }>;
1267
+ results: z.ZodArray<z.ZodObject<{
1268
+ entry: z.ZodObject<{
1269
+ id: z.ZodString;
1270
+ collection: z.ZodString;
1271
+ locale: z.ZodString;
1272
+ slug: z.ZodString;
1273
+ translationGroup: z.ZodNullable<z.ZodString>;
1274
+ status: z.ZodEnum<{
1275
+ draft: "draft";
1276
+ published: "published";
1277
+ scheduled: "scheduled";
1278
+ }>;
1279
+ version: z.ZodNumber;
1280
+ frontmatter: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1281
+ tags: z.ZodArray<z.ZodString>;
1282
+ hasUnpublishedChanges: z.ZodBoolean;
1283
+ publishedAt: z.ZodNullable<z.ZodString>;
1284
+ updatedAt: z.ZodString;
1285
+ }, z.core.$strip>;
1286
+ snippet: z.ZodString;
1287
+ score: z.ZodNumber;
1288
+ }, z.core.$strip>>;
1289
+ }, z.core.$strip>>;
1241
1290
  export declare const getEntry: OperationSpec<"get_entry", z.ZodObject<{
1242
1291
  collection: z.ZodString;
1243
1292
  locale: z.ZodString;
@@ -1248,6 +1297,7 @@ export declare const getEntry: OperationSpec<"get_entry", z.ZodObject<{
1248
1297
  markdown: "markdown";
1249
1298
  }>>>;
1250
1299
  published: z.ZodDefault<z.ZodBoolean>;
1300
+ related: z.ZodDefault<z.ZodBoolean>;
1251
1301
  }, z.core.$strict>, z.ZodObject<{
1252
1302
  outcome: z.ZodEnum<{
1253
1303
  not_found: "not_found";
@@ -1347,6 +1397,24 @@ export declare const getEntry: OperationSpec<"get_entry", z.ZodObject<{
1347
1397
  }, z.core.$strip>>;
1348
1398
  html: z.ZodOptional<z.ZodString>;
1349
1399
  }, z.core.$strip>>;
1400
+ related: z.ZodOptional<z.ZodArray<z.ZodObject<{
1401
+ id: z.ZodString;
1402
+ collection: z.ZodString;
1403
+ locale: z.ZodString;
1404
+ slug: z.ZodString;
1405
+ translationGroup: z.ZodNullable<z.ZodString>;
1406
+ status: z.ZodEnum<{
1407
+ draft: "draft";
1408
+ published: "published";
1409
+ scheduled: "scheduled";
1410
+ }>;
1411
+ version: z.ZodNumber;
1412
+ frontmatter: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1413
+ tags: z.ZodArray<z.ZodString>;
1414
+ hasUnpublishedChanges: z.ZodBoolean;
1415
+ publishedAt: z.ZodNullable<z.ZodString>;
1416
+ updatedAt: z.ZodString;
1417
+ }, z.core.$strip>>>;
1350
1418
  }, z.core.$strip>>;
1351
1419
  export declare const getTranslations: OperationSpec<"get_translations", z.ZodObject<{
1352
1420
  collection: z.ZodString;
@@ -1813,6 +1881,38 @@ export declare const requestChanges: OperationSpec<"request_changes", z.ZodObjec
1813
1881
  note: z.ZodNullable<z.ZodString>;
1814
1882
  }, z.core.$strip>>;
1815
1883
  }, z.core.$strip>>;
1884
+ export declare const contentStats: OperationSpec<"content_stats", z.ZodObject<{
1885
+ days: z.ZodDefault<z.ZodNumber>;
1886
+ limit: z.ZodDefault<z.ZodNumber>;
1887
+ }, z.core.$strict>, z.ZodObject<{
1888
+ outcome: z.ZodEnum<{
1889
+ ok: "ok";
1890
+ unavailable: "unavailable";
1891
+ }>;
1892
+ since: z.ZodOptional<z.ZodString>;
1893
+ pages: z.ZodOptional<z.ZodArray<z.ZodObject<{
1894
+ path: z.ZodString;
1895
+ views: z.ZodNumber;
1896
+ entry: z.ZodNullable<z.ZodObject<{
1897
+ id: z.ZodString;
1898
+ collection: z.ZodString;
1899
+ locale: z.ZodString;
1900
+ slug: z.ZodString;
1901
+ translationGroup: z.ZodNullable<z.ZodString>;
1902
+ status: z.ZodEnum<{
1903
+ draft: "draft";
1904
+ published: "published";
1905
+ scheduled: "scheduled";
1906
+ }>;
1907
+ version: z.ZodNumber;
1908
+ frontmatter: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1909
+ tags: z.ZodArray<z.ZodString>;
1910
+ hasUnpublishedChanges: z.ZodBoolean;
1911
+ publishedAt: z.ZodNullable<z.ZodString>;
1912
+ updatedAt: z.ZodString;
1913
+ }, z.core.$strip>>;
1914
+ }, z.core.$strip>>>;
1915
+ }, z.core.$strip>>;
1816
1916
  /** Every operation, sorted by name: the order of `tools/list`. */
1817
1917
  export declare const OPERATIONS: readonly [OperationSpec<"approve_review", z.ZodObject<{
1818
1918
  id: z.ZodString;
@@ -1893,6 +1993,37 @@ export declare const OPERATIONS: readonly [OperationSpec<"approve_review", z.Zod
1893
1993
  }, z.core.$strip>>;
1894
1994
  contractsHash: z.ZodString;
1895
1995
  }, z.core.$strip>>;
1996
+ }, z.core.$strip>>, OperationSpec<"content_stats", z.ZodObject<{
1997
+ days: z.ZodDefault<z.ZodNumber>;
1998
+ limit: z.ZodDefault<z.ZodNumber>;
1999
+ }, z.core.$strict>, z.ZodObject<{
2000
+ outcome: z.ZodEnum<{
2001
+ ok: "ok";
2002
+ unavailable: "unavailable";
2003
+ }>;
2004
+ since: z.ZodOptional<z.ZodString>;
2005
+ pages: z.ZodOptional<z.ZodArray<z.ZodObject<{
2006
+ path: z.ZodString;
2007
+ views: z.ZodNumber;
2008
+ entry: z.ZodNullable<z.ZodObject<{
2009
+ id: z.ZodString;
2010
+ collection: z.ZodString;
2011
+ locale: z.ZodString;
2012
+ slug: z.ZodString;
2013
+ translationGroup: z.ZodNullable<z.ZodString>;
2014
+ status: z.ZodEnum<{
2015
+ draft: "draft";
2016
+ published: "published";
2017
+ scheduled: "scheduled";
2018
+ }>;
2019
+ version: z.ZodNumber;
2020
+ frontmatter: z.ZodRecord<z.ZodString, z.ZodUnknown>;
2021
+ tags: z.ZodArray<z.ZodString>;
2022
+ hasUnpublishedChanges: z.ZodBoolean;
2023
+ publishedAt: z.ZodNullable<z.ZodString>;
2024
+ updatedAt: z.ZodString;
2025
+ }, z.core.$strip>>;
2026
+ }, z.core.$strip>>>;
1896
2027
  }, z.core.$strip>>, OperationSpec<"create_draft", z.ZodObject<{
1897
2028
  collection: z.ZodString;
1898
2029
  locale: z.ZodString;
@@ -1999,6 +2130,7 @@ export declare const OPERATIONS: readonly [OperationSpec<"approve_review", z.Zod
1999
2130
  markdown: "markdown";
2000
2131
  }>>>;
2001
2132
  published: z.ZodDefault<z.ZodBoolean>;
2133
+ related: z.ZodDefault<z.ZodBoolean>;
2002
2134
  }, z.core.$strict>, z.ZodObject<{
2003
2135
  outcome: z.ZodEnum<{
2004
2136
  not_found: "not_found";
@@ -2098,6 +2230,24 @@ export declare const OPERATIONS: readonly [OperationSpec<"approve_review", z.Zod
2098
2230
  }, z.core.$strip>>;
2099
2231
  html: z.ZodOptional<z.ZodString>;
2100
2232
  }, z.core.$strip>>;
2233
+ related: z.ZodOptional<z.ZodArray<z.ZodObject<{
2234
+ id: z.ZodString;
2235
+ collection: z.ZodString;
2236
+ locale: z.ZodString;
2237
+ slug: z.ZodString;
2238
+ translationGroup: z.ZodNullable<z.ZodString>;
2239
+ status: z.ZodEnum<{
2240
+ draft: "draft";
2241
+ published: "published";
2242
+ scheduled: "scheduled";
2243
+ }>;
2244
+ version: z.ZodNumber;
2245
+ frontmatter: z.ZodRecord<z.ZodString, z.ZodUnknown>;
2246
+ tags: z.ZodArray<z.ZodString>;
2247
+ hasUnpublishedChanges: z.ZodBoolean;
2248
+ publishedAt: z.ZodNullable<z.ZodString>;
2249
+ updatedAt: z.ZodString;
2250
+ }, z.core.$strip>>>;
2101
2251
  }, z.core.$strip>>, OperationSpec<"get_translations", z.ZodObject<{
2102
2252
  collection: z.ZodString;
2103
2253
  translationGroup: z.ZodString;
@@ -2323,6 +2473,47 @@ export declare const OPERATIONS: readonly [OperationSpec<"approve_review", z.Zod
2323
2473
  decidedAt: z.ZodNullable<z.ZodString>;
2324
2474
  note: z.ZodNullable<z.ZodString>;
2325
2475
  }, z.core.$strip>>;
2476
+ }, z.core.$strip>>, OperationSpec<"search", z.ZodObject<{
2477
+ query: z.ZodString;
2478
+ collection: z.ZodOptional<z.ZodString>;
2479
+ locale: z.ZodOptional<z.ZodString>;
2480
+ limit: z.ZodDefault<z.ZodNumber>;
2481
+ mode: z.ZodOptional<z.ZodEnum<{
2482
+ fulltext: "fulltext";
2483
+ hybrid: "hybrid";
2484
+ semantic: "semantic";
2485
+ }>>;
2486
+ }, z.core.$strict>, z.ZodObject<{
2487
+ outcome: z.ZodEnum<{
2488
+ ok: "ok";
2489
+ }>;
2490
+ mode: z.ZodEnum<{
2491
+ fulltext: "fulltext";
2492
+ hybrid: "hybrid";
2493
+ semantic: "semantic";
2494
+ }>;
2495
+ results: z.ZodArray<z.ZodObject<{
2496
+ entry: z.ZodObject<{
2497
+ id: z.ZodString;
2498
+ collection: z.ZodString;
2499
+ locale: z.ZodString;
2500
+ slug: z.ZodString;
2501
+ translationGroup: z.ZodNullable<z.ZodString>;
2502
+ status: z.ZodEnum<{
2503
+ draft: "draft";
2504
+ published: "published";
2505
+ scheduled: "scheduled";
2506
+ }>;
2507
+ version: z.ZodNumber;
2508
+ frontmatter: z.ZodRecord<z.ZodString, z.ZodUnknown>;
2509
+ tags: z.ZodArray<z.ZodString>;
2510
+ hasUnpublishedChanges: z.ZodBoolean;
2511
+ publishedAt: z.ZodNullable<z.ZodString>;
2512
+ updatedAt: z.ZodString;
2513
+ }, z.core.$strip>;
2514
+ snippet: z.ZodString;
2515
+ score: z.ZodNumber;
2516
+ }, z.core.$strip>>;
2326
2517
  }, z.core.$strip>>, OperationSpec<"unpublish", z.ZodObject<{
2327
2518
  id: z.ZodString;
2328
2519
  }, z.core.$strict>, z.ZodObject<{
@@ -2516,7 +2707,38 @@ export interface WebhookCheck {
2516
2707
  */
2517
2708
  export declare function verifyWebhook(check: WebhookCheck): Promise<boolean>;
2518
2709
  //#endregion
2710
+ //#region src/search/chunks.d.ts
2711
+ /**
2712
+ * The pieces of an entry that semantic search embeds (v0.5): one summary, and one piece for each
2713
+ * section the body has under an `h2`. Vectors are derived data, like the Comark document: a change
2714
+ * of this version makes every vector one to make again.
2715
+ */
2716
+ export declare const CHUNKER_VERSION = "1";
2717
+ interface Chunk {
2718
+ /** The place of the piece in the entry: 0 is the summary, then the sections in order. */
2719
+ readonly index: number;
2720
+ readonly kind: 'summary' | 'section';
2721
+ /** The heading of a section; `null` for the summary. */
2722
+ readonly heading: string | null;
2723
+ readonly text: string;
2724
+ }
2725
+ /** How long a piece may grow, in characters, before it is cut at a word. */
2726
+ export declare const CHUNK_LIMITS: {
2727
+ readonly summary: 1200;
2728
+ readonly section: 2000;
2729
+ };
2730
+ /**
2731
+ * The pieces of an entry: the summary (its title, its description and the text before the first
2732
+ * `h2`), then each section (its heading and its text up to the next `h2`). An `h1` adds nothing:
2733
+ * it repeats the title. A section with no text is left out.
2734
+ */
2735
+ export declare function chunksOf(entry: {
2736
+ readonly title: string;
2737
+ readonly description?: string;
2738
+ readonly document: EntryDocument;
2739
+ }): Chunk[];
2740
+ //#endregion
2519
2741
  //#region src/index.d.ts
2520
2742
  export declare const name = "@hollware/core";
2521
2743
  //#endregion
2522
- export { type AcknowledgedWarning, type AuthDefinition, type BuiltInContractId, type BuiltInOptions, type CollectionDefinition, type ComponentDefinition, type ComponentUse, type ConfigProblem, type Contract, type ContractContext, type ContractDescription, type ContractFinding, type ContractText, type CustomContractOptions, type DescribedSchema, type DisallowedMarker, type Effect, type EntryDocument, type EntryFacts, type EntryLookup, type EntryRef, type ErrorCode, type ExtractedContent, type Heading, type HollwareConfig, type IdPrefix, type ImageRef, type LinkRef, type LinkTarget, type Operation, type OperationByName, type OperationName, type OperationSpec, type OutcomeInfo, type OutcomeOf, type Phase, type ProcessMarkdown, type ProcessMarkdownOptions, type ProcessResult, type ProcessedMarkdown, type RenderHtml, type ResolveConfig, type ResolvedAuth, type ResolvedCollection, type ResolvedConfig, type ResolvedSite, type RunContractsInput, type SchemaOutput, type Scope, type Severity, type SiteDefinition, type SiteDescription, type Verdict, type Violation, schemas_d_exports as schemas };
2744
+ export { type AcknowledgedWarning, type AuthDefinition, type BuiltInContractId, type BuiltInOptions, type Chunk, type CollectionDefinition, type ComponentDefinition, type ComponentUse, type ConfigProblem, type Contract, type ContractContext, type ContractDescription, type ContractFinding, type ContractText, type CustomContractOptions, type DescribedSchema, type DisallowedMarker, type Effect, type EntryDocument, type EntryFacts, type EntryLookup, type EntryRef, type ErrorCode, type ExtractedContent, type Heading, type HollwareConfig, type IdPrefix, type ImageRef, type LinkRef, type LinkTarget, type Operation, type OperationByName, type OperationName, type OperationSpec, type OutcomeInfo, type OutcomeOf, type Phase, type ProcessMarkdown, type ProcessMarkdownOptions, type ProcessResult, type ProcessedMarkdown, type RenderHtml, type ResolveConfig, type ResolvedAuth, type ResolvedCollection, type ResolvedConfig, type ResolvedSite, type RunContractsInput, type SchemaOutput, type Scope, type Severity, type SiteDefinition, type SiteDescription, type Verdict, type Violation, schemas_d_exports as schemas };
package/dist/index.mjs CHANGED
@@ -1702,6 +1702,7 @@ const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
1702
1702
  const ID_PREFIXES = {
1703
1703
  challenge: "chl",
1704
1704
  delivery: "dlv",
1705
+ embedding: "emb",
1705
1706
  entry: "ent",
1706
1707
  event: "evt",
1707
1708
  invitation: "inv",
@@ -3052,6 +3053,7 @@ var schemas_exports = /* @__PURE__ */ __exportAll({
3052
3053
  reviewId: () => reviewId,
3053
3054
  reviewStatus: () => reviewStatus,
3054
3055
  revisionId: () => revisionId,
3056
+ searchMode: () => searchMode,
3055
3057
  severity: () => severity,
3056
3058
  siteDescription: () => siteDescription,
3057
3059
  slug: () => slug,
@@ -3162,6 +3164,12 @@ const format = z.enum([
3162
3164
  "document",
3163
3165
  "html"
3164
3166
  ]);
3167
+ /** How a search is made: by its words, by meaning, or both merged (ADR-030). */
3168
+ const searchMode = z.enum([
3169
+ "fulltext",
3170
+ "semantic",
3171
+ "hybrid"
3172
+ ]);
3165
3173
  const componentDescription = z.object({
3166
3174
  name: z.string(),
3167
3175
  description: z.string().nullable(),
@@ -3240,6 +3248,30 @@ const listEntries = defineOperation({
3240
3248
  effect: "read",
3241
3249
  outcomes: { ok: { isError: false } }
3242
3250
  });
3251
+ const search = defineOperation({
3252
+ name: "search",
3253
+ title: "Search entries",
3254
+ description: "Searches the published entries of the site, best match first. Full text finds the entries that hold every word of query, in their title, tags and text; semantic finds them by meaning, in any language; hybrid merges both. Narrow it to a collection or a locale. Drafts are not searched: read them with list_entries.",
3255
+ input: z.strictObject({
3256
+ query: z.string().trim().min(1).max(200).describe("The words to find, or a question; punctuation is ignored by full text."),
3257
+ collection: collectionName.optional(),
3258
+ locale: locale.optional(),
3259
+ limit: z.number().int().min(1).max(50).default(10),
3260
+ mode: searchMode.optional().describe("fulltext, semantic or hybrid; hybrid when not given. Semantic and hybrid need the semantic search of the deployment: without it the search is full text, and mode in the answer says so.")
3261
+ }),
3262
+ output: z.object({
3263
+ outcome: z.enum(["ok"]),
3264
+ mode: searchMode.describe("How the search was made."),
3265
+ results: z.array(z.object({
3266
+ entry: entrySummary,
3267
+ snippet: z.string().describe("The words around the match, the words found between [ and ]; for a match by meaning, the start of the closest part."),
3268
+ score: z.number().describe("How well it matches: higher is better.")
3269
+ }))
3270
+ }),
3271
+ scopes: ["content:read"],
3272
+ effect: "read",
3273
+ outcomes: { ok: { isError: false } }
3274
+ });
3243
3275
  const getEntry = defineOperation({
3244
3276
  name: "get_entry",
3245
3277
  title: "Get an entry",
@@ -3249,11 +3281,13 @@ const getEntry = defineOperation({
3249
3281
  locale,
3250
3282
  slug,
3251
3283
  formats: z.array(format).min(1).default(["markdown"]).describe("What to return of the revision: markdown, the Comark document, or html."),
3252
- published: z.boolean().default(false).describe("Read the revision that is live instead of the latest one: not_found when the entry is not published.")
3284
+ published: z.boolean().default(false).describe("Read the revision that is live instead of the latest one: not_found when the entry is not published."),
3285
+ related: z.boolean().default(false).describe("Also return the published entries closest to this one by meaning, in its collection and locale, closest first. Needs the semantic search of the deployment; empty without it.")
3253
3286
  }),
3254
3287
  output: z.object({
3255
3288
  outcome: z.enum(["ok", "not_found"]),
3256
- entry: entry.optional()
3289
+ entry: entry.optional(),
3290
+ related: z.array(entrySummary).optional().describe("When related was asked for.")
3257
3291
  }),
3258
3292
  scopes: ["content:read"],
3259
3293
  effect: "read",
@@ -3480,9 +3514,34 @@ const requestChanges = defineOperation({
3480
3514
  not_found: { isError: true }
3481
3515
  }
3482
3516
  });
3517
+ const contentStats = defineOperation({
3518
+ name: "content_stats",
3519
+ title: "Read what readers read",
3520
+ description: "The most read pages of the sites that read this one, over the last days, most read first, each with the published entry it shows when it shows one. Use it to pick what to update. unavailable when the deployment has no analytics.",
3521
+ input: z.strictObject({
3522
+ days: z.number().int().min(1).max(90).default(30).describe("How many days back to count."),
3523
+ limit: z.number().int().min(1).max(50).default(10)
3524
+ }),
3525
+ output: z.object({
3526
+ outcome: z.enum(["ok", "unavailable"]),
3527
+ since: z.string().optional().describe("The start of the count, in ISO 8601."),
3528
+ pages: z.array(z.object({
3529
+ path: z.string(),
3530
+ views: z.number().describe("Page views, sampling accounted for."),
3531
+ entry: entrySummary.nullable().describe("The published entry at that path, or null.")
3532
+ })).optional()
3533
+ }),
3534
+ scopes: ["content:read"],
3535
+ effect: "read",
3536
+ outcomes: {
3537
+ ok: { isError: false },
3538
+ unavailable: { isError: true }
3539
+ }
3540
+ });
3483
3541
  /** Every operation, sorted by name: the order of `tools/list`. */
3484
3542
  const OPERATIONS = [
3485
3543
  approveReview,
3544
+ contentStats,
3486
3545
  createDraft,
3487
3546
  describeSite,
3488
3547
  getEntry,
@@ -3491,6 +3550,7 @@ const OPERATIONS = [
3491
3550
  listReviews,
3492
3551
  publish,
3493
3552
  requestChanges,
3553
+ search,
3494
3554
  unpublish,
3495
3555
  updateDraft,
3496
3556
  validate
@@ -3607,7 +3667,82 @@ async function verifyWebhook(check) {
3607
3667
  return sameText(signature, await signWebhook(check.secret, timestamp, check.body));
3608
3668
  }
3609
3669
  //#endregion
3670
+ //#region src/search/chunks.ts
3671
+ /**
3672
+ * The pieces of an entry that semantic search embeds (v0.5): one summary, and one piece for each
3673
+ * section the body has under an `h2`. Vectors are derived data, like the Comark document: a change
3674
+ * of this version makes every vector one to make again.
3675
+ */
3676
+ const CHUNKER_VERSION = "1";
3677
+ /** How long a piece may grow, in characters, before it is cut at a word. */
3678
+ const CHUNK_LIMITS = {
3679
+ summary: 1200,
3680
+ section: 2e3
3681
+ };
3682
+ /** The text a reader sees in a node: its strings, never its tag, attributes or comments. */
3683
+ function textOf(node) {
3684
+ if (typeof node === "string") return node;
3685
+ if (!Array.isArray(node) || node[0] === null) return "";
3686
+ return node.slice(2).map((child) => textOf(child)).join("");
3687
+ }
3688
+ function tagOf(node) {
3689
+ return Array.isArray(node) && typeof node[0] === "string" ? node[0] : null;
3690
+ }
3691
+ /** The words of a text in one line, cut at the last word that fits in `limit`. */
3692
+ function fit(text, limit) {
3693
+ const flat = text.replace(/\s+/g, " ").trim();
3694
+ if (flat.length <= limit) return flat;
3695
+ const cut = flat.slice(0, limit);
3696
+ const lastSpace = cut.lastIndexOf(" ");
3697
+ return (lastSpace > 0 ? cut.slice(0, lastSpace) : cut).trim();
3698
+ }
3699
+ /**
3700
+ * The pieces of an entry: the summary (its title, its description and the text before the first
3701
+ * `h2`), then each section (its heading and its text up to the next `h2`). An `h1` adds nothing:
3702
+ * it repeats the title. A section with no text is left out.
3703
+ */
3704
+ function chunksOf(entry) {
3705
+ const intro = [];
3706
+ const sections = [];
3707
+ for (const node of entry.document.nodes) {
3708
+ const tag = tagOf(node);
3709
+ if (tag === "h1") continue;
3710
+ if (tag === "h2") {
3711
+ sections.push({
3712
+ heading: textOf(node).trim(),
3713
+ parts: []
3714
+ });
3715
+ continue;
3716
+ }
3717
+ const text = textOf(node);
3718
+ const current = sections.at(-1);
3719
+ if (current === void 0) intro.push(text);
3720
+ else current.parts.push(text);
3721
+ }
3722
+ const chunks = [{
3723
+ index: 0,
3724
+ kind: "summary",
3725
+ heading: null,
3726
+ text: fit([
3727
+ entry.title,
3728
+ entry.description ?? "",
3729
+ intro.join(" ")
3730
+ ].filter((part) => part !== "").join("\n"), CHUNK_LIMITS.summary)
3731
+ }];
3732
+ for (const section of sections) {
3733
+ const body = section.parts.join(" ").trim();
3734
+ if (body === "") continue;
3735
+ chunks.push({
3736
+ index: chunks.length,
3737
+ kind: "section",
3738
+ heading: section.heading,
3739
+ text: fit(`${section.heading}\n${body}`, CHUNK_LIMITS.section)
3740
+ });
3741
+ }
3742
+ return chunks;
3743
+ }
3744
+ //#endregion
3610
3745
  //#region src/index.ts
3611
3746
  const name = "@hollware/core";
3612
3747
  //#endregion
3613
- export { BUILT_IN_CONTRACT_IDS, COMARK_VERSION, ConfigError, DEFAULT_MAX_MARKDOWN_BYTES, ERROR_CODES, EVENT_ID_HEADER, HollwareError, ID_PREFIXES, MAX_MARKDOWN_BYTES_CEILING, OPERATIONS, PERSONAL_TOKEN_PREFIX, PERSONAL_TOKEN_TTL_DAYS, PIPELINE_VERSION, READ_TOKEN_PREFIX, REFUSAL_CONTRACT_IDS, SCOPES, SIGNATURE_HEADER, SITE_KEY_MAX_LENGTH, SITE_KEY_PATTERN, TIMESTAMP_HEADER, approveReview, contracts, createDraft, defineCollection, defineComponent, defineConfig, defineContract, defineOperation, describeBuiltInContracts, describeContract, describeResolvedSite, describeSite, generatePersonalToken, generateReadToken, getEntry, getTranslations, hashPersonalToken, isBuiltInContract, isId, isPersonalToken, isReadToken, isScope, isSiteKey, listEntries, listReviews, name, newId, processMarkdown, publish, renderHtml, requestChanges, resolveConfig, runContracts, schemas_exports as schemas, signWebhook, sizeLimitViolation, ulid, uniqueSlugViolation, uniqueTranslationViolation, unpublish, updateDraft, validate, verifyWebhook };
3748
+ export { BUILT_IN_CONTRACT_IDS, CHUNKER_VERSION, CHUNK_LIMITS, COMARK_VERSION, ConfigError, DEFAULT_MAX_MARKDOWN_BYTES, ERROR_CODES, EVENT_ID_HEADER, HollwareError, ID_PREFIXES, MAX_MARKDOWN_BYTES_CEILING, OPERATIONS, PERSONAL_TOKEN_PREFIX, PERSONAL_TOKEN_TTL_DAYS, PIPELINE_VERSION, READ_TOKEN_PREFIX, REFUSAL_CONTRACT_IDS, SCOPES, SIGNATURE_HEADER, SITE_KEY_MAX_LENGTH, SITE_KEY_PATTERN, TIMESTAMP_HEADER, approveReview, chunksOf, contentStats, contracts, createDraft, defineCollection, defineComponent, defineConfig, defineContract, defineOperation, describeBuiltInContracts, describeContract, describeResolvedSite, describeSite, generatePersonalToken, generateReadToken, getEntry, getTranslations, hashPersonalToken, isBuiltInContract, isId, isPersonalToken, isReadToken, isScope, isSiteKey, listEntries, listReviews, name, newId, processMarkdown, publish, renderHtml, requestChanges, resolveConfig, runContracts, schemas_exports as schemas, search, signWebhook, sizeLimitViolation, ulid, uniqueSlugViolation, uniqueTranslationViolation, unpublish, updateDraft, validate, verifyWebhook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hollware/core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Contracts, operation specs, schemas and the Markdown pipeline of Hollware. No Cloudflare dependencies.",
5
5
  "homepage": "https://hollware.com",
6
6
  "bugs": {