@owlmeans/client-panel 0.1.18-rc.58 → 0.1.18-rc.59

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 (32) hide show
  1. package/README.md +2 -2
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/client-panel/SKILL.md +30 -4
  4. package/build/auth/login/index.d.ts +2 -0
  5. package/build/auth/login/index.d.ts.map +1 -1
  6. package/build/auth/login/index.js +2 -0
  7. package/build/auth/login/index.js.map +1 -1
  8. package/build/auth/login/model.d.ts +2 -2
  9. package/build/auth/login/model.d.ts.map +1 -1
  10. package/build/auth/login/model.js +5 -2
  11. package/build/auth/login/model.js.map +1 -1
  12. package/build/auth/login/provider/consts.local.d.ts +3 -0
  13. package/build/auth/login/provider/consts.local.d.ts.map +1 -0
  14. package/build/auth/login/provider/consts.local.js +3 -0
  15. package/build/auth/login/provider/consts.local.js.map +1 -0
  16. package/build/auth/login/provider-config.d.ts +37 -0
  17. package/build/auth/login/provider-config.d.ts.map +1 -0
  18. package/build/auth/login/provider-config.js +2 -0
  19. package/build/auth/login/provider-config.js.map +1 -0
  20. package/build/auth/login/provider.d.ts +5 -0
  21. package/build/auth/login/provider.d.ts.map +1 -0
  22. package/build/auth/login/provider.js +55 -0
  23. package/build/auth/login/provider.js.map +1 -0
  24. package/build/auth/login/types.d.ts +48 -1
  25. package/build/auth/login/types.d.ts.map +1 -1
  26. package/package.json +7 -7
  27. package/src/auth/login/index.ts +2 -0
  28. package/src/auth/login/model.ts +5 -2
  29. package/src/auth/login/provider/consts.local.ts +2 -0
  30. package/src/auth/login/provider-config.ts +38 -0
  31. package/src/auth/login/provider.ts +65 -0
  32. package/src/auth/login/types.ts +52 -1
package/README.md CHANGED
@@ -16,7 +16,7 @@ headless navigation model.
16
16
  ## Installation
17
17
 
18
18
  ```bash
19
- bun add @owlmeans/client-panel@^0.1.18-rc.58
19
+ bun add @owlmeans/client-panel@^0.1.18-rc.59
20
20
  ```
21
21
 
22
22
  ## Usage
@@ -153,7 +153,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
153
153
  your project's skill store (`.agents/skills/`):
154
154
 
155
155
  ```sh
156
- npx @owlmeans/agent-skills@^0.1.18-rc.49
156
+ npx @owlmeans/agent-skills@^0.1.18-rc.51
157
157
  ```
158
158
 
159
159
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/client-panel",
4
- "version": "0.1.18-rc.58",
5
- "generatedAt": "2026-10-05T21:03:06.410Z",
4
+ "version": "0.1.18-rc.59",
5
+ "generatedAt": "2026-10-06T11:45:57.680Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/client-panel
9
9
 
10
10
  **Layer:** Client
11
- **Install:** `"@owlmeans/client-panel": "^0.1.18-rc.58"` in `dependencies`
11
+ **Install:** `"@owlmeans/client-panel": "^0.1.18-rc.59"` in `dependencies`
12
12
 
13
13
  ## Key Exports
14
14
 
@@ -40,9 +40,11 @@ controller; apps may override it through their own `buttons` resource.
40
40
 
41
41
  ## Subpath Exports
42
42
 
43
- - `./auth` — `useLoginMethods` and the login model types (`LoginMethodsModel`, `LoginTermsModel`,
44
- `LoginCreditModel`, `UseLoginMethodsOptions`). Importing it also registers this package's own
45
- seven-language `client-panel-auth` bundle by side effect.
43
+ - `./auth` — `useLoginMethods`, `loginProviderHelper` and the login model types
44
+ (`LoginMethodsModel`, `LoginTermsModel`, `LoginCreditModel`, `LoginProviderConfig`,
45
+ `LoginProviderModel`, `LoginProviderPlacement`, `UseLoginMethodsOptions`). Importing it also
46
+ registers this package's own seven-language `client-panel-auth` bundle and the
47
+ `LoginScreenConfig.provider` augmentation of `@owlmeans/config` by side effect.
46
48
  - `./auth/plugins` — the shapes an auth UI plugin is written against: `Ed22519BasicAuthUIPluginForm`
