@wildo-ai/saas-technical-doc 1.1.1 → 1.1.2

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.
Files changed (64) hide show
  1. package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts +52 -0
  2. package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts.map +1 -0
  3. package/dist/esm/companion/application-documentation/application-administration-documentation.js +58 -0
  4. package/dist/esm/companion/application-documentation/application-administration-documentation.js.map +1 -0
  5. package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts +76 -0
  6. package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts.map +1 -0
  7. package/dist/esm/companion/application-documentation/application-authentication-documentation.js +116 -0
  8. package/dist/esm/companion/application-documentation/application-authentication-documentation.js.map +1 -0
  9. package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts +87 -0
  10. package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts.map +1 -0
  11. package/dist/esm/companion/application-documentation/application-connection-documentation.js +138 -0
  12. package/dist/esm/companion/application-documentation/application-connection-documentation.js.map +1 -0
  13. package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts +48 -0
  14. package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts.map +1 -0
  15. package/dist/esm/companion/application-documentation/application-integration-documentation.js +63 -0
  16. package/dist/esm/companion/application-documentation/application-integration-documentation.js.map +1 -0
  17. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts +21 -3
  18. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts.map +1 -1
  19. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js +166 -10
  20. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js.map +1 -1
  21. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts +8 -0
  22. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts.map +1 -1
  23. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js +8 -1
  24. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js.map +1 -1
  25. package/dist/esm/companion/index.d.ts +4 -0
  26. package/dist/esm/companion/index.d.ts.map +1 -1
  27. package/dist/esm/companion/index.js +4 -0
  28. package/dist/esm/companion/index.js.map +1 -1
  29. package/dist/esm/companion/openapi-generator.d.ts.map +1 -1
  30. package/dist/esm/companion/openapi-generator.js +9 -7
  31. package/dist/esm/companion/openapi-generator.js.map +1 -1
  32. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts.map +1 -1
  33. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js +10 -2
  34. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js.map +1 -1
  35. package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts +28 -0
  36. package/dist/esm/companion/rendering/technical-documentation-markdown-links.d.ts.map +1 -0
  37. package/dist/esm/companion/rendering/technical-documentation-markdown-links.js +52 -0
  38. package/dist/esm/companion/rendering/technical-documentation-markdown-links.js.map +1 -0
  39. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.d.ts.map +1 -1
  40. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js +7 -2
  41. package/dist/esm/companion/rendering/technical-documentation-markdown-renderer.js.map +1 -1
  42. package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts +1 -0
  43. package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts.map +1 -1
  44. package/dist/esm/companion/rendering/technical-documentation-render-model.js +91 -84
  45. package/dist/esm/companion/rendering/technical-documentation-render-model.js.map +1 -1
  46. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts +74 -69
  47. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts.map +1 -1
  48. package/dist/esm/content/application-consumer-documentation-content.techdoc.js +654 -1120
  49. package/dist/esm/content/application-consumer-documentation-content.techdoc.js.map +1 -1
  50. package/dist/esm/runtime/decode-jwt-claims.d.ts +7 -4
  51. package/dist/esm/runtime/decode-jwt-claims.d.ts.map +1 -1
  52. package/dist/esm/runtime/decode-jwt-claims.js +7 -4
  53. package/dist/esm/runtime/decode-jwt-claims.js.map +1 -1
  54. package/dist/esm/runtime/docs-auth-session.schemas.d.ts +16 -5
  55. package/dist/esm/runtime/docs-auth-session.schemas.d.ts.map +1 -1
  56. package/dist/esm/runtime/docs-auth-session.schemas.js +16 -5
  57. package/dist/esm/runtime/docs-auth-session.schemas.js.map +1 -1
  58. package/dist/esm/runtime/use-docs-auth-session.d.ts +9 -7
  59. package/dist/esm/runtime/use-docs-auth-session.d.ts.map +1 -1
  60. package/dist/esm/runtime/use-docs-auth-session.js +9 -7
  61. package/dist/esm/runtime/use-docs-auth-session.js.map +1 -1
  62. package/dist/tsconfig.build.tsbuildinfo +1 -1
  63. package/package.json +5 -5
  64. package/dist/esm/.builder.pid +0 -9
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Who may perform each administrative action in THIS application.
3
+ *
4
+ * The roles page lists the roles and what each is for; it could not say which role may actually
5
+ * invite a person, change their access or move a team, because that lives on the operations —
6
+ * every one of which declares the roles it accepts. An administrator's first question ("can a
7
+ * manager do this, or does it need an owner?") therefore had no answer anywhere in the portal, and
8
+ * the pages fell back on prose about least privilege.
9
+ *
10
+ * The projection joins two things the companion already holds: the admitted operations for the
11
+ * membership and organization-unit resources, and the application's own role labels. It is a
12
+ * matrix of what this application publishes, not a restatement of the framework's defaults.
13
+ */
14
+ export declare const APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_REF: "source:companion-projection:application-administration";
15
+ export declare const APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_VERSION = 1;
16
+ export interface ApplicationAdministrationActionSource {
17
+ /** What the action does, in the words the API reference uses for the same operation. */
18
+ readonly action: string;
19
+ /**
20
+ * Labels of the organization roles that may perform it, in the application's own words.
21
+ * Empty means the operation declares no role gate — see `openToEveryMember`.
22
+ */
23
+ readonly roleLabels: readonly string[];
24
+ /**
25
+ * True when the operation declares no required role. That is a real, intentional state (any
26
+ * authenticated member of the organization may call it), not a gap in the projection, so it is
27
+ * said explicitly rather than shown as an empty cell.
28
+ */
29
+ readonly openToEveryMember: boolean;
30
+ }
31
+ export interface ApplicationAdministrationDocumentationSource {
32
+ readonly memberActions: readonly ApplicationAdministrationActionSource[];
33
+ readonly organizationUnitActions: readonly ApplicationAdministrationActionSource[];
34
+ }
35
+ /** Named presentations a page may request with `{{APPLICATION_ADMINISTRATION:<selector>}}`. */
36
+ export declare enum ApplicationAdministrationPresentation {
37
+ /** Membership actions — invite, change access, suspend, remove — against the roles that may do them. */
38
+ MEMBER_ACTIONS = "memberActions",
39
+ /** Organization-unit actions against the roles that may do them. */
40
+ ORGANIZATION_UNIT_ACTIONS = "organizationUnitActions"
41
+ }
42
+ export interface ApplicationAdministrationDocumentationFact {
43
+ readonly sourceRef: string;
44
+ readonly sourceVersion: number;
45
+ readonly presentations: Readonly<Record<ApplicationAdministrationPresentation, string>>;
46
+ }
47
+ /**
48
+ * Projects the administrative matrix. A pure function of the source, so two runs over the same
49
+ * application produce identical bytes.
50
+ */
51
+ export declare function projectApplicationAdministrationDocumentation(source: ApplicationAdministrationDocumentationSource): ApplicationAdministrationDocumentationFact;
52
+ //# sourceMappingURL=application-administration-documentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-administration-documentation.d.ts","sourceRoot":"","sources":["../../../../../src/companion/application-documentation/application-administration-documentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mDAAmD,EAAG,wDAAiE,CAAC;AACrI,eAAO,MAAM,uDAAuD,IAAI,CAAC;AAEzE,MAAM,WAAW,qCAAqC;IACpD,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,4CAA4C;IAC3D,QAAQ,CAAC,aAAa,EAAE,SAAS,qCAAqC,EAAE,CAAC;IACzE,QAAQ,CAAC,uBAAuB,EAAE,SAAS,qCAAqC,EAAE,CAAC;CACpF;AAED,+FAA+F;AAC/F,oBAAY,qCAAqC;IAC/C,wGAAwG;IACxG,cAAc,kBAAkB;IAChC,oEAAoE;IACpE,yBAAyB,4BAA4B;CACtD;AAED,MAAM,WAAW,0CAA0C;IACzD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,qCAAqC,EAAE,MAAM,CAAC,CAAC,CAAC;CACzF;AAoBD;;;GAGG;AACH,wBAAgB,6CAA6C,CAC3D,MAAM,EAAE,4CAA4C,GACnD,0CAA0C,CAoB5C"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Who may perform each administrative action in THIS application.
3
+ *
4
+ * The roles page lists the roles and what each is for; it could not say which role may actually
5
+ * invite a person, change their access or move a team, because that lives on the operations —
6
+ * every one of which declares the roles it accepts. An administrator's first question ("can a
7
+ * manager do this, or does it need an owner?") therefore had no answer anywhere in the portal, and
8
+ * the pages fell back on prose about least privilege.
9
+ *
10
+ * The projection joins two things the companion already holds: the admitted operations for the
11
+ * membership and organization-unit resources, and the application's own role labels. It is a
12
+ * matrix of what this application publishes, not a restatement of the framework's defaults.
13
+ */
14
+ export const APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_REF = 'source:companion-projection:application-administration';
15
+ export const APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_VERSION = 1;
16
+ /** Named presentations a page may request with `{{APPLICATION_ADMINISTRATION:<selector>}}`. */
17
+ export var ApplicationAdministrationPresentation;
18
+ (function (ApplicationAdministrationPresentation) {
19
+ /** Membership actions — invite, change access, suspend, remove — against the roles that may do them. */
20
+ ApplicationAdministrationPresentation["MEMBER_ACTIONS"] = "memberActions";
21
+ /** Organization-unit actions against the roles that may do them. */
22
+ ApplicationAdministrationPresentation["ORGANIZATION_UNIT_ACTIONS"] = "organizationUnitActions";
23
+ })(ApplicationAdministrationPresentation || (ApplicationAdministrationPresentation = {}));
24
+ function actionsTable(actions, emptyMessage) {
25
+ if (actions.length === 0)
26
+ return emptyMessage;
27
+ const rows = actions
28
+ .map((action) => {
29
+ const who = action.openToEveryMember
30
+ ? 'Any active member of the organization'
31
+ : action.roleLabels.length === 0
32
+ ? 'Not published for organization roles'
33
+ : action.roleLabels.join(', ');
34
+ return `| ${action.action} | ${who} |`;
35
+ })
36
+ .join('\n');
37
+ return `| Action | Who may perform it |\n| --- | --- |\n${rows}\n\nA role higher in the inheritance chain can do everything the roles below it can, so a role that is not named here may still qualify through the role it includes. The operation contract in the API reference is authoritative for one exact call.`;
38
+ }
39
+ /**
40
+ * Projects the administrative matrix. A pure function of the source, so two runs over the same
41
+ * application produce identical bytes.
42
+ */
43
+ export function projectApplicationAdministrationDocumentation(source) {
44
+ for (const action of [...source.memberActions, ...source.organizationUnitActions]) {
45
+ if (action.action.trim().length === 0) {
46
+ throw new Error('application administration projection received an action with no description');
47
+ }
48
+ }
49
+ return {
50
+ sourceRef: APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_REF,
51
+ sourceVersion: APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_VERSION,
52
+ presentations: Object.freeze({
53
+ [ApplicationAdministrationPresentation.MEMBER_ACTIONS]: actionsTable(source.memberActions, 'This application publishes no membership administration operations, so member access is managed outside this documentation.'),
54
+ [ApplicationAdministrationPresentation.ORGANIZATION_UNIT_ACTIONS]: actionsTable(source.organizationUnitActions, 'This application does not publish organization units, so there are no unit administration actions.'),
55
+ }),
56
+ };
57
+ }
58
+ //# sourceMappingURL=application-administration-documentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-administration-documentation.js","sourceRoot":"","sources":["../../../../../src/companion/application-documentation/application-administration-documentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,mDAAmD,GAAG,wDAAiE,CAAC;AACrI,MAAM,CAAC,MAAM,uDAAuD,GAAG,CAAC,CAAC;AAuBzE,+FAA+F;AAC/F,MAAM,CAAN,IAAY,qCAKX;AALD,WAAY,qCAAqC;IAC/C,wGAAwG;IACxG,yEAAgC,CAAA;IAChC,oEAAoE;IACpE,8FAAqD,CAAA;AACvD,CAAC,EALW,qCAAqC,KAArC,qCAAqC,QAKhD;AAQD,SAAS,YAAY,CACnB,OAAyD,EACzD,YAAoB;IAEpB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IAC9C,MAAM,IAAI,GAAG,OAAO;SACjB,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACd,MAAM,GAAG,GAAG,MAAM,CAAC,iBAAiB;YAClC,CAAC,CAAC,uCAAuC;YACzC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;gBAC9B,CAAC,CAAC,sCAAsC;gBACxC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,KAAK,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC;IACzC,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO,mDAAmD,IAAI,wPAAwP,CAAC;AACzT,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6CAA6C,CAC3D,MAAoD;IAEpD,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,MAAM,CAAC,aAAa,EAAE,GAAG,MAAM,CAAC,uBAAuB,CAAC,EAAE,CAAC;QAClF,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;QAClG,CAAC;IACH,CAAC;IACD,OAAO;QACL,SAAS,EAAE,mDAAmD;QAC9D,aAAa,EAAE,uDAAuD;QACtE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC;YAC3B,CAAC,qCAAqC,CAAC,cAAc,CAAC,EAAE,YAAY,CAClE,MAAM,CAAC,aAAa,EACpB,6HAA6H,CAC9H;YACD,CAAC,qCAAqC,CAAC,yBAAyB,CAAC,EAAE,YAAY,CAC7E,MAAM,CAAC,uBAAuB,EAC9B,oGAAoG,CACrG;SACF,CAAC;KACH,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Who may perform each administrative action in THIS application.\n *\n * The roles page lists the roles and what each is for; it could not say which role may actually\n * invite a person, change their access or move a team, because that lives on the operations —\n * every one of which declares the roles it accepts. An administrator's first question (\"can a\n * manager do this, or does it need an owner?\") therefore had no answer anywhere in the portal, and\n * the pages fell back on prose about least privilege.\n *\n * The projection joins two things the companion already holds: the admitted operations for the\n * membership and organization-unit resources, and the application's own role labels. It is a\n * matrix of what this application publishes, not a restatement of the framework's defaults.\n */\nexport const APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_REF = 'source:companion-projection:application-administration' as const;\nexport const APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_VERSION = 1;\n\nexport interface ApplicationAdministrationActionSource {\n /** What the action does, in the words the API reference uses for the same operation. */\n readonly action: string;\n /**\n * Labels of the organization roles that may perform it, in the application's own words.\n * Empty means the operation declares no role gate — see `openToEveryMember`.\n */\n readonly roleLabels: readonly string[];\n /**\n * True when the operation declares no required role. That is a real, intentional state (any\n * authenticated member of the organization may call it), not a gap in the projection, so it is\n * said explicitly rather than shown as an empty cell.\n */\n readonly openToEveryMember: boolean;\n}\n\nexport interface ApplicationAdministrationDocumentationSource {\n readonly memberActions: readonly ApplicationAdministrationActionSource[];\n readonly organizationUnitActions: readonly ApplicationAdministrationActionSource[];\n}\n\n/** Named presentations a page may request with `{{APPLICATION_ADMINISTRATION:<selector>}}`. */\nexport enum ApplicationAdministrationPresentation {\n /** Membership actions — invite, change access, suspend, remove — against the roles that may do them. */\n MEMBER_ACTIONS = 'memberActions',\n /** Organization-unit actions against the roles that may do them. */\n ORGANIZATION_UNIT_ACTIONS = 'organizationUnitActions',\n}\n\nexport interface ApplicationAdministrationDocumentationFact {\n readonly sourceRef: string;\n readonly sourceVersion: number;\n readonly presentations: Readonly<Record<ApplicationAdministrationPresentation, string>>;\n}\n\nfunction actionsTable(\n actions: readonly ApplicationAdministrationActionSource[],\n emptyMessage: string,\n): string {\n if (actions.length === 0) return emptyMessage;\n const rows = actions\n .map((action) => {\n const who = action.openToEveryMember\n ? 'Any active member of the organization'\n : action.roleLabels.length === 0\n ? 'Not published for organization roles'\n : action.roleLabels.join(', ');\n return `| ${action.action} | ${who} |`;\n })\n .join('\\n');\n return `| Action | Who may perform it |\\n| --- | --- |\\n${rows}\\n\\nA role higher in the inheritance chain can do everything the roles below it can, so a role that is not named here may still qualify through the role it includes. The operation contract in the API reference is authoritative for one exact call.`;\n}\n\n/**\n * Projects the administrative matrix. A pure function of the source, so two runs over the same\n * application produce identical bytes.\n */\nexport function projectApplicationAdministrationDocumentation(\n source: ApplicationAdministrationDocumentationSource,\n): ApplicationAdministrationDocumentationFact {\n for (const action of [...source.memberActions, ...source.organizationUnitActions]) {\n if (action.action.trim().length === 0) {\n throw new Error('application administration projection received an action with no description');\n }\n }\n return {\n sourceRef: APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_REF,\n sourceVersion: APPLICATION_ADMINISTRATION_DOCUMENTATION_SOURCE_VERSION,\n presentations: Object.freeze({\n [ApplicationAdministrationPresentation.MEMBER_ACTIONS]: actionsTable(\n source.memberActions,\n 'This application publishes no membership administration operations, so member access is managed outside this documentation.',\n ),\n [ApplicationAdministrationPresentation.ORGANIZATION_UNIT_ACTIONS]: actionsTable(\n source.organizationUnitActions,\n 'This application does not publish organization units, so there are no unit administration actions.',\n ),\n }),\n };\n}\n"]}
@@ -0,0 +1,76 @@
1
+ import { AuthMethod } from '@wildo-ai/saas-models';
2
+ /**
3
+ * What THIS application actually asks of a person signing in.
4
+ *
5
+ * The engine catalogue can only say which sign-in methods exist. Three pages printed that
6
+ * catalogue and warned the reader it was "not a promise", which is honest and useless: a Wonder
7
+ * Todos reader was shown SMS codes and external identity providers the application never enables,
8
+ * and was told nothing about the password rules, the lockout, the session lifetime or how many
9
+ * recovery codes an enrolment hands out — all of which are configured, and all of which the
10
+ * companion can read.
11
+ *
12
+ * Policy is per user type by construction (`UserTypeAuthPolicy`), so the projection is too. A user
13
+ * type is shown by its configured key, not by an invented display name: the key is what the
14
+ * application calls that kind of account, and there is no label source that could say otherwise.
15
+ */
16
+ export declare const APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_REF: "source:companion-projection:application-authentication";
17
+ export declare const APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_VERSION = 1;
18
+ export interface ApplicationUserTypeAuthenticationSource {
19
+ /** The application's own key for this kind of account, for example `member` or `admin`. */
20
+ readonly userTypeKey: string;
21
+ /** Methods this application enables for the user type, in the engine's declaration order. */
22
+ readonly enabledMethods: readonly AuthMethod[];
23
+ readonly multifactor: {
24
+ readonly required: boolean;
25
+ readonly strongRequired: boolean;
26
+ readonly enrollmentGracePeriodDays: number;
27
+ readonly passkeyExemptFromMultifactor: boolean;
28
+ /** When set, the only second factors this application accepts. */
29
+ readonly acceptableMethods?: readonly AuthMethod[];
30
+ };
31
+ readonly password: {
32
+ readonly minLength: number;
33
+ readonly maxLength: number;
34
+ readonly requireUppercase: boolean;
35
+ readonly requireLowercase: boolean;
36
+ readonly requireNumbers: boolean;
37
+ readonly requireSpecialChars: boolean;
38
+ /** Days before a password must be changed. `0` means it does not expire. */
39
+ readonly expiryDays: number;
40
+ };
41
+ readonly session: {
42
+ readonly durationMinutes: number;
43
+ readonly maxConcurrentSessions?: number;
44
+ };
45
+ readonly lockout: {
46
+ readonly maxLoginAttempts: number;
47
+ readonly lockoutDurationMinutes: number;
48
+ readonly progressive: boolean;
49
+ readonly maxProgressiveLockoutHours: number;
50
+ };
51
+ }
52
+ export interface ApplicationAuthenticationDocumentationSource {
53
+ readonly userTypes: readonly ApplicationUserTypeAuthenticationSource[];
54
+ }
55
+ /** Named presentations a page may request with `{{APPLICATION_AUTHENTICATION:<selector>}}`. */
56
+ export declare enum ApplicationAuthenticationPresentation {
57
+ /** The sign-in methods this application enables, per user type. */
58
+ ENABLED_SIGN_IN_METHODS = "enabledSignInMethods",
59
+ /** Whether a second factor is required, which ones count, and what enrolment hands out. */
60
+ MULTIFACTOR_POLICY = "multifactorPolicy",
61
+ /** The password rules a person must satisfy. */
62
+ PASSWORD_RULES = "passwordRules",
63
+ /** How long a session lasts and what a run of failed attempts costs. */
64
+ SESSION_AND_LOCKOUT = "sessionAndLockout"
65
+ }
66
+ export interface ApplicationAuthenticationDocumentationFact {
67
+ readonly sourceRef: string;
68
+ readonly sourceVersion: number;
69
+ readonly presentations: Readonly<Record<ApplicationAuthenticationPresentation, string>>;
70
+ }
71
+ /**
72
+ * Projects the application's authentication policy into the presentations a page may request.
73
+ * A pure function of the source, so two runs over the same application produce identical bytes.
74
+ */
75
+ export declare function projectApplicationAuthenticationDocumentation(source: ApplicationAuthenticationDocumentationSource): ApplicationAuthenticationDocumentationFact;
76
+ //# sourceMappingURL=application-authentication-documentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-authentication-documentation.d.ts","sourceRoot":"","sources":["../../../../../src/companion/application-documentation/application-authentication-documentation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA8D,MAAM,uBAAuB,CAAC;AAG/G;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mDAAmD,EAAG,wDAAiE,CAAC;AACrI,eAAO,MAAM,uDAAuD,IAAI,CAAC;AAEzE,MAAM,WAAW,uCAAuC;IACtD,2FAA2F;IAC3F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6FAA6F;IAC7F,QAAQ,CAAC,cAAc,EAAE,SAAS,UAAU,EAAE,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;QAC3B,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;QACjC,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC;QAC3C,QAAQ,CAAC,4BAA4B,EAAE,OAAO,CAAC;QAC/C,kEAAkE;QAClE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;KACpD,CAAC;IACF,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;QACnC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;QACnC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;QACjC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;QACtC,4EAA4E;QAC5E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;QACjC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;KACzC,CAAC;IACF,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;QAClC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;QACxC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;QAC9B,QAAQ,CAAC,0BAA0B,EAAE,MAAM,CAAC;KAC7C,CAAC;CACH;AAED,MAAM,WAAW,4CAA4C;IAC3D,QAAQ,CAAC,SAAS,EAAE,SAAS,uCAAuC,EAAE,CAAC;CACxE;AAED,+FAA+F;AAC/F,oBAAY,qCAAqC;IAC/C,mEAAmE;IACnE,uBAAuB,yBAAyB;IAChD,2FAA2F;IAC3F,kBAAkB,sBAAsB;IACxC,gDAAgD;IAChD,cAAc,kBAAkB;IAChC,wEAAwE;IACxE,mBAAmB,sBAAsB;CAC1C;AAED,MAAM,WAAW,0CAA0C;IACzD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,qCAAqC,EAAE,MAAM,CAAC,CAAC,CAAC;CACzF;AAoED;;;GAGG;AACH,wBAAgB,6CAA6C,CAC3D,MAAM,EAAE,4CAA4C,GACnD,0CAA0C,CAmB5C"}
@@ -0,0 +1,116 @@
1
+ import { MFA_BACKUP_CODE_CHARACTER_LENGTH, MFA_BACKUP_CODE_CONTRACT } from '@wildo-ai/saas-models';
2
+ import { authenticationMethodConsumerWords } from '@wildo-ai/saas-specifications/companion';
3
+ /**
4
+ * What THIS application actually asks of a person signing in.
5
+ *
6
+ * The engine catalogue can only say which sign-in methods exist. Three pages printed that
7
+ * catalogue and warned the reader it was "not a promise", which is honest and useless: a Wonder
8
+ * Todos reader was shown SMS codes and external identity providers the application never enables,
9
+ * and was told nothing about the password rules, the lockout, the session lifetime or how many
10
+ * recovery codes an enrolment hands out — all of which are configured, and all of which the
11
+ * companion can read.
12
+ *
13
+ * Policy is per user type by construction (`UserTypeAuthPolicy`), so the projection is too. A user
14
+ * type is shown by its configured key, not by an invented display name: the key is what the
15
+ * application calls that kind of account, and there is no label source that could say otherwise.
16
+ */
17
+ export const APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_REF = 'source:companion-projection:application-authentication';
18
+ export const APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_VERSION = 1;
19
+ /** Named presentations a page may request with `{{APPLICATION_AUTHENTICATION:<selector>}}`. */
20
+ export var ApplicationAuthenticationPresentation;
21
+ (function (ApplicationAuthenticationPresentation) {
22
+ /** The sign-in methods this application enables, per user type. */
23
+ ApplicationAuthenticationPresentation["ENABLED_SIGN_IN_METHODS"] = "enabledSignInMethods";
24
+ /** Whether a second factor is required, which ones count, and what enrolment hands out. */
25
+ ApplicationAuthenticationPresentation["MULTIFACTOR_POLICY"] = "multifactorPolicy";
26
+ /** The password rules a person must satisfy. */
27
+ ApplicationAuthenticationPresentation["PASSWORD_RULES"] = "passwordRules";
28
+ /** How long a session lasts and what a run of failed attempts costs. */
29
+ ApplicationAuthenticationPresentation["SESSION_AND_LOCKOUT"] = "sessionAndLockout";
30
+ })(ApplicationAuthenticationPresentation || (ApplicationAuthenticationPresentation = {}));
31
+ function methodNames(methods) {
32
+ if (methods.length === 0)
33
+ return 'None';
34
+ return methods.map((method) => authenticationMethodConsumerWords(method).name).join(', ');
35
+ }
36
+ function userTypeCell(userType) {
37
+ return `\`${userType.userTypeKey}\``;
38
+ }
39
+ function enabledSignInMethodsMarkdown(source) {
40
+ const rows = source.userTypes
41
+ .map((userType) => `| ${userTypeCell(userType)} | ${methodNames(userType.enabledMethods)} |`)
42
+ .join('\n');
43
+ return `| Account kind | Sign-in methods this application enables |\n| --- | --- |\n${rows}\n\nAn organization can narrow this further for its own people — for example by requiring its single sign-on — so the sign-in screen remains the last word on what you can use right now.`;
44
+ }
45
+ function multifactorPolicyMarkdown(source) {
46
+ const rows = source.userTypes.map((userType) => {
47
+ const { multifactor } = userType;
48
+ const required = multifactor.required
49
+ ? multifactor.strongRequired ? 'Required, and a phishing-resistant method is required' : 'Required'
50
+ : 'Optional';
51
+ const grace = multifactor.required && multifactor.enrollmentGracePeriodDays > 0
52
+ ? `${multifactor.enrollmentGracePeriodDays} day${multifactor.enrollmentGracePeriodDays === 1 ? '' : 's'} to enrol`
53
+ : multifactor.required ? 'No grace period' : '—';
54
+ const accepted = multifactor.acceptableMethods === undefined
55
+ ? 'Any second factor enabled above'
56
+ : methodNames(multifactor.acceptableMethods);
57
+ const passkey = multifactor.passkeyExemptFromMultifactor ? 'Yes' : 'No';
58
+ return `| ${userTypeCell(userType)} | ${required} | ${grace} | ${accepted} | ${passkey} |`;
59
+ }).join('\n');
60
+ const codes = `Enrolling an authenticator app also hands you **${MFA_BACKUP_CODE_CONTRACT.codeCount} recovery codes** of ${MFA_BACKUP_CODE_CHARACTER_LENGTH} characters each. ${MFA_BACKUP_CODE_CONTRACT.singleUse ? 'Each code works once and is gone when used' : 'Codes may be reused'}; the application stores only a hash of them, so nobody can read them back to you. Enrolling again replaces the whole set.`;
61
+ return `| Account kind | Second factor | Enrolment window | Accepted as a second factor | Passkey counts on its own |\n| --- | --- | --- | --- | --- |\n${rows}\n\n${codes}`;
62
+ }
63
+ function passwordRulesMarkdown(source) {
64
+ const rows = source.userTypes.map((userType) => {
65
+ const { password } = userType;
66
+ const classes = [
67
+ password.requireUppercase ? 'an uppercase letter' : null,
68
+ password.requireLowercase ? 'a lowercase letter' : null,
69
+ password.requireNumbers ? 'a digit' : null,
70
+ password.requireSpecialChars ? 'a special character' : null,
71
+ ].filter((requirement) => requirement !== null);
72
+ const composition = classes.length === 0 ? 'No character requirement' : `Must contain ${classes.join(', ')}`;
73
+ const expiry = password.expiryDays > 0 ? `Every ${password.expiryDays} days` : 'Never expires';
74
+ return `| ${userTypeCell(userType)} | ${password.minLength}–${password.maxLength} characters | ${composition} | ${expiry} |`;
75
+ }).join('\n');
76
+ return `| Account kind | Length | Composition | Change required |\n| --- | --- | --- | --- |\n${rows}`;
77
+ }
78
+ function sessionAndLockoutMarkdown(source) {
79
+ const rows = source.userTypes.map((userType) => {
80
+ const { session, lockout } = userType;
81
+ const concurrent = session.maxConcurrentSessions === undefined
82
+ ? 'Not limited'
83
+ : `${session.maxConcurrentSessions} at a time`;
84
+ const progressive = lockout.progressive
85
+ ? `, lengthening with each further run up to ${lockout.maxProgressiveLockoutHours} hours`
86
+ : '';
87
+ const lock = `${lockout.maxLoginAttempts} failed attempts lock the account for ${lockout.lockoutDurationMinutes} minutes${progressive}`;
88
+ return `| ${userTypeCell(userType)} | ${session.durationMinutes} minutes | ${concurrent} | ${lock} |`;
89
+ }).join('\n');
90
+ return `| Account kind | Session lasts | Concurrent sessions | Failed sign-ins |\n| --- | --- | --- | --- |\n${rows}\n\nA session ends when its time runs out, when you sign out, or when the application invalidates it — changing a password does exactly that, on every device.`;
91
+ }
92
+ /**
93
+ * Projects the application's authentication policy into the presentations a page may request.
94
+ * A pure function of the source, so two runs over the same application produce identical bytes.
95
+ */
96
+ export function projectApplicationAuthenticationDocumentation(source) {
97
+ if (source.userTypes.length === 0) {
98
+ throw new Error('application authentication projection requires at least one declared user type');
99
+ }
100
+ for (const userType of source.userTypes) {
101
+ if (userType.userTypeKey.trim().length === 0) {
102
+ throw new Error('application authentication projection received a user type with no key');
103
+ }
104
+ }
105
+ return {
106
+ sourceRef: APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_REF,
107
+ sourceVersion: APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_VERSION,
108
+ presentations: Object.freeze({
109
+ [ApplicationAuthenticationPresentation.ENABLED_SIGN_IN_METHODS]: enabledSignInMethodsMarkdown(source),
110
+ [ApplicationAuthenticationPresentation.MULTIFACTOR_POLICY]: multifactorPolicyMarkdown(source),
111
+ [ApplicationAuthenticationPresentation.PASSWORD_RULES]: passwordRulesMarkdown(source),
112
+ [ApplicationAuthenticationPresentation.SESSION_AND_LOCKOUT]: sessionAndLockoutMarkdown(source),
113
+ }),
114
+ };
115
+ }
116
+ //# sourceMappingURL=application-authentication-documentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-authentication-documentation.js","sourceRoot":"","sources":["../../../../../src/companion/application-documentation/application-authentication-documentation.ts"],"names":[],"mappings":"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","sourcesContent":["import { AuthMethod, MFA_BACKUP_CODE_CHARACTER_LENGTH, MFA_BACKUP_CODE_CONTRACT } from '@wildo-ai/saas-models';\nimport { authenticationMethodConsumerWords } from '@wildo-ai/saas-specifications/companion';\n\n/**\n * What THIS application actually asks of a person signing in.\n *\n * The engine catalogue can only say which sign-in methods exist. Three pages printed that\n * catalogue and warned the reader it was \"not a promise\", which is honest and useless: a Wonder\n * Todos reader was shown SMS codes and external identity providers the application never enables,\n * and was told nothing about the password rules, the lockout, the session lifetime or how many\n * recovery codes an enrolment hands out — all of which are configured, and all of which the\n * companion can read.\n *\n * Policy is per user type by construction (`UserTypeAuthPolicy`), so the projection is too. A user\n * type is shown by its configured key, not by an invented display name: the key is what the\n * application calls that kind of account, and there is no label source that could say otherwise.\n */\nexport const APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_REF = 'source:companion-projection:application-authentication' as const;\nexport const APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_VERSION = 1;\n\nexport interface ApplicationUserTypeAuthenticationSource {\n /** The application's own key for this kind of account, for example `member` or `admin`. */\n readonly userTypeKey: string;\n /** Methods this application enables for the user type, in the engine's declaration order. */\n readonly enabledMethods: readonly AuthMethod[];\n readonly multifactor: {\n readonly required: boolean;\n readonly strongRequired: boolean;\n readonly enrollmentGracePeriodDays: number;\n readonly passkeyExemptFromMultifactor: boolean;\n /** When set, the only second factors this application accepts. */\n readonly acceptableMethods?: readonly AuthMethod[];\n };\n readonly password: {\n readonly minLength: number;\n readonly maxLength: number;\n readonly requireUppercase: boolean;\n readonly requireLowercase: boolean;\n readonly requireNumbers: boolean;\n readonly requireSpecialChars: boolean;\n /** Days before a password must be changed. `0` means it does not expire. */\n readonly expiryDays: number;\n };\n readonly session: {\n readonly durationMinutes: number;\n readonly maxConcurrentSessions?: number;\n };\n readonly lockout: {\n readonly maxLoginAttempts: number;\n readonly lockoutDurationMinutes: number;\n readonly progressive: boolean;\n readonly maxProgressiveLockoutHours: number;\n };\n}\n\nexport interface ApplicationAuthenticationDocumentationSource {\n readonly userTypes: readonly ApplicationUserTypeAuthenticationSource[];\n}\n\n/** Named presentations a page may request with `{{APPLICATION_AUTHENTICATION:<selector>}}`. */\nexport enum ApplicationAuthenticationPresentation {\n /** The sign-in methods this application enables, per user type. */\n ENABLED_SIGN_IN_METHODS = 'enabledSignInMethods',\n /** Whether a second factor is required, which ones count, and what enrolment hands out. */\n MULTIFACTOR_POLICY = 'multifactorPolicy',\n /** The password rules a person must satisfy. */\n PASSWORD_RULES = 'passwordRules',\n /** How long a session lasts and what a run of failed attempts costs. */\n SESSION_AND_LOCKOUT = 'sessionAndLockout',\n}\n\nexport interface ApplicationAuthenticationDocumentationFact {\n readonly sourceRef: string;\n readonly sourceVersion: number;\n readonly presentations: Readonly<Record<ApplicationAuthenticationPresentation, string>>;\n}\n\nfunction methodNames(methods: readonly AuthMethod[]): string {\n if (methods.length === 0) return 'None';\n return methods.map((method) => authenticationMethodConsumerWords(method).name).join(', ');\n}\n\nfunction userTypeCell(userType: ApplicationUserTypeAuthenticationSource): string {\n return `\\`${userType.userTypeKey}\\``;\n}\n\nfunction enabledSignInMethodsMarkdown(source: ApplicationAuthenticationDocumentationSource): string {\n const rows = source.userTypes\n .map((userType) => `| ${userTypeCell(userType)} | ${methodNames(userType.enabledMethods)} |`)\n .join('\\n');\n return `| Account kind | Sign-in methods this application enables |\\n| --- | --- |\\n${rows}\\n\\nAn organization can narrow this further for its own people — for example by requiring its single sign-on — so the sign-in screen remains the last word on what you can use right now.`;\n}\n\nfunction multifactorPolicyMarkdown(source: ApplicationAuthenticationDocumentationSource): string {\n const rows = source.userTypes.map((userType) => {\n const { multifactor } = userType;\n const required = multifactor.required\n ? multifactor.strongRequired ? 'Required, and a phishing-resistant method is required' : 'Required'\n : 'Optional';\n const grace = multifactor.required && multifactor.enrollmentGracePeriodDays > 0\n ? `${multifactor.enrollmentGracePeriodDays} day${multifactor.enrollmentGracePeriodDays === 1 ? '' : 's'} to enrol`\n : multifactor.required ? 'No grace period' : '—';\n const accepted = multifactor.acceptableMethods === undefined\n ? 'Any second factor enabled above'\n : methodNames(multifactor.acceptableMethods);\n const passkey = multifactor.passkeyExemptFromMultifactor ? 'Yes' : 'No';\n return `| ${userTypeCell(userType)} | ${required} | ${grace} | ${accepted} | ${passkey} |`;\n }).join('\\n');\n const codes = `Enrolling an authenticator app also hands you **${MFA_BACKUP_CODE_CONTRACT.codeCount} recovery codes** of ${MFA_BACKUP_CODE_CHARACTER_LENGTH} characters each. ${MFA_BACKUP_CODE_CONTRACT.singleUse ? 'Each code works once and is gone when used' : 'Codes may be reused'}; the application stores only a hash of them, so nobody can read them back to you. Enrolling again replaces the whole set.`;\n return `| Account kind | Second factor | Enrolment window | Accepted as a second factor | Passkey counts on its own |\\n| --- | --- | --- | --- | --- |\\n${rows}\\n\\n${codes}`;\n}\n\nfunction passwordRulesMarkdown(source: ApplicationAuthenticationDocumentationSource): string {\n const rows = source.userTypes.map((userType) => {\n const { password } = userType;\n const classes = [\n password.requireUppercase ? 'an uppercase letter' : null,\n password.requireLowercase ? 'a lowercase letter' : null,\n password.requireNumbers ? 'a digit' : null,\n password.requireSpecialChars ? 'a special character' : null,\n ].filter((requirement): requirement is string => requirement !== null);\n const composition = classes.length === 0 ? 'No character requirement' : `Must contain ${classes.join(', ')}`;\n const expiry = password.expiryDays > 0 ? `Every ${password.expiryDays} days` : 'Never expires';\n return `| ${userTypeCell(userType)} | ${password.minLength}–${password.maxLength} characters | ${composition} | ${expiry} |`;\n }).join('\\n');\n return `| Account kind | Length | Composition | Change required |\\n| --- | --- | --- | --- |\\n${rows}`;\n}\n\nfunction sessionAndLockoutMarkdown(source: ApplicationAuthenticationDocumentationSource): string {\n const rows = source.userTypes.map((userType) => {\n const { session, lockout } = userType;\n const concurrent = session.maxConcurrentSessions === undefined\n ? 'Not limited'\n : `${session.maxConcurrentSessions} at a time`;\n const progressive = lockout.progressive\n ? `, lengthening with each further run up to ${lockout.maxProgressiveLockoutHours} hours`\n : '';\n const lock = `${lockout.maxLoginAttempts} failed attempts lock the account for ${lockout.lockoutDurationMinutes} minutes${progressive}`;\n return `| ${userTypeCell(userType)} | ${session.durationMinutes} minutes | ${concurrent} | ${lock} |`;\n }).join('\\n');\n return `| Account kind | Session lasts | Concurrent sessions | Failed sign-ins |\\n| --- | --- | --- | --- |\\n${rows}\\n\\nA session ends when its time runs out, when you sign out, or when the application invalidates it — changing a password does exactly that, on every device.`;\n}\n\n/**\n * Projects the application's authentication policy into the presentations a page may request.\n * A pure function of the source, so two runs over the same application produce identical bytes.\n */\nexport function projectApplicationAuthenticationDocumentation(\n source: ApplicationAuthenticationDocumentationSource,\n): ApplicationAuthenticationDocumentationFact {\n if (source.userTypes.length === 0) {\n throw new Error('application authentication projection requires at least one declared user type');\n }\n for (const userType of source.userTypes) {\n if (userType.userTypeKey.trim().length === 0) {\n throw new Error('application authentication projection received a user type with no key');\n }\n }\n return {\n sourceRef: APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_REF,\n sourceVersion: APPLICATION_AUTHENTICATION_DOCUMENTATION_SOURCE_VERSION,\n presentations: Object.freeze({\n [ApplicationAuthenticationPresentation.ENABLED_SIGN_IN_METHODS]: enabledSignInMethodsMarkdown(source),\n [ApplicationAuthenticationPresentation.MULTIFACTOR_POLICY]: multifactorPolicyMarkdown(source),\n [ApplicationAuthenticationPresentation.PASSWORD_RULES]: passwordRulesMarkdown(source),\n [ApplicationAuthenticationPresentation.SESSION_AND_LOCKOUT]: sessionAndLockoutMarkdown(source),\n }),\n };\n}\n"]}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The application's own name and the addresses an integrator connects to.
3
+ *
4
+ * Engine documentation is written once for every generated application, so it
5
+ * used to hedge everything an application knows about itself: "the server origin
6
+ * from this application's API reference", "the address published for this
7
+ * environment", "the application". Every one of those values is available to the
8
+ * companion at derivation time, and this projection is how they reach the page.
9
+ *
10
+ * The split is deliberate. The APPLICATION supplies what only it knows — its
11
+ * customer-facing name and the base URLs it publishes. The ENGINE supplies the
12
+ * paths, because a route is the engine's own contract and an application that
13
+ * restated it would hold a second copy that can drift (see
14
+ * `MANUAL_CONTROLLER_ROUTES_URLS`, whose values are mounted under
15
+ * `MAIN_API_BASE_PATH`). An endpoint appears only when the companion has
16
+ * observed that the application configures that surface, so a page never
17
+ * advertises a door this application does not open.
18
+ */
19
+ export declare const APPLICATION_CONNECTION_DOCUMENTATION_SOURCE_REF: "source:companion-projection:application-connection";
20
+ export declare const APPLICATION_CONNECTION_DOCUMENTATION_SOURCE_VERSION = 1;
21
+ /** One base URL an application publishes for an environment a reader may call. */
22
+ export interface ApplicationConnectionServer {
23
+ readonly url: string;
24
+ readonly description?: string;
25
+ }
26
+ /**
27
+ * What the companion measures about this application. `surfaces` mirrors the
28
+ * evidence the derivation already computes for applicability gating: the same
29
+ * observation that decides whether the MCP pages are published decides whether
30
+ * the MCP endpoint is printed, so a published address and a published page can
31
+ * never disagree.
32
+ */
33
+ export interface ApplicationConnectionDocumentationSource {
34
+ /** The name customers see. The application's `displayName`, else its `appName`. */
35
+ readonly applicationName: string;
36
+ /** Base URLs the application publishes, in the order it authored them. Possibly empty. */
37
+ readonly apiServers: readonly ApplicationConnectionServer[];
38
+ readonly surfaces: {
39
+ readonly mcpToolServer: boolean;
40
+ readonly a2aAgent: boolean;
41
+ };
42
+ }
43
+ /** Named presentations a page may request with `{{APPLICATION_CONNECTION:<selector>}}`. */
44
+ export declare enum ApplicationConnectionPresentation {
45
+ /** Every connection detail as one table: base URLs, the API mount, agent endpoints. */
46
+ CONNECTION_DETAILS = "connectionDetails",
47
+ /** The base URLs alone, for a page that only needs to say where requests go. */
48
+ BASE_URLS = "baseUrls",
49
+ /**
50
+ * The MCP endpoint sentence. Never empty: an application that exposes no MCP surface gets a
51
+ * sentence saying so. Projection runs for every unit and gating happens afterwards, so a
52
+ * presentation that rendered nothing would leave a blank where a suppressed page's prose was,
53
+ * and would say nothing at all if the page were ever published without its gate.
54
+ */
55
+ MCP_ENDPOINT = "mcpEndpoint",
56
+ /** The A2A agent-card sentence. Never empty, for the same reason as the MCP endpoint. */
57
+ A2A_AGENT_CARD = "a2aAgentCard"
58
+ }
59
+ /**
60
+ * Values a page substitutes INSIDE a sample — a JSON client configuration, an HTTP request line —
61
+ * where a block presentation cannot go. Each is a single string, never markdown.
62
+ */
63
+ export declare enum ApplicationConnectionInlineValue {
64
+ /** The first base URL the application publishes, or an explicit stand-in when it publishes none. */
65
+ BASE_URL = "baseUrl",
66
+ /** The full MCP endpoint URL a client configures. */
67
+ MCP_ENDPOINT_URL = "mcpEndpointUrl",
68
+ /** The full agent-card URL an A2A peer fetches. */
69
+ AGENT_CARD_URL = "agentCardUrl",
70
+ /** The full A2A task endpoint URL. */
71
+ AGENT_TASK_URL = "agentTaskUrl"
72
+ }
73
+ export interface ApplicationConnectionDocumentationFact {
74
+ readonly sourceRef: string;
75
+ readonly sourceVersion: number;
76
+ /** Substituted wherever a page writes the inline application-name token. */
77
+ readonly applicationName: string;
78
+ readonly presentations: Readonly<Record<ApplicationConnectionPresentation, string>>;
79
+ readonly inlineValues: Readonly<Record<ApplicationConnectionInlineValue, string>>;
80
+ }
81
+ /**
82
+ * Projects the application's connection facts into the presentations a page may
83
+ * request. Every presentation is a pure function of the source, so two runs over
84
+ * the same application produce identical bytes.
85
+ */
86
+ export declare function projectApplicationConnectionDocumentation(source: ApplicationConnectionDocumentationSource): ApplicationConnectionDocumentationFact;
87
+ //# sourceMappingURL=application-connection-documentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application-connection-documentation.d.ts","sourceRoot":"","sources":["../../../../../src/companion/application-documentation/application-connection-documentation.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,+CAA+C,EAAG,oDAA6D,CAAC;AAC7H,eAAO,MAAM,mDAAmD,IAAI,CAAC;AAErE,kFAAkF;AAClF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,wCAAwC;IACvD,mFAAmF;IACnF,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,0FAA0F;IAC1F,QAAQ,CAAC,UAAU,EAAE,SAAS,2BAA2B,EAAE,CAAC;IAC5D,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;QAChC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH;AAED,2FAA2F;AAC3F,oBAAY,iCAAiC;IAC3C,uFAAuF;IACvF,kBAAkB,sBAAsB;IACxC,gFAAgF;IAChF,SAAS,aAAa;IACtB;;;;;OAKG;IACH,YAAY,gBAAgB;IAC5B,yFAAyF;IACzF,cAAc,iBAAiB;CAChC;AAED;;;GAGG;AACH,oBAAY,gCAAgC;IAC1C,oGAAoG;IACpG,QAAQ,YAAY;IACpB,qDAAqD;IACrD,gBAAgB,mBAAmB;IACnC,mDAAmD;IACnD,cAAc,iBAAiB;IAC/B,sCAAsC;IACtC,cAAc,iBAAiB;CAChC;AAED,MAAM,WAAW,sCAAsC;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,4EAA4E;IAC5E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,iCAAiC,EAAE,MAAM,CAAC,CAAC,CAAC;IACpF,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,gCAAgC,EAAE,MAAM,CAAC,CAAC,CAAC;CACnF;AA0DD;;;;GAIG;AACH,wBAAgB,yCAAyC,CACvD,MAAM,EAAE,wCAAwC,GAC/C,sCAAsC,CAwBxC"}
@@ -0,0 +1,138 @@
1
+ import { MAIN_API_BASE_PATH, MANUAL_CONTROLLER_ROUTES_URLS, ManualControllerRouteKey } from '@wildo-ai/saas-models';
2
+ /**
3
+ * The application's own name and the addresses an integrator connects to.
4
+ *
5
+ * Engine documentation is written once for every generated application, so it
6
+ * used to hedge everything an application knows about itself: "the server origin
7
+ * from this application's API reference", "the address published for this
8
+ * environment", "the application". Every one of those values is available to the
9
+ * companion at derivation time, and this projection is how they reach the page.
10
+ *
11
+ * The split is deliberate. The APPLICATION supplies what only it knows — its
12
+ * customer-facing name and the base URLs it publishes. The ENGINE supplies the
13
+ * paths, because a route is the engine's own contract and an application that
14
+ * restated it would hold a second copy that can drift (see
15
+ * `MANUAL_CONTROLLER_ROUTES_URLS`, whose values are mounted under
16
+ * `MAIN_API_BASE_PATH`). An endpoint appears only when the companion has
17
+ * observed that the application configures that surface, so a page never
18
+ * advertises a door this application does not open.
19
+ */
20
+ export const APPLICATION_CONNECTION_DOCUMENTATION_SOURCE_REF = 'source:companion-projection:application-connection';
21
+ export const APPLICATION_CONNECTION_DOCUMENTATION_SOURCE_VERSION = 1;
22
+ /** Named presentations a page may request with `{{APPLICATION_CONNECTION:<selector>}}`. */
23
+ export var ApplicationConnectionPresentation;
24
+ (function (ApplicationConnectionPresentation) {
25
+ /** Every connection detail as one table: base URLs, the API mount, agent endpoints. */
26
+ ApplicationConnectionPresentation["CONNECTION_DETAILS"] = "connectionDetails";
27
+ /** The base URLs alone, for a page that only needs to say where requests go. */
28
+ ApplicationConnectionPresentation["BASE_URLS"] = "baseUrls";
29
+ /**
30
+ * The MCP endpoint sentence. Never empty: an application that exposes no MCP surface gets a
31
+ * sentence saying so. Projection runs for every unit and gating happens afterwards, so a
32
+ * presentation that rendered nothing would leave a blank where a suppressed page's prose was,
33
+ * and would say nothing at all if the page were ever published without its gate.
34
+ */
35
+ ApplicationConnectionPresentation["MCP_ENDPOINT"] = "mcpEndpoint";
36
+ /** The A2A agent-card sentence. Never empty, for the same reason as the MCP endpoint. */
37
+ ApplicationConnectionPresentation["A2A_AGENT_CARD"] = "a2aAgentCard";
38
+ })(ApplicationConnectionPresentation || (ApplicationConnectionPresentation = {}));
39
+ /**
40
+ * Values a page substitutes INSIDE a sample — a JSON client configuration, an HTTP request line —
41
+ * where a block presentation cannot go. Each is a single string, never markdown.
42
+ */
43
+ export var ApplicationConnectionInlineValue;
44
+ (function (ApplicationConnectionInlineValue) {
45
+ /** The first base URL the application publishes, or an explicit stand-in when it publishes none. */
46
+ ApplicationConnectionInlineValue["BASE_URL"] = "baseUrl";
47
+ /** The full MCP endpoint URL a client configures. */
48
+ ApplicationConnectionInlineValue["MCP_ENDPOINT_URL"] = "mcpEndpointUrl";
49
+ /** The full agent-card URL an A2A peer fetches. */
50
+ ApplicationConnectionInlineValue["AGENT_CARD_URL"] = "agentCardUrl";
51
+ /** The full A2A task endpoint URL. */
52
+ ApplicationConnectionInlineValue["AGENT_TASK_URL"] = "agentTaskUrl";
53
+ })(ApplicationConnectionInlineValue || (ApplicationConnectionInlineValue = {}));
54
+ /** A manual route as a reader must call it: the mount plus the engine-owned path segment. */
55
+ function publicRoutePath(routeKey) {
56
+ return `${MAIN_API_BASE_PATH}${MANUAL_CONTROLLER_ROUTES_URLS[routeKey]}`;
57
+ }
58
+ function baseUrlsMarkdown(source) {
59
+ if (source.apiServers.length === 0) {
60
+ // Honest rather than invented: an application that publishes no base URL cannot have one
61
+ // printed for it, and the reader is told where the answer actually lives.
62
+ return `${source.applicationName} does not publish its base URLs in this documentation. Read them from the **Servers** list at the top of the API reference, or ask whoever operates the environment you are calling.`;
63
+ }
64
+ const rows = source.apiServers
65
+ .map((server) => `| \`${server.url}\` | ${server.description ?? 'Published base URL'} |`)
66
+ .join('\n');
67
+ return `| Base URL | Environment |\n| --- | --- |\n${rows}`;
68
+ }
69
+ function mcpEndpointMarkdown(source) {
70
+ if (!source.surfaces.mcpToolServer)
71
+ return `${source.applicationName} does not publish a Model Context Protocol endpoint.`;
72
+ return `${source.applicationName} serves its Model Context Protocol endpoint at \`${publicRoutePath(ManualControllerRouteKey.MCP_ENDPOINT)}\`, under the base URL of the environment you are connecting to.`;
73
+ }
74
+ function a2aAgentCardMarkdown(source) {
75
+ if (!source.surfaces.a2aAgent)
76
+ return `${source.applicationName} does not publish an agent card.`;
77
+ return `${source.applicationName} publishes its agent card at \`${publicRoutePath(ManualControllerRouteKey.A2A_AGENT_CARD)}\` and accepts task calls at \`${publicRoutePath(ManualControllerRouteKey.A2A_TASKS)}\`, under the base URL of the environment you are connecting to.`;
78
+ }
79
+ function connectionDetailsMarkdown(source) {
80
+ const rows = [];
81
+ for (const server of source.apiServers) {
82
+ rows.push(`| Base URL${server.description === undefined ? '' : ` (${server.description})`} | \`${server.url}\` |`);
83
+ }
84
+ rows.push(`| API mount | \`${MAIN_API_BASE_PATH}\` — every path in the API reference already includes it |`);
85
+ if (source.surfaces.mcpToolServer) {
86
+ rows.push(`| MCP endpoint | \`${publicRoutePath(ManualControllerRouteKey.MCP_ENDPOINT)}\` |`);
87
+ }
88
+ if (source.surfaces.a2aAgent) {
89
+ rows.push(`| A2A agent card | \`${publicRoutePath(ManualControllerRouteKey.A2A_AGENT_CARD)}\` |`);
90
+ rows.push(`| A2A task endpoint | \`${publicRoutePath(ManualControllerRouteKey.A2A_TASKS)}\` |`);
91
+ }
92
+ const table = `| What | Value |\n| --- | --- |\n${rows.join('\n')}`;
93
+ return source.apiServers.length > 0
94
+ ? table
95
+ : `${table}\n\nThis application publishes no base URL here; read it from the **Servers** list at the top of the API reference.`;
96
+ }
97
+ /**
98
+ * The origin a sample should show. An application that publishes no base URL gets an explicit
99
+ * stand-in rather than a fabricated host: the reader must substitute their own environment, and the
100
+ * sample says so in the place where the value belongs.
101
+ */
102
+ function primaryBaseUrl(source) {
103
+ const first = source.apiServers[0];
104
+ return first === undefined ? 'https://your-environment.example' : first.url.replace(/\/+$/, '');
105
+ }
106
+ /**
107
+ * Projects the application's connection facts into the presentations a page may
108
+ * request. Every presentation is a pure function of the source, so two runs over
109
+ * the same application produce identical bytes.
110
+ */
111
+ export function projectApplicationConnectionDocumentation(source) {
112
+ const applicationName = source.applicationName.trim();
113
+ if (applicationName.length === 0)
114
+ throw new Error('application connection projection requires a customer-facing application name');
115
+ for (const server of source.apiServers) {
116
+ if (server.url.trim().length === 0)
117
+ throw new Error('application connection projection received an empty base URL');
118
+ }
119
+ const named = { ...source, applicationName };
120
+ return {
121
+ sourceRef: APPLICATION_CONNECTION_DOCUMENTATION_SOURCE_REF,
122
+ sourceVersion: APPLICATION_CONNECTION_DOCUMENTATION_SOURCE_VERSION,
123
+ applicationName,
124
+ presentations: Object.freeze({
125
+ [ApplicationConnectionPresentation.CONNECTION_DETAILS]: connectionDetailsMarkdown(named),
126
+ [ApplicationConnectionPresentation.BASE_URLS]: baseUrlsMarkdown(named),
127
+ [ApplicationConnectionPresentation.MCP_ENDPOINT]: mcpEndpointMarkdown(named),
128
+ [ApplicationConnectionPresentation.A2A_AGENT_CARD]: a2aAgentCardMarkdown(named),
129
+ }),
130
+ inlineValues: Object.freeze({
131
+ [ApplicationConnectionInlineValue.BASE_URL]: primaryBaseUrl(named),
132
+ [ApplicationConnectionInlineValue.MCP_ENDPOINT_URL]: `${primaryBaseUrl(named)}${publicRoutePath(ManualControllerRouteKey.MCP_ENDPOINT)}`,
133
+ [ApplicationConnectionInlineValue.AGENT_CARD_URL]: `${primaryBaseUrl(named)}${publicRoutePath(ManualControllerRouteKey.A2A_AGENT_CARD)}`,
134
+ [ApplicationConnectionInlineValue.AGENT_TASK_URL]: `${primaryBaseUrl(named)}${publicRoutePath(ManualControllerRouteKey.A2A_TASKS)}`,
135
+ }),
136
+ };
137
+ }
138
+ //# sourceMappingURL=application-connection-documentation.js.map