@gjsify/system-accounts 0.0.0-stage → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -2
- package/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/goa.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/esm/mapping.js +1 -0
- package/lib/esm/no-account-store.js +1 -0
- package/lib/esm/system-accounts.js +1 -0
- package/lib/esm/types.js +0 -0
- package/lib/types/fake-goa.d.ts +43 -0
- package/lib/types/goa.d.ts +9 -0
- package/lib/types/goa.gjs.spec.d.ts +2 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/mapping.d.ts +44 -0
- package/lib/types/mapping.spec.d.ts +2 -0
- package/lib/types/no-account-store.d.ts +4 -0
- package/lib/types/no-account-store.spec.d.ts +2 -0
- package/lib/types/system-accounts.d.ts +3 -0
- package/lib/types/types.d.ts +78 -0
- package/package.json +88 -5
package/README.md
CHANGED
|
@@ -1,3 +1,65 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @gjsify/system-accounts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The accounts a user already set up on their system: find them, read their mail server settings, log in without asking for a password again. The interface is the capabilities a caller needs; a driver per platform maps the system's account store onto them ([ADR 0095](../../../docs/adr/0095-system-accounts-are-asked-by-capability.md)).
|
|
4
|
+
|
|
5
|
+
Part of the [gjsify](https://github.com/gjsify/gjsify) project. **Tier 3 (experimental, ADR 0003)**: new, one driver, no promise yet.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
gjsify install @gjsify/system-accounts
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createSystemAccounts, isUnavailable } from '@gjsify/system-accounts';
|
|
17
|
+
|
|
18
|
+
const accounts = createSystemAccounts();
|
|
19
|
+
if (accounts.capabilities().services.length === 0) {
|
|
20
|
+
// No account store on this host: ask the user instead.
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (const account of await accounts.listAccounts('mail')) {
|
|
24
|
+
const mail = await accounts.getMailSettings(account);
|
|
25
|
+
if (isUnavailable(mail) || !mail.smtp) continue;
|
|
26
|
+
|
|
27
|
+
const credentials = await accounts.getCredentials(account, 'smtp');
|
|
28
|
+
if (credentials.kind === 'password') login(mail.smtp, credentials.secret);
|
|
29
|
+
if (credentials.kind === 'oauth2') loginXoauth2(mail.smtp, credentials.token); // valid until credentials.expiresAt
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Call | Answers |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `capabilities()` | what THIS host can answer: `services` it lists, `credentials` kinds it hands out. Both empty without an account store |
|
|
36
|
+
| `listAccounts(service?)` | `Account[]` (id, provider, identity, label, enabled services). Empty when the store cannot be reached |
|
|
37
|
+
| `getMailSettings(account)` | `{ sender, imap?, smtp? }`, each server as host, port, `security` (`tls` / `starttls` / `none`) and user name; or `Unavailable` |
|
|
38
|
+
| `getCredentials(account, purpose)` | `{ kind: 'password', secret }`, `{ kind: 'oauth2', token, expiresAt }`, or `Unavailable` |
|
|
39
|
+
|
|
40
|
+
**A missing capability is a value, never an exception.** `Unavailable` carries a `reason`: `no-account-store`, `unknown-account`, `not-supported` (the account has no mail server, no credentials) or `credentials-unavailable` (the store would not hand the secret out). The ADR's `Credentials` has two members; the third is how that rule is spelled for a call that has to return one.
|
|
41
|
+
|
|
42
|
+
**Secrets** go to the caller and nowhere else: never logged, cached or put in an error. They are read per call; an OAuth2 token carries its expiry, so ask again afterwards instead of keeping it.
|
|
43
|
+
|
|
44
|
+
## Platforms
|
|
45
|
+
|
|
46
|
+
| Platform | State |
|
|
47
|
+
|---|---|
|
|
48
|
+
| Linux, GNOME | **driver**: GNOME Online Accounts through `libgoa` (`gi://Goa?version=1.0&optional`). A host without the typelib still starts and reports empty capabilities |
|
|
49
|
+
| Linux without GOA, macOS, Windows | no driver; empty capabilities. Whether a platform offers a usable account store to third-party apps is **unexplored**, not ruled out |
|
|
50
|
+
| Android, iOS, browser | not served (`gjsify.runtimes`: only `gjs` is `native`) |
|
|
51
|
+
|
|
52
|
+
IMAP and SMTP passwords are looked up under the ids GOA keeps them under (`imap-password`, `smtp-password`); SMTP falls back to the stored IMAP password, because one login often serves both.
|
|
53
|
+
|
|
54
|
+
## Testing
|
|
55
|
+
|
|
56
|
+
`libgoa` has no constructor that takes a connection: `Goa.Client.new` dials the session bus. `src/fake-goa.ts` therefore exports the interfaces it calls (`ObjectManager`, `PasswordBased`, `OAuth2Based`, and the few bus methods a client needs) on a peer-to-peer `Gio.DBusServer` and points the session address at it, so the driver runs libgoa's own client code with no GNOME session, no real account and no keyring. Dummy secrets are compared, never printed.
|
|
57
|
+
|
|
58
|
+
- GLib caches the session connection per process. Run the suite through `test:gjs`, which starts it with a dead bus path; the fake refuses to go on if the process is already bound to a real bus, so a developer's accounts never reach a test.
|
|
59
|
+
- The mapping from account fields to the contract (host/port splitting, `tls`/`starttls`/`none` from the two flags and the port, which password ids to try) is pure and runs on Node too.
|
|
60
|
+
- Not covered: a real session bus, a real provider (the OAuth2 path has run against the fake only), and a host whose `libgoa` is genuinely missing (the empty-capabilities path is tested by passing no namespace).
|
|
61
|
+
- The `&optional` import costs a top-level await in the bundle that reaches it, and a `@gjsify/unit` run wedges on it (ADR 0085 clause 5). The specs therefore reach the driver (`src/goa.ts`) directly; only `createSystemAccounts` (`src/system-accounts.ts`) carries the flag.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
|
package/lib/esm/goa.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{enabledServices as e,mailSettingsFrom as t,passwordIds as n}from"./mapping.js";import{isUnavailable as r,noAccountStore as i}from"./no-account-store.js";import a from"gi://GLib?version=2.0";import o from"gi://Gio?version=2.0";const unavailable=e=>({kind:`unavailable`,reason:e});function newClient(e,t){return new Promise((n,r)=>{e.Client.new(t,(t,i)=>{try{n(e.Client.new_finish(i))}catch(e){r(e)}})})}async function connect(e,t){let n=new o.Cancellable,r=a.timeout_add(a.PRIORITY_DEFAULT,t,()=>(n.cancel(),a.SOURCE_REMOVE));try{return await newClient(e,n)}catch{return}finally{a.Source.remove(r)}}function secretOf(e,t){return new Promise(n=>{e.call_get_password(t,null,(t,r)=>{try{let[t,i]=e.call_get_password_finish(r);n(t&&i?i:void 0)}catch{n(void 0)}})})}function tokenOf(e){return new Promise(t=>{e.call_get_access_token(null,(n,r)=>{try{let[n,i,a]=e.call_get_access_token_finish(r);t(n&&i?[i,a]:void 0)}catch{t(void 0)}})})}function toAccount(t){let n=t.get_account();if(n)return{id:n.id??``,provider:n.provider_type??``,providerName:n.provider_name??``,identity:n.identity??``,label:n.presentation_identity??``,services:e({mail:!!t.get_mail(),calendar:!!t.get_calendar(),contacts:!!t.get_contacts(),files:!!t.get_files()},{mail:!!n.mail_disabled,calendar:!!n.calendar_disabled,contacts:!!n.contacts_disabled,files:!!n.files_disabled})}}function createGoaAccounts({goa:e,now:a=Date.now,connectTimeoutMs:o=5e3}){if(!e)return i;let s,client=async()=>{s??=connect(e,o);let t=await s;return t||(s=void 0),t};async function find(e){let t=await client();return t?t.lookup_by_id(e.id)??unavailable(`unknown-account`):unavailable(`no-account-store`)}return{capabilities:()=>({services:[`mail`,`calendar`,`contacts`,`files`],credentials:[`password`,`oauth2`]}),async listAccounts(e){let t=await client();if(!t)return[];let n=t.get_accounts().flatMap(e=>toAccount(e)??[]);return e?n.filter(t=>t.services.includes(e)):n},async getMailSettings(e){let n=await find(e);if(r(n))return n;let i=n.get_mail();return i?t({emailAddress:i.email_address??``,name:i.name??``,imapSupported:i.imap_supported,imap:{host:i.imap_host??``,userName:i.imap_user_name??``,useSsl:i.imap_use_ssl,useTls:i.imap_use_tls},smtpSupported:i.smtp_supported,smtp:{host:i.smtp_host??``,userName:i.smtp_user_name??``,useSsl:i.smtp_use_ssl,useTls:i.smtp_use_tls,authenticate:i.smtp_use_auth}})??unavailable(`not-supported`):unavailable(`not-supported`)},async getCredentials(e,t){let i=await find(e);if(r(i))return i;let o=i.get_oauth2_based();if(o){let e=await tokenOf(o);if(!e)return unavailable(`credentials-unavailable`);let[t,n]=e;return{kind:`oauth2`,token:t,expiresAt:a()+n*1e3}}let s=i.get_password_based();if(!s)return unavailable(`not-supported`);for(let e of n(t)){let t=await secretOf(s,e);if(t!==void 0)return{kind:`password`,secret:t}}return unavailable(`credentials-unavailable`)}}}export{createGoaAccounts};
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isUnavailable as e}from"./no-account-store.js";import{createSystemAccounts as t}from"./system-accounts.js";export{t as createSystemAccounts,e as isUnavailable};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";function splitHostPort(e){let t=e.trim(),n=/^\[([^\]]+)\](?::(\d*))?$/.exec(t);if(n)return{host:n[1],port:parsePort(n[2])};let r=t.indexOf(`:`);return r===-1||r!==t.lastIndexOf(`:`)?{host:t}:{host:t.slice(0,r),port:parsePort(t.slice(r+1))}}function parsePort(e){if(!e||!/^\d+$/.test(e))return;let t=Number.parseInt(e,10);return t>=1&&t<=65535?t:void 0}const e={imap:993,smtp:465},t={imap:{tls:993,starttls:143,none:143},smtp:{tls:465,starttls:587,none:25}};function securityFromFlags(t,n,r,i){return n?`tls`:r?`starttls`:i===e[t]?`tls`:`none`}function serverSettingsFrom(e,n){let{host:r,port:i}=splitHostPort(n.host);if(!r)return;let a=securityFromFlags(e,n.useSsl,n.useTls,i);return{host:r,port:i??t[e][a],security:a,...n.authenticate&&n.userName?{username:n.userName}:{}}}function mailSettingsFrom(e){let t=e.imapSupported?serverSettingsFrom(`imap`,{...e.imap,authenticate:!0}):void 0,n=e.smtpSupported?serverSettingsFrom(`smtp`,e.smtp):void 0;if(!(!t&&!n))return{sender:{address:e.emailAddress,...e.name?{name:e.name}:{}},...t?{imap:t}:{},...n?{smtp:n}:{}}}function enabledServices(e,t){return[`mail`,`calendar`,`contacts`,`files`].filter(n=>e[n]&&!t[n])}function passwordIds(e){return e===`imap`?[`imap-password`,`password`,``]:[`smtp-password`,`imap-password`,`password`,``]}export{enabledServices,mailSettingsFrom,passwordIds,securityFromFlags,serverSettingsFrom,splitHostPort};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";const e={kind:`unavailable`,reason:`no-account-store`},t={capabilities:()=>({services:[],credentials:[]}),listAccounts:async()=>[],getMailSettings:async()=>e,getCredentials:async()=>e};function isUnavailable(e){return`kind`in e&&e.kind===`unavailable`}export{isUnavailable,t as noAccountStore};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{createGoaAccounts as e}from"./goa.js";import t from"gi://Goa?version=1.0&optional";function createSystemAccounts(){return e({goa:t})}export{createSystemAccounts};
|
package/lib/esm/types.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AccountService } from './types.js';
|
|
2
|
+
export interface FakeMail {
|
|
3
|
+
readonly email: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly imap?: {
|
|
6
|
+
host: string;
|
|
7
|
+
user: string;
|
|
8
|
+
ssl: boolean;
|
|
9
|
+
tls: boolean;
|
|
10
|
+
};
|
|
11
|
+
readonly smtp?: {
|
|
12
|
+
host: string;
|
|
13
|
+
user: string;
|
|
14
|
+
ssl: boolean;
|
|
15
|
+
tls: boolean;
|
|
16
|
+
auth: boolean;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface FakeAccount {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly provider?: string;
|
|
22
|
+
readonly identity?: string;
|
|
23
|
+
readonly disabled?: Partial<Record<AccountService, boolean>>;
|
|
24
|
+
readonly mail?: FakeMail;
|
|
25
|
+
readonly calendar?: boolean;
|
|
26
|
+
readonly contacts?: boolean;
|
|
27
|
+
readonly files?: boolean;
|
|
28
|
+
/** Secret id → dummy secret. */
|
|
29
|
+
readonly passwords?: Readonly<Record<string, string>>;
|
|
30
|
+
readonly oauth2?: {
|
|
31
|
+
readonly token: string;
|
|
32
|
+
readonly expiresIn: number;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface FakeGoa {
|
|
36
|
+
setAccounts(accounts: readonly FakeAccount[]): void;
|
|
37
|
+
/** The secret ids libgoa was asked for since `setAccounts`, never the secrets. */
|
|
38
|
+
readonly requestedPasswordIds: string[];
|
|
39
|
+
/** How many `GetManagedObjects` calls arrived: proof the driver talked to THIS server. */
|
|
40
|
+
managedObjectsCalls(): number;
|
|
41
|
+
}
|
|
42
|
+
/** One fake per process; later calls return the same one, so a test sets its own accounts. */
|
|
43
|
+
export declare function startFakeGoa(): Promise<FakeGoa>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type Goa from 'gi://Goa?version=1.0';
|
|
2
|
+
import type { SystemAccounts } from './types.js';
|
|
3
|
+
export interface GoaOptions {
|
|
4
|
+
/** The libgoa namespace, or `undefined` on a host without it. */
|
|
5
|
+
readonly goa: typeof Goa | undefined;
|
|
6
|
+
readonly now?: () => number;
|
|
7
|
+
readonly connectTimeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function createGoaAccounts({ goa, now, connectTimeoutMs, }: GoaOptions): SystemAccounts;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createSystemAccounts } from './system-accounts.js';
|
|
2
|
+
export { isUnavailable } from './no-account-store.js';
|
|
3
|
+
export type { Account, AccountCapabilities, AccountService, ConnectionSecurity, CredentialKind, CredentialPurpose, Credentials, MailSettings, OAuth2Credentials, PasswordCredentials, ServerSettings, SystemAccounts, Unavailable, UnavailableReason, } from './types.js';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AccountService, ConnectionSecurity, CredentialPurpose, MailSettings, ServerSettings } from './types.js';
|
|
2
|
+
export interface HostPort {
|
|
3
|
+
readonly host: string;
|
|
4
|
+
readonly port?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Split `host`, `host:port` and `[v6]:port`. A bare IPv6 literal has several colons and no
|
|
8
|
+
* port. A port outside 1..65535 is dropped rather than guessed, so the caller falls back to
|
|
9
|
+
* the protocol default instead of dialling port 0.
|
|
10
|
+
*/
|
|
11
|
+
export declare function splitHostPort(raw: string): HostPort;
|
|
12
|
+
/**
|
|
13
|
+
* The two flags an account store keeps for transport security. When neither is set the
|
|
14
|
+
* port still tells: 993/465 is TLS from the first byte, which some stores leave unflagged.
|
|
15
|
+
*/
|
|
16
|
+
export declare function securityFromFlags(protocol: CredentialPurpose, useSsl: boolean, useTls: boolean, explicitPort?: number): ConnectionSecurity;
|
|
17
|
+
export interface ServerFields {
|
|
18
|
+
readonly host: string;
|
|
19
|
+
readonly userName: string;
|
|
20
|
+
readonly useSsl: boolean;
|
|
21
|
+
readonly useTls: boolean;
|
|
22
|
+
/** False for a server that takes no login: the user name is then not reported. */
|
|
23
|
+
readonly authenticate: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** `undefined` when the account names no host. */
|
|
26
|
+
export declare function serverSettingsFrom(protocol: CredentialPurpose, fields: ServerFields): ServerSettings | undefined;
|
|
27
|
+
export interface MailFields {
|
|
28
|
+
readonly emailAddress: string;
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly imapSupported: boolean;
|
|
31
|
+
readonly imap: Omit<ServerFields, 'authenticate'>;
|
|
32
|
+
readonly smtpSupported: boolean;
|
|
33
|
+
readonly smtp: ServerFields;
|
|
34
|
+
}
|
|
35
|
+
/** `undefined` when the account has neither an IMAP nor an SMTP server. */
|
|
36
|
+
export declare function mailSettingsFrom(fields: MailFields): MailSettings | undefined;
|
|
37
|
+
/** The services an account is set up for and the user has not switched off. */
|
|
38
|
+
export declare function enabledServices(present: Readonly<Record<AccountService, boolean>>, disabled: Readonly<Record<AccountService, boolean>>): AccountService[];
|
|
39
|
+
/**
|
|
40
|
+
* The secret ids to try, in order. The store keeps the IMAP and the SMTP password under
|
|
41
|
+
* their own ids; the bare ids are what older account types used. SMTP falls back to the
|
|
42
|
+
* IMAP password because one login often serves both and the store keeps it once.
|
|
43
|
+
*/
|
|
44
|
+
export declare function passwordIds(purpose: CredentialPurpose): readonly string[];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SystemAccounts, Unavailable } from './types.js';
|
|
2
|
+
/** The answer of a host with no account store: nothing is listed, every question gets a reason. */
|
|
3
|
+
export declare const noAccountStore: SystemAccounts;
|
|
4
|
+
export declare function isUnavailable<T extends object>(answer: T | Unavailable): answer is Unavailable;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export type AccountService = 'mail' | 'calendar' | 'contacts' | 'files';
|
|
2
|
+
/** Which login a credential is for. IMAP and SMTP can hold different secrets. */
|
|
3
|
+
export type CredentialPurpose = 'imap' | 'smtp';
|
|
4
|
+
export type CredentialKind = 'password' | 'oauth2';
|
|
5
|
+
/** `tls` is TLS from the first byte, `starttls` upgrades a plain connection, `none` stays plain. */
|
|
6
|
+
export type ConnectionSecurity = 'tls' | 'starttls' | 'none';
|
|
7
|
+
export interface Account {
|
|
8
|
+
/** Stable for one account on one host; opaque to the caller. */
|
|
9
|
+
readonly id: string;
|
|
10
|
+
/** The store's provider key, e.g. `google` or `imap_smtp`. For display and diagnostics, not for branching. */
|
|
11
|
+
readonly provider: string;
|
|
12
|
+
readonly providerName: string;
|
|
13
|
+
/** What the user logs in as, usually the mail address. */
|
|
14
|
+
readonly identity: string;
|
|
15
|
+
/** The name the system shows for the account. */
|
|
16
|
+
readonly label: string;
|
|
17
|
+
/** The services the account is set up for AND the user has not switched off. */
|
|
18
|
+
readonly services: readonly AccountService[];
|
|
19
|
+
}
|
|
20
|
+
export interface ServerSettings {
|
|
21
|
+
readonly host: string;
|
|
22
|
+
readonly port: number;
|
|
23
|
+
readonly security: ConnectionSecurity;
|
|
24
|
+
/** Absent when the server takes no login (an SMTP relay without authentication). */
|
|
25
|
+
readonly username?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface MailSettings {
|
|
28
|
+
readonly sender: {
|
|
29
|
+
readonly address: string;
|
|
30
|
+
readonly name?: string;
|
|
31
|
+
};
|
|
32
|
+
/** Absent when the account has no IMAP server configured. */
|
|
33
|
+
readonly imap?: ServerSettings;
|
|
34
|
+
/** Absent when the account has no SMTP server configured. */
|
|
35
|
+
readonly smtp?: ServerSettings;
|
|
36
|
+
}
|
|
37
|
+
export interface PasswordCredentials {
|
|
38
|
+
readonly kind: 'password';
|
|
39
|
+
readonly secret: string;
|
|
40
|
+
}
|
|
41
|
+
export interface OAuth2Credentials {
|
|
42
|
+
readonly kind: 'oauth2';
|
|
43
|
+
/** An access token, valid until `expiresAt`. Ask again afterwards; never store it. */
|
|
44
|
+
readonly token: string;
|
|
45
|
+
/** Milliseconds since the epoch. */
|
|
46
|
+
readonly expiresAt: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Why a question got no answer. A missing capability is a value, never an exception
|
|
50
|
+
* (ADR 0095), so callers branch on this instead of wrapping every call in try/catch.
|
|
51
|
+
*/
|
|
52
|
+
export type UnavailableReason =
|
|
53
|
+
/** The host has no account store, or it cannot be reached. */
|
|
54
|
+
'no-account-store' | 'unknown-account'
|
|
55
|
+
/** The account exists but is not set up for what was asked, e.g. no mail server. */
|
|
56
|
+
| 'not-supported'
|
|
57
|
+
/** The store would not hand the secret out: locked, cancelled, or none stored. */
|
|
58
|
+
| 'credentials-unavailable';
|
|
59
|
+
export interface Unavailable {
|
|
60
|
+
readonly kind: 'unavailable';
|
|
61
|
+
readonly reason: UnavailableReason;
|
|
62
|
+
}
|
|
63
|
+
export type Credentials = PasswordCredentials | OAuth2Credentials | Unavailable;
|
|
64
|
+
export interface AccountCapabilities {
|
|
65
|
+
/** Services `listAccounts` can report. Empty: this host has no account store. */
|
|
66
|
+
readonly services: readonly AccountService[];
|
|
67
|
+
/** Credential kinds `getCredentials` can hand out. Empty: accounts are listed but no secret is given. */
|
|
68
|
+
readonly credentials: readonly CredentialKind[];
|
|
69
|
+
}
|
|
70
|
+
export interface SystemAccounts {
|
|
71
|
+
/** What THIS host can answer. Synchronous and cheap: decided when the driver is created. */
|
|
72
|
+
capabilities(): AccountCapabilities;
|
|
73
|
+
/** Accounts, optionally only those set up for `service`. Empty when the store cannot be reached. */
|
|
74
|
+
listAccounts(service?: AccountService): Promise<Account[]>;
|
|
75
|
+
getMailSettings(account: Account): Promise<MailSettings | Unavailable>;
|
|
76
|
+
/** The caller owns the result: it is never logged, cached or put in an error by this package. */
|
|
77
|
+
getCredentials(account: Account, purpose: CredentialPurpose): Promise<Credentials>;
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,89 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@gjsify/system-accounts",
|
|
3
|
+
"version": "0.56.0",
|
|
4
|
+
"description": "The accounts a user already set up on their system: find them, read their mail server settings, get credentials. GNOME Online Accounts on Linux.",
|
|
5
|
+
"module": "lib/esm/index.js",
|
|
6
|
+
"types": "lib/types/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./lib/types/index.d.ts",
|
|
12
|
+
"default": "./lib/esm/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"lib",
|
|
17
|
+
"!lib/types/fake-goa.d.ts",
|
|
18
|
+
"!lib/types/**/*.spec.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"clear": "gjsify clear lib tmp tsconfig.tsbuildinfo test.gjs.mjs test.node.mjs",
|
|
22
|
+
"check": "gjsify tsc --noEmit",
|
|
23
|
+
"build": "gjsify run build:gjsify && gjsify run build:types",
|
|
24
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}' 'src/test.gjs.mts' 'src/fake-goa.ts'",
|
|
25
|
+
"build:types": "gjsify tsc",
|
|
26
|
+
"build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
|
|
27
|
+
"build:test:gjs": "gjsify build src/test.gjs.mts --app gjs --outfile test.gjs.mjs",
|
|
28
|
+
"build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
|
|
29
|
+
"test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:node && gjsify run test:gjs",
|
|
30
|
+
"test:gjs": "gjsify env DBUS_SESSION_BUS_ADDRESS=unix:path=/nonexistent XDG_RUNTIME_DIR=/nonexistent gjsify run test.gjs.mjs",
|
|
31
|
+
"test:node": "node test.node.mjs"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"gjs",
|
|
35
|
+
"accounts",
|
|
36
|
+
"gnome-online-accounts",
|
|
37
|
+
"goa",
|
|
38
|
+
"mail",
|
|
39
|
+
"imap",
|
|
40
|
+
"smtp"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@girs/gio-2.0": "5.4.0",
|
|
44
|
+
"@girs/gjs": "5.4.0",
|
|
45
|
+
"@girs/glib-2.0": "5.4.0",
|
|
46
|
+
"@girs/gobject-2.0": "5.4.0",
|
|
47
|
+
"@girs/goa-1.0": "5.4.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@gjsify/cli": "^0.56.0",
|
|
51
|
+
"@gjsify/unit": "^0.56.0",
|
|
52
|
+
"typescript": "^6.0.3"
|
|
53
|
+
},
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
58
|
+
"directory": "packages/gjs/system-accounts"
|
|
59
|
+
},
|
|
60
|
+
"bugs": {
|
|
61
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
62
|
+
},
|
|
63
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/gjs/system-accounts#readme",
|
|
64
|
+
"gjsify": {
|
|
65
|
+
"tier": 3,
|
|
66
|
+
"runtimes": {
|
|
67
|
+
"gjs": "native",
|
|
68
|
+
"node": "none",
|
|
69
|
+
"browser": "none",
|
|
70
|
+
"nativescript": "none"
|
|
71
|
+
},
|
|
72
|
+
"headless": [
|
|
73
|
+
"Gdk",
|
|
74
|
+
"GdkPixbuf",
|
|
75
|
+
"Gsk",
|
|
76
|
+
"Gtk",
|
|
77
|
+
"Adw"
|
|
78
|
+
],
|
|
79
|
+
"os": {
|
|
80
|
+
"linux": "supported",
|
|
81
|
+
"darwin": "none",
|
|
82
|
+
"win32": "none"
|
|
83
|
+
},
|
|
84
|
+
"osNotes": {
|
|
85
|
+
"darwin": "No driver: the package answers with empty capabilities (ADR 0095). macOS has no system-account API for third-party apps that was found; this is not proven either way.",
|
|
86
|
+
"win32": "No driver: the package answers with empty capabilities (ADR 0095). No system-account API for third-party apps was found; this is not proven either way."
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|