47
49
  and its `Ed22519BasicAuthUIPluginFormSchema`.
48
50
 
@@ -63,6 +65,30 @@ bound `LoginStep` confirms the terms instead of this screen. `LoginMethodsModel.
63
65
  (`required && !deferred && !accepted`) — a renderer never blocks on `required`/`accepted` alone, or
64
66
  it would re-block a screen whose confirmation moved elsewhere.
65
67
 
68
+ ### The provider disclosure — `LoginMethodsModel.provider`
69
+
70
+ A sign-in screen that a third party runs for an application (a generated target signing people in
71
+ through the platform's IAM) names both parties: who signs the person in, and on behalf of which
72
+ product. The rules live here, headless, so every renderer discloses the same thing:
73
+
74
+ - **Config** — `cfg.security.auth.login.provider?: LoginProviderConfig`
75
+ (`{ name, operator?, info?, placement?: 'top' | 'inline' }`), declared onto `@owlmeans/config`'s
76
+ `LoginScreenConfig` by module augmentation in `auth/login/provider-config.ts` (the
77
+ `client-auth/login/terms-config.ts` pattern), never by editing `@owlmeans/config`. The `./auth`
78
+ barrel re-exports it, so any import of `@owlmeans/client-panel/auth` loads the field; a program
79
+ that builds the literal without one adds `import type {} from '@owlmeans/client-panel/auth'`.
80
+ - **Model** — `loginProviderHelper.resolve(cfg, brand)` → `LoginProviderModel { name, operator,
81
+ product, info: string | null, placement }` or `null`. Null unless the provider `name` AND the
82
+ product (`cfg.brand.name`) are set — a relationship with one party missing is never shown. Blank
83
+ strings are unset (build-time env arrives as `''`); `operator` defaults to `name`; `placement` to
84
+ `'inline'` (anything but `'top'` is inline); `info` survives only as an absolute `http(s)` URL or
85
+ a root-relative path (`//host` and `/\` are refused). `useLoginMethods().provider` carries it.
86
+ - **Copy** — `loginProviderHelper.fill(template, model)` puts `{{provider}}`, `{{operator}}` and
87
+ `{{product}}` into an ALREADY-TRANSLATED sentence in one pass; names never travel as i18next
88
+ params. The keys (`login.provider.*`) and the markup belong to `web-panel`.
89
+ - The disclosure is independent of the credit line: `credit.poweredBy: false` never hides who
90
+ signs a person in.
91
+
66
92
  ## Usage
67
93
 
