@ctcstack/ctcdocs-core 0.6.0 → 0.7.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.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { GENERATED_DIRECTORY_ALLOWLIST, GENERATED_FILE_ALLOWLIST, PLATFORM_ROUTE_HREFS, PLATFORM_ROUTES, PROJECT_LAYOUT, RESERVED_SLUGS, } from './project-layout.js';
1
+ export { GENERATED_DIRECTORY_ALLOWLIST, GENERATED_FILE_ALLOWLIST, PLATFORM_ROUTE_HREFS, PLATFORM_ROUTES, permanentLinkPath, PROJECT_LAYOUT, RESERVED_SLUGS, SHORT_ID_PATTERN, } from './project-layout.js';
2
2
  export { assertGeneratedPathAllowed, isGeneratedPathAllowed, normalizeRepositoryPath, } from './generated-paths.js';
3
3
  export { generatedMarkdownHeader, generatedSourceHeader, } from './ownership-markers.js';
4
4
  export { findProjectRoot, ProjectRootError } from './project-root.js';
5
5
  export { loadSiteConfiguration, parseSiteConfiguration, SiteConfigurationError, } from './site-configuration.js';
6
- export type { BrandConfiguration, DeploymentConfiguration, DeploymentEnvironmentConfiguration, DeploymentEnvironmentConfigurations, DeploymentVisibility, HomeConfiguration, NavigationConfiguration, SiteConfiguration, SyncConfigurationDefaults, } from './site-configuration.js';
6
+ export type { AddressPolicy, BrandConfiguration, DeploymentConfiguration, DeploymentEnvironmentConfiguration, DeploymentEnvironmentConfigurations, DeploymentVisibility, HomeConfiguration, NavigationConfiguration, SiteConfiguration, SyncConfigurationDefaults, } from './site-configuration.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { GENERATED_DIRECTORY_ALLOWLIST, GENERATED_FILE_ALLOWLIST, PLATFORM_ROUTE_HREFS, PLATFORM_ROUTES, PROJECT_LAYOUT, RESERVED_SLUGS, } from './project-layout.js';
1
+ export { GENERATED_DIRECTORY_ALLOWLIST, GENERATED_FILE_ALLOWLIST, PLATFORM_ROUTE_HREFS, PLATFORM_ROUTES, permanentLinkPath, PROJECT_LAYOUT, RESERVED_SLUGS, SHORT_ID_PATTERN, } from './project-layout.js';
2
2
  export { assertGeneratedPathAllowed, isGeneratedPathAllowed, normalizeRepositoryPath, } from './generated-paths.js';
3
3
  export { generatedMarkdownHeader, generatedSourceHeader, } from './ownership-markers.js';
4
4
  export { findProjectRoot, ProjectRootError } from './project-root.js';