68
94
  ```typescript
@@ -1,3 +1,5 @@
1
1
  export type * from './types.js';
2
2
  export * from './model.js';
3
+ export * from './provider.js';
4
+ export * from './provider-config.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA"}
@@ -1,2 +1,4 @@
1
1
  export * from './model.js';
2
+ export * from './provider.js';
3
+ export * from './provider-config.js';
2
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/login/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA"}
@@ -3,8 +3,8 @@ import type { LoginMethodsModel, UseLoginMethodsOptions } from './types.js';
3
3
  * Everything a sign-in screen needs, with no opinion about how it looks.
4
4
  *
5
5
  * Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
6
- * be confirmed first and what the credit line says are the same on every platform, and only the
7
- * rendering differs. A renderer that re-derives any of them will drift from the one that does not.
6
+ * be confirmed first, what the credit line says and who is disclosed as signing the person in are
7
+ * the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
8
8
  */
9
9
  export declare const useLoginMethods: (opts?: UseLoginMethodsOptions) => LoginMethodsModel;
10
10
  //# sourceMappingURL=model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAE3E;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,UAAW,sBAAsB,KAAG,iBAoF/D,CAAA"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAG3E;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,UAAW,sBAAsB,KAAG,iBAsF/D,CAAA"}
@@ -1,12 +1,13 @@
1
1
  import { useCallback, useMemo, useState } from 'react';
2
2
  import { useContext, useNavigate } from '@owlmeans/client';
3
3
  import { resolveCredit, LoginOutcome, LOGIN_SERVICE, loginMethodsHelper, loginTermsHelper } from '@owlmeans/client-auth/login';
4
+ import { loginProviderHelper } from './provider.js';
4
5
  /**
5
6
  * Everything a sign-in screen needs, with no opinion about how it looks.
6
7
  *
7
8
  * Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
8
- * be confirmed first and what the credit line says are the same on every platform, and only the
9
- * rendering differs. A renderer that re-derives any of them will drift from the one that does not.
9
+ * be confirmed first, what the credit line says and who is disclosed as signing the person in are
10
+ * the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
10
11
  */
11
12
  export const useLoginMethods = (opts) => {
12
13
  const context = useContext();
@@ -16,6 +17,7 @@ export const useLoginMethods = (opts) => {
16
17
  const brand = context.cfg.brand;
17
18
  const resolved = useMemo(() => loginTermsHelper.resolveTerms(opts?.terms ?? cfg?.terms), [opts?.terms, cfg]);
18
19
  const credit = useMemo(() => resolveCredit(cfg?.credit, brand, context.cfg.service), [cfg, brand, context]);
20
+ const provider = useMemo(() => loginProviderHelper.resolve(cfg?.provider, brand), [cfg, brand]);
19
21
  const [accepted, setAccepted] = useState(() => loginTermsHelper.termsAccepted(resolved));
20
22
  const [attempted, setAttempted] = useState(false);
21
23
  const [busy, setBusy] = useState(null);
@@ -75,6 +77,7 @@ export const useLoginMethods = (opts) => {
75
77
  accept,
76
78
  },
77
79
  credit,
80
+ provider,
78
81
  blocked,
79
82
  busy,
80
83
  outcome,
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/auth/login/model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ /** The names a provider sentence may carry, matched in one pass. */
2
+ export declare const PROVIDER_PLACEHOLDER: RegExp;
3
+ //# sourceMappingURL=consts.local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../../../../src/auth/login/provider/consts.local.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,eAAO,MAAM,oBAAoB,QAAyC,CAAA"}
@@ -0,0 +1,3 @@
1
+ /** The names a provider sentence may carry, matched in one pass. */
2
+ export const PROVIDER_PLACEHOLDER = /\{\{(provider|operator|product)\}\}/g;
3
+ //# sourceMappingURL=consts.local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../../../../src/auth/login/provider/consts.local.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAG,sCAAsC,CAAA"}
@@ -0,0 +1,37 @@
1
+ import type { LoginProviderConfig } from './types.js';
2
+ /**
3
+ * `LoginScreenConfig.provider`, added by TypeScript module augmentation rather than by editing
4
+ * `@owlmeans/config` itself.
5
+ *
6
+ * `@owlmeans/config` has roughly 85 dependents, and every one of them sits in the same release
7
+ * closure — a config bump ripples through every package that pins a range against it, whether or
8
+ * not it touches sign-in. The field below is read by exactly one consumer family (the sign-in
9
+ * screen's headless model here and its renderers), so it is declared here instead, merged onto the
10
+ * base interface by TypeScript's own declaration merging — the pattern
11
+ * `@owlmeans/client-auth/login`'s `terms-config.ts` set.
12
+ *
13
+ * This file must be part of whatever a consumer's own compilation actually loads, or the merge
14
+ * never happens — an ambient augmentation applies only once the compiler has read the file that
15
+ * declares it. `@owlmeans/client-panel/auth`'s barrel re-exports this module for exactly that
16
+ * reason, so ANY import from `@owlmeans/client-panel/auth` pulls this augmentation into that
17
+ * program. An app that builds its `security.auth.login` literal in a file that imports nothing from
18
+ * it (a config file importing only `@owlmeans/config`) still benefits as long as some other file in
19
+ * the same `tsc` program does; if none does, add `import type {} from '@owlmeans/client-panel/auth'`
20
+ * to the file building the literal — an import with an empty type list, kept only to pull this
21
+ * file into that program's graph.
22
+ *
23
+ * `export {}` below is required, not decorative: without at least one top-level import/export this
24
+ * file is a SCRIPT to TypeScript, not a MODULE, and `declare module '@owlmeans/config'` inside a
25
+ * script augments the global scope instead of merging onto the named module's exports.
26
+ */
27
+ declare module '@owlmeans/config' {
28
+ interface LoginScreenConfig {
29
+ /**
30
+ * Who signs a person in on this application's behalf — an identity service the application
31
+ * does not itself run. Absent, the screen discloses nothing and renders exactly as before.
32
+ */
33
+ provider?: LoginProviderConfig;
34
+ }
35
+ }
36
+ export {};
37
+ //# sourceMappingURL=provider-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../../src/auth/login/provider-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,QAAQ,kBAAkB,CAAC;IAChC,UAAU,iBAAiB;QACzB;;;WAGG;QACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;KAC/B;CACF;AAED,OAAO,EAAG,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.js","sourceRoot":"","sources":["../../../src/auth/login/provider-config.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import type { LoginProviderHelper } from './types.js';
2
+ import './provider-config.js';
3
+ export declare const createLoginProviderHelper: () => LoginProviderHelper;
4
+ export declare const loginProviderHelper: LoginProviderHelper;
5
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/auth/login/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAuB,mBAAmB,EAAsB,MAAM,YAAY,CAAA;AAE9F,OAAO,sBAAsB,CAAA;AAE7B,eAAO,MAAM,yBAAyB,QAAO,mBAyD5C,CAAA;AAED,eAAO,MAAM,mBAAmB,qBAA8B,CAAA"}
@@ -0,0 +1,55 @@
1
+ import { PROVIDER_PLACEHOLDER } from './provider/consts.local.js';
2
+ import './provider-config.js';
3
+ export const createLoginProviderHelper = () => {
4
+ /**
5
+ * A configured value that was actually SET — `''` is how a build-time default spells "unset"
6
+ * (see `resolveCredit` in `@owlmeans/client-auth/login`, which follows the same rule).
7
+ */
8
+ const set = (value) => typeof value === 'string' && value.trim() !== '' ? value.trim() : null;
9
+ /**
10
+ * A link a person may be sent to: an absolute http(s) URL, or a path on this origin.
11
+ *
12
+ * `//host` is protocol-relative — another origin wearing a path's clothes — and `/\` is read as
13
+ * the same by browsers, so both are refused along with every other scheme (`javascript:` above
14
+ * all, since this lands in an `href`).
15
+ */
16
+ const link = (value) => {
17
+ const href = set(value);
18
+ if (href == null) {
19
+ return null;
20
+ }
21
+ if (href.startsWith('/')) {
22
+ return /^\/[/\\]/.test(href) ? null : href;
23
+ }
24
+ try {
25
+ const url = new URL(href);
26
+ return url.protocol === 'https:' || url.protocol === 'http:' ? href : null;
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ };
32
+ const resolve = (cfg, brand) => {
33
+ const name = set(cfg?.name);
34
+ const product = set(brand?.name);
35
+ if (name == null || product == null) {
36
+ return null;
37
+ }
38
+ return {
39
+ name,
40
+ operator: set(cfg?.operator) ?? name,
41
+ product,
42
+ info: link(cfg?.info),
43
+ placement: cfg?.placement === 'top' ? 'top' : 'inline',
44
+ };
45
+ };
46
+ const fill = (template, model) => {
47
+ const values = {
48
+ provider: model.name, operator: model.operator, product: model.product,
49
+ };
50
+ return template.replace(PROVIDER_PLACEHOLDER, (match, key) => values[key] ?? match);
51
+ };
52
+ return { resolve, fill };
53
+ };
54
+ export const loginProviderHelper = createLoginProviderHelper();
55
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/auth/login/provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AACjE,OAAO,sBAAsB,CAAA;AAE7B,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAwB,EAAE;IACjE;;;OAGG;IACH,MAAM,GAAG,GAAG,CAAC,KAAqB,EAAiB,EAAE,CACnD,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAExE;;;;;;OAMG;IACH,MAAM,IAAI,GAAG,CAAC,KAAqB,EAAiB,EAAE;QACpD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;QACvB,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5C,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;YAEzB,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC5E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC,CAAA;IAED,MAAM,OAAO,GAAG,CAAC,GAAyB,EAAE,KAAqB,EAA6B,EAAE;QAC9F,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC3B,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;YACpC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO;YACL,IAAI;YACJ,QAAQ,EAAE,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,IAAI;YACpC,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;YACrB,SAAS,EAAE,GAAG,EAAE,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;SACvD,CAAA;IACH,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,CAAC,QAAgB,EAAE,KAAyB,EAAU,EAAE;QACnE,MAAM,MAAM,GAA2B;YACrC,QAAQ,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO;SACvE,CAAA;QAED,OAAO,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAA;IAC7F,CAAC,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AAC1B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,yBAAyB,EAAE,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import type { LoginMethod, LoginOutcome, ResolvedTermsDocument } from '@owlmeans/client-auth/login';
2
- import type { LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config';
2
+ import type { BrandSettings, LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config';
3
3
  export interface UseLoginMethodsOptions {
4
4
  /** Overrides the configuration the context carries. */
5
5
  config?: LoginScreenConfig;
@@ -33,12 +33,59 @@ export interface LoginCreditModel {
33
33
  poweredBy: boolean;
34
34
  line: string | null;
35
35
  }
36
+ /** Where the provider disclosure sits: a strip above the card, or a line inside it. */
37
+ export type LoginProviderPlacement = 'top' | 'inline';
38
+ /**
39
+ * Who actually signs a person in, when that is not the application on whose screen they are.
40
+ *
41
+ * Read from `cfg.security.auth.login.provider` (declared onto `LoginScreenConfig` by
42
+ * `./provider-config.ts`). Blank strings are unset, exactly as they are for the credit line: this
43
+ * config is filled from build-time environment variables, and an undelivered one arrives as `''`.
44
+ */
45
+ export interface LoginProviderConfig {
46
+ /** The sign-in service as a person reads it — "OwlMeans IAM". */
47
+ name: string;
48
+ /** Who runs it. Defaults to `name`. */
49
+ operator?: string;
50
+ /** "More information" — an absolute http(s) URL or a root-relative path; anything else is dropped. */
51
+ info?: string;
52
+ /** Default `'inline'`. */
53
+ placement?: LoginProviderPlacement;
54
+ }
55
+ export interface LoginProviderModel {
56
+ name: string;
57
+ operator: string;
58
+ /** The application the person is signing in to — `cfg.brand.name`. */
59
+ product: string;
60
+ info: string | null;
61
+ placement: LoginProviderPlacement;
62
+ }
63
+ export interface LoginProviderHelper {
64
+ /**
65
+ * The disclosure a sign-in screen renders, or `null` when there is nothing to disclose.
66
+ *
67
+ * Null unless both the provider's `name` and the product (`brand.name`) are set — a sentence
68
+ * relating two parties cannot be written with one of them missing, and a screen that showed half
69
+ * of it would be worse than one that shows none.
70
+ */
71
+ resolve: (cfg?: LoginProviderConfig, brand?: BrandSettings) => LoginProviderModel | null;
72
+ /**
73
+ * Put the names into a sentence AFTER it was translated.
74
+ *
75
+ * `{{provider}}`, `{{operator}}` and `{{product}}`, replaced in one pass — a name that itself
76
+ * contains a placeholder is never expanded a second time. The names never travel as i18next
77
+ * interpolation parameters, so no translation backend gets the chance to escape or format them.
78
+ */
79
+ fill: (template: string, model: LoginProviderModel) => string;
80
+ }
36
81
  export interface LoginMethodsModel {
37
82
  methods: LoginMethod[];
38
83
  /** The one a screen highlights and focuses. It is never started automatically. */
39
84
  primary: LoginMethod | null;
40
85
  terms: LoginTermsModel;
41
86
  credit: LoginCreditModel;
87
+ /** Who signs the person in, on behalf of which product — `null` when the app configured nobody. */
88
+ provider: LoginProviderModel | null;
42
89
  /** True while the terms have not been confirmed and confirming them is required. */
43
90
  blocked: boolean;
44
91
  /** Id of the method currently starting. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnG,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE3E,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,KAAK,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAA;IAChC,qDAAqD;IACrD,OAAO,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC,CAAA;CACtE;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAA;IAClB,8FAA8F;IAC9F,SAAS,EAAE,qBAAqB,EAAE,CAAA;IAClC,6FAA6F;IAC7F,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,kFAAkF;IAClF,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,eAAe,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;IACxB,oFAAoF;IACpF,OAAO,EAAE,OAAO,CAAA;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;CACtC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/auth/login/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnG,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE1F,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,KAAK,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAA;IAChC,qDAAqD;IACrD,OAAO,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC,CAAA;CACtE;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAA;IAClB,8FAA8F;IAC9F,SAAS,EAAE,qBAAqB,EAAE,CAAA;IAClC,6FAA6F;IAC7F,OAAO,EAAE,qBAAqB,EAAE,CAAA;IAChC,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,uFAAuF;AACvF,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,QAAQ,CAAA;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sGAAsG;IACtG,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0BAA0B;IAC1B,SAAS,CAAC,EAAE,sBAAsB,CAAA;CACnC;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,SAAS,EAAE,sBAAsB,CAAA;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;;OAMG;IACH,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,mBAAmB,EAAE,KAAK,CAAC,EAAE,aAAa,KAAK,kBAAkB,GAAG,IAAI,CAAA;IACxF;;;;;;OAMG;IACH,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,MAAM,CAAA;CAC9D;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,kFAAkF;IAClF,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,eAAe,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;IACxB,mGAAmG;IACnG,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAA;IACnC,oFAAoF;IACpF,OAAO,EAAE,OAAO,CAAA;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;CACtC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/client-panel",
3
- "version": "0.1.18-rc.58",
3
+ "version": "0.1.18-rc.59",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -36,16 +36,16 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@hookform/resolvers": "^5.9.1",
39
- "@owlmeans/client": "^0.1.18-rc.51",
40
- "@owlmeans/client-i18n": "^0.1.18-rc.50",
41
- "@owlmeans/client-entrypoint": "^0.1.18-rc.47",
42
- "@owlmeans/client-route": "^0.1.18-rc.48",
39
+ "@owlmeans/client": "^0.1.18-rc.52",
40
+ "@owlmeans/client-i18n": "^0.1.18-rc.51",
41
+ "@owlmeans/client-entrypoint": "^0.1.18-rc.48",
42
+ "@owlmeans/client-route": "^0.1.18-rc.49",
43
43
  "@owlmeans/error": "^0.1.18-rc.38",
44
44
  "@owlmeans/entrypoint": "^0.1.18-rc.41"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@owlmeans/auth": "^0.1.18-rc.40",
48
- "@owlmeans/client-auth": "^0.1.18-rc.58",
48
+ "@owlmeans/client-auth": "^0.1.18-rc.59",
49
49
  "@owlmeans/config": "^0.1.18-rc.44",
50
50
  "@owlmeans/i18n": "^0.1.18-rc.39",
51
51
  "@owlmeans/router": "^0.1.18-rc.33",
@@ -64,7 +64,7 @@
64
64
  "devDependencies": {
65
65
  "@owlmeans/dep-config": "workspace:*",
66
66
  "@owlmeans/auth": "^0.1.18-rc.40",
67
- "@owlmeans/client-auth": "^0.1.18-rc.58",
67
+ "@owlmeans/client-auth": "^0.1.18-rc.59",
68
68
  "@types/react": "^19.3.0",
69
69
  "nodemon": "^3.1.14",
70
70
  "react-hook-form": "^7.89.0",
@@ -1,2 +1,4 @@
1
1
  export type * from './types.js'
2
2
  export * from './model.js'
3
+ export * from './provider.js'
4
+ export * from './provider-config.js'
@@ -4,13 +4,14 @@ import type { CommonConfig } from '@owlmeans/config'
4
4
  import { resolveCredit, LoginOutcome, LOGIN_SERVICE, loginMethodsHelper, loginTermsHelper } from '@owlmeans/client-auth/login'
5
5
  import type { LoginContext, LoginMethod, LoginService } from '@owlmeans/client-auth/login'
6
6
  import type { LoginMethodsModel, UseLoginMethodsOptions } from './types.js'
7
+ import { loginProviderHelper } from './provider.js'
7
8
 
8
9
  /**
9
10
  * Everything a sign-in screen needs, with no opinion about how it looks.
10
11
  *
11
12
  * Headless and cross-platform, like `usePanelNav`: the rules about what may be offered, what must
12
- * be confirmed first and what the credit line says are the same on every platform, and only the
13
- * rendering differs. A renderer that re-derives any of them will drift from the one that does not.
13
+ * be confirmed first, what the credit line says and who is disclosed as signing the person in are
14
+ * the same on every platform, and only the rendering differs. A renderer that re-derives any of them will drift from the one that does not.
14
15
  */
15
16
  export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsModel => {
16
17
  const context = useContext() as unknown as LoginContext
@@ -24,6 +25,7 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
24
25
  const credit = useMemo(
25
26
  () => resolveCredit(cfg?.credit, brand, context.cfg.service), [cfg, brand, context]
26
27
  )
28
+ const provider = useMemo(() => loginProviderHelper.resolve(cfg?.provider, brand), [cfg, brand])
27
29
 
28
30
  const [accepted, setAccepted] = useState(() => loginTermsHelper.termsAccepted(resolved))
29
31
  const [attempted, setAttempted] = useState(false)
@@ -90,6 +92,7 @@ export const useLoginMethods = (opts?: UseLoginMethodsOptions): LoginMethodsMode
90
92
  accept,
91
93
  },
92
94
  credit,
95
+ provider,
93
96
  blocked,
94
97
  busy,
95
98
  outcome,
@@ -0,0 +1,2 @@
1
+ /** The names a provider sentence may carry, matched in one pass. */
2
+ export const PROVIDER_PLACEHOLDER = /\{\{(provider|operator|product)\}\}/g
@@ -0,0 +1,38 @@
1
+ import type { LoginProviderConfig } from './types.js'
2
+
3
+ /**
4
+ * `LoginScreenConfig.provider`, added by TypeScript module augmentation rather than by editing
5
+ * `@owlmeans/config` itself.
6
+ *
7
+ * `@owlmeans/config` has roughly 85 dependents, and every one of them sits in the same release
8
+ * closure — a config bump ripples through every package that pins a range against it, whether or
9
+ * not it touches sign-in. The field below is read by exactly one consumer family (the sign-in
10
+ * screen's headless model here and its renderers), so it is declared here instead, merged onto the
11
+ * base interface by TypeScript's own declaration merging — the pattern
12
+ * `@owlmeans/client-auth/login`'s `terms-config.ts` set.
13
+ *
14
+ * This file must be part of whatever a consumer's own compilation actually loads, or the merge
15
+ * never happens — an ambient augmentation applies only once the compiler has read the file that
16
+ * declares it. `@owlmeans/client-panel/auth`'s barrel re-exports this module for exactly that
17
+ * reason, so ANY import from `@owlmeans/client-panel/auth` pulls this augmentation into that
18
+ * program. An app that builds its `security.auth.login` literal in a file that imports nothing from
19
+ * it (a config file importing only `@owlmeans/config`) still benefits as long as some other file in
20
+ * the same `tsc` program does; if none does, add `import type {} from '@owlmeans/client-panel/auth'`
21
+ * to the file building the literal — an import with an empty type list, kept only to pull this
22
+ * file into that program's graph.
23
+ *
24
+ * `export {}` below is required, not decorative: without at least one top-level import/export this
25
+ * file is a SCRIPT to TypeScript, not a MODULE, and `declare module '@owlmeans/config'` inside a
26
+ * script augments the global scope instead of merging onto the named module's exports.
27
+ */
28
+ declare module '@owlmeans/config' {
29
+ interface LoginScreenConfig {
30
+ /**
31
+ * Who signs a person in on this application's behalf — an identity service the application
32
+ * does not itself run. Absent, the screen discloses nothing and renders exactly as before.
33
+ */
34
+ provider?: LoginProviderConfig
35
+ }
36
+ }
37
+
38
+ export { }
@@ -0,0 +1,65 @@
1
+ import type { BrandSettings } from '@owlmeans/config'
2
+ import type { LoginProviderConfig, LoginProviderHelper, LoginProviderModel } from './types.js'
3
+ import { PROVIDER_PLACEHOLDER } from './provider/consts.local.js'
4
+ import './provider-config.js'
5
+
6
+ export const createLoginProviderHelper = (): LoginProviderHelper => {
7
+ /**
8
+ * A configured value that was actually SET — `''` is how a build-time default spells "unset"
9
+ * (see `resolveCredit` in `@owlmeans/client-auth/login`, which follows the same rule).
10
+ */
11
+ const set = (value?: string | null): string | null =>
12
+ typeof value === 'string' && value.trim() !== '' ? value.trim() : null
13
+
14
+ /**
15
+ * A link a person may be sent to: an absolute http(s) URL, or a path on this origin.
16
+ *
17
+ * `//host` is protocol-relative — another origin wearing a path's clothes — and `/\` is read as
18
+ * the same by browsers, so both are refused along with every other scheme (`javascript:` above
19
+ * all, since this lands in an `href`).
20
+ */
21
+ const link = (value?: string | null): string | null => {
22
+ const href = set(value)
23
+ if (href == null) {
24
+ return null
25
+ }
26
+ if (href.startsWith('/')) {
27
+ return /^\/[/\\]/.test(href) ? null : href
28
+ }
29
+ try {
30
+ const url = new URL(href)
31
+
32
+ return url.protocol === 'https:' || url.protocol === 'http:' ? href : null
33
+ } catch {
34
+ return null
35
+ }
36
+ }
37
+
38
+ const resolve = (cfg?: LoginProviderConfig, brand?: BrandSettings): LoginProviderModel | null => {
39
+ const name = set(cfg?.name)
40
+ const product = set(brand?.name)
41
+ if (name == null || product == null) {
42
+ return null
43
+ }
44
+
45
+ return {
46
+ name,
47
+ operator: set(cfg?.operator) ?? name,
48
+ product,
49
+ info: link(cfg?.info),
50
+ placement: cfg?.placement === 'top' ? 'top' : 'inline',
51
+ }
52
+ }
53
+
54
+ const fill = (template: string, model: LoginProviderModel): string => {
55
+ const values: Record<string, string> = {
56
+ provider: model.name, operator: model.operator, product: model.product,
57
+ }
58
+
59
+ return template.replace(PROVIDER_PLACEHOLDER, (match, key: string) => values[key] ?? match)
60
+ }
61
+
62
+ return { resolve, fill }
63
+ }
64
+
65
+ export const loginProviderHelper = createLoginProviderHelper()
@@ -1,5 +1,5 @@
1
1
  import type { LoginMethod, LoginOutcome, ResolvedTermsDocument } from '@owlmeans/client-auth/login'
2
- import type { LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config'
2
+ import type { BrandSettings, LoginScreenConfig, LoginTermsConfig } from '@owlmeans/config'
3
3
 
4
4
  export interface UseLoginMethodsOptions {
5
5
  /** Overrides the configuration the context carries. */
@@ -37,12 +37,63 @@ export interface LoginCreditModel {
37
37
  line: string | null
38
38
  }
39
39
 
40
+ /** Where the provider disclosure sits: a strip above the card, or a line inside it. */
41
+ export type LoginProviderPlacement = 'top' | 'inline'
42
+
43
+ /**
44
+ * Who actually signs a person in, when that is not the application on whose screen they are.
45
+ *
46
+ * Read from `cfg.security.auth.login.provider` (declared onto `LoginScreenConfig` by
47
+ * `./provider-config.ts`). Blank strings are unset, exactly as they are for the credit line: this
48
+ * config is filled from build-time environment variables, and an undelivered one arrives as `''`.
49
+ */
50
+ export interface LoginProviderConfig {
51
+ /** The sign-in service as a person reads it — "OwlMeans IAM". */
52
+ name: string
53
+ /** Who runs it. Defaults to `name`. */
54
+ operator?: string
55
+ /** "More information" — an absolute http(s) URL or a root-relative path; anything else is dropped. */
56
+ info?: string
57
+ /** Default `'inline'`. */
58
+ placement?: LoginProviderPlacement
59
+ }
60
+
61
+ export interface LoginProviderModel {
62
+ name: string
63
+ operator: string
64
+ /** The application the person is signing in to — `cfg.brand.name`. */
65
+ product: string
66
+ info: string | null
67
+ placement: LoginProviderPlacement
68
+ }
69
+
70
+ export interface LoginProviderHelper {
71
+ /**
72
+ * The disclosure a sign-in screen renders, or `null` when there is nothing to disclose.
73
+ *
74
+ * Null unless both the provider's `name` and the product (`brand.name`) are set — a sentence
75
+ * relating two parties cannot be written with one of them missing, and a screen that showed half
76
+ * of it would be worse than one that shows none.
77
+ */
78
+ resolve: (cfg?: LoginProviderConfig, brand?: BrandSettings) => LoginProviderModel | null
79
+ /**
80
+ * Put the names into a sentence AFTER it was translated.
81
+ *
82
+ * `{{provider}}`, `{{operator}}` and `{{product}}`, replaced in one pass — a name that itself
83
+ * contains a placeholder is never expanded a second time. The names never travel as i18next
84
+ * interpolation parameters, so no translation backend gets the chance to escape or format them.
85
+ */
86
+ fill: (template: string, model: LoginProviderModel) => string
87
+ }
88
+
40
89
  export interface LoginMethodsModel {
41
90
  methods: LoginMethod[]
42
91
  /** The one a screen highlights and focuses. It is never started automatically. */
43
92
  primary: LoginMethod | null
44
93
  terms: LoginTermsModel
45
94
  credit: LoginCreditModel
95
+ /** Who signs the person in, on behalf of which product — `null` when the app configured nobody. */
96
+ provider: LoginProviderModel | null
46
97
  /** True while the terms have not been confirmed and confirming them is required. */
47
98
  blocked: boolean
48
99
  /** Id of the method currently starting. */