@@ -46,11 +46,24 @@ export declare const PROJECT_LAYOUT: {
46
46
  export declare const PLATFORM_ROUTES: {
47
47
  /** The whole corpus, grouped by folder. See docs/ADR/017-full-index-page.md. */
48
48
  readonly fullIndex: "documents";
49
+ /**
50
+ * Permanent links, `/d/<short ID>/`, one per document and section. See
51
+ * docs/ADR/022-permanent-short-ids.md.
52
+ */
53
+ readonly permanentLinks: "d";
49
54
  };
50
55
  /** Each platform route as the href a link uses. */
51
56
  export declare const PLATFORM_ROUTE_HREFS: {
52
57
  readonly fullIndex: "/documents/";
53
58
  };
54
59
  export declare const RESERVED_SLUGS: readonly string[];
60
+ /**
61
+ * A short ID: lowercase hexadecimal, six characters unless six collided with
62
+ * another item's, in which case it is longer. It is recorded once per item and
63
+ * never changes, so a permanent link keeps working through every rename.
64
+ */
65
+ export declare const SHORT_ID_PATTERN: RegExp;
66
+ /** The site-relative permanent link of an item with this short ID. */
67
+ export declare function permanentLinkPath(shortId: string): string;
55
68
  export declare const GENERATED_DIRECTORY_ALLOWLIST: readonly ["src/content/docs/_generated", "src/assets/generated", "src/generated"];
56
69
  export declare const GENERATED_FILE_ALLOWLIST: readonly ["data/sync-manifest.json", "data/docs-index.json", "data/latest-sync-report.json"];
@@ -46,12 +46,27 @@ export const PROJECT_LAYOUT = {
46
46
  export const PLATFORM_ROUTES = {
47
47
  /** The whole corpus, grouped by folder. See docs/ADR/017-full-index-page.md. */
48
48
  fullIndex: 'documents',
49
+ /**
50
+ * Permanent links, `/d/<short ID>/`, one per document and section. See
51
+ * docs/ADR/022-permanent-short-ids.md.
52
+ */
53
+ permanentLinks: 'd',
49
54
  };
50
55
  /** Each platform route as the href a link uses. */
51
56
  export const PLATFORM_ROUTE_HREFS = {
52
57
  fullIndex: `/${PLATFORM_ROUTES.fullIndex}/`,
53
58
  };
54
59
  export const RESERVED_SLUGS = Object.freeze(Object.values(PLATFORM_ROUTES));
60
+ /**
61
+ * A short ID: lowercase hexadecimal, six characters unless six collided with
62
+ * another item's, in which case it is longer. It is recorded once per item and
63
+ * never changes, so a permanent link keeps working through every rename.
64
+ */
65
+ export const SHORT_ID_PATTERN = /^[0-9a-f]{6,64}$/u;
66
+ /** The site-relative permanent link of an item with this short ID. */
67
+ export function permanentLinkPath(shortId) {
68
+ return `/${PLATFORM_ROUTES.permanentLinks}/${shortId}/`;
69
+ }
55
70
  export const GENERATED_DIRECTORY_ALLOWLIST = [
56
71
  PROJECT_LAYOUT.generatedDocumentsDirectory,
57
72
  PROJECT_LAYOUT.generatedAssetsDirectory,
@@ -109,7 +109,16 @@ export interface NavigationConfiguration {
109
109
  * single word never mixes Latin, Cyrillic and Greek letters (ADR-020).
110
110
  */
111
111
  readonly nameScripts: readonly string[] | null;
112
+ /**
113
+ * Whether an address stays where it was first given (`stable`, ADR-005) or
114
+ * follows its item's current Drive path on every sync over the whole corpus
115
+ * (`follow-names`, ADR-021). Either way a moved address leaves a redirect,
116
+ * so a project can switch while its structure settles and back once people
117
+ * share links. Defaults to `stable`.
118
+ */
119
+ readonly addresses: AddressPolicy;
112
120
  }
121
+ export type AddressPolicy = 'stable' | 'follow-names';
113
122
  export interface SiteConfiguration {
114
123
  readonly brand: BrandConfiguration;
115
124
  readonly deployment: DeploymentConfiguration;
@@ -134,6 +134,16 @@ function optionalCount(source, key, path, fallback) {
134
134
  }
135
135
  return value;
136
136
  }
137
+ function optionalAddressPolicy(source, key, path) {
138
+ const value = source[key];
139
+ if (value === undefined) {
140
+ return 'stable';
141
+ }
142
+ if (value !== 'stable' && value !== 'follow-names') {
143
+ fail(path, 'must be "stable" or "follow-names"');
144
+ }
145
+ return value;
146
+ }
137
147
  function optionalVisibility(source, path) {
138
148
  const value = source.visibility;
139
149
  if (value === undefined) {
@@ -221,6 +231,7 @@ export function parseSiteConfiguration(input) {
221
231
  landingDocumentTitles: titleList(navigationSource, 'landingDocumentTitles', 'navigation.landingDocumentTitles'),
222
232
  sectionIndexPages: flag(navigationSource, 'sectionIndexPages', 'navigation.sectionIndexPages'),
223
233
  nameScripts: optionalScriptList(navigationSource, 'nameScripts', 'navigation.nameScripts'),
234
+ addresses: optionalAddressPolicy(navigationSource, 'addresses', 'navigation.addresses'),
224
235
  },
225
236
  sync: {
226
237
  commitBotName: text(syncSource, 'commitBotName', 'sync.commitBotName'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctcstack/ctcdocs-core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Configuration schema, project layout, and generated-path allowlist shared by the CTCDocs platform packages.",
5
5
  "keywords": [
6
6
  "ctcdocs",
package/src/index.ts CHANGED
@@ -3,8 +3,10 @@ export {
3
3
  GENERATED_FILE_ALLOWLIST,
4
4
  PLATFORM_ROUTE_HREFS,
5
5
  PLATFORM_ROUTES,
6
+ permanentLinkPath,
6
7
  PROJECT_LAYOUT,
7
8
  RESERVED_SLUGS,
9
+ SHORT_ID_PATTERN,
8
10
  } from './project-layout.js';
9
11
  export {
10
12
  assertGeneratedPathAllowed,
@@ -22,6 +24,7 @@ export {
22
24
  SiteConfigurationError,
23
25
  } from './site-configuration.js';
24
26
  export type {
27
+ AddressPolicy,
25
28
  BrandConfiguration,
26
29
  DeploymentConfiguration,
27
30
  DeploymentEnvironmentConfiguration,
@@ -47,6 +47,11 @@ export const PROJECT_LAYOUT = {
47
47
  export const PLATFORM_ROUTES = {
48
48
  /** The whole corpus, grouped by folder. See docs/ADR/017-full-index-page.md. */
49
49
  fullIndex: 'documents',
50
+ /**
51
+ * Permanent links, `/d/<short ID>/`, one per document and section. See
52
+ * docs/ADR/022-permanent-short-ids.md.
53
+ */
54
+ permanentLinks: 'd',
50
55
  } as const;
51
56
 
52
57
  /** Each platform route as the href a link uses. */
@@ -58,6 +63,18 @@ export const RESERVED_SLUGS: readonly string[] = Object.freeze(
58
63
  Object.values(PLATFORM_ROUTES),
59
64
  );
60
65
 
66
+ /**
67
+ * A short ID: lowercase hexadecimal, six characters unless six collided with
68
+ * another item's, in which case it is longer. It is recorded once per item and
69
+ * never changes, so a permanent link keeps working through every rename.
70
+ */
71
+ export const SHORT_ID_PATTERN = /^[0-9a-f]{6,64}$/u;
72
+
73
+ /** The site-relative permanent link of an item with this short ID. */
74
+ export function permanentLinkPath(shortId: string): string {
75
+ return `/${PLATFORM_ROUTES.permanentLinks}/${shortId}/`;
76
+ }
77
+
61
78
  export const GENERATED_DIRECTORY_ALLOWLIST = [
62
79
  PROJECT_LAYOUT.generatedDocumentsDirectory,
63
80
  PROJECT_LAYOUT.generatedAssetsDirectory,
@@ -134,8 +134,18 @@ export interface NavigationConfiguration {
134
134
  * single word never mixes Latin, Cyrillic and Greek letters (ADR-020).
135
135
  */
136
136
  readonly nameScripts: readonly string[] | null;
137
+ /**
138
+ * Whether an address stays where it was first given (`stable`, ADR-005) or
139
+ * follows its item's current Drive path on every sync over the whole corpus
140
+ * (`follow-names`, ADR-021). Either way a moved address leaves a redirect,
141
+ * so a project can switch while its structure settles and back once people
142
+ * share links. Defaults to `stable`.
143
+ */
144
+ readonly addresses: AddressPolicy;
137
145
  }
138
146
 
147
+ export type AddressPolicy = 'stable' | 'follow-names';
148
+
139
149
  export interface SiteConfiguration {
140
150
  readonly brand: BrandConfiguration;
141
151
  readonly deployment: DeploymentConfiguration;
@@ -312,6 +322,21 @@ function optionalCount(
312
322
  return value;
313
323
  }
314
324
 
325
+ function optionalAddressPolicy(
326
+ source: Record<string, unknown>,
327
+ key: string,
328
+ path: string,
329
+ ): AddressPolicy {
330
+ const value = source[key];
331
+ if (value === undefined) {
332
+ return 'stable';
333
+ }
334
+ if (value !== 'stable' && value !== 'follow-names') {
335
+ fail(path, 'must be "stable" or "follow-names"');
336
+ }
337
+ return value;
338
+ }
339
+
315
340
  function optionalVisibility(
316
341
  source: Record<string, unknown>,
317
342
  path: string,
@@ -450,6 +475,11 @@ export function parseSiteConfiguration(input: unknown): SiteConfiguration {
450
475
  'nameScripts',
451
476
  'navigation.nameScripts',
452
477
  ),
478
+ addresses: optionalAddressPolicy(
479
+ navigationSource,
480
+ 'addresses',
481
+ 'navigation.addresses',
482
+ ),
453
483
  },
454
484
  sync: {
455
485
  commitBotName: text(syncSource, 'commitBotName', 'sync.commitBotName'),