@orangecheck/me-client 0.1.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/LICENSE +21 -0
- package/README.md +135 -0
- package/dist/index.d.mts +105 -0
- package/dist/index.d.ts +105 -0
- package/dist/index.js +210 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +181 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 OrangeCheck
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# @orangecheck/me-client
|
|
2
|
+
|
|
3
|
+
Drop-in client for [me.ochk.io](https://me.ochk.io). Sign-in-with-OC button, session lifecycle hooks, payment authorization, developer telemetry, and the canonical billable-event taxonomy as TypeScript types.
|
|
4
|
+
|
|
5
|
+
> You pay for sessions and actions, not for clicks.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
yarn add @orangecheck/me-client @orangecheck/auth-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { OcSignInButton, useOcSession } from '@orangecheck/me-client';
|
|
17
|
+
import { OcSessionProvider } from '@orangecheck/auth-client';
|
|
18
|
+
|
|
19
|
+
export function App() {
|
|
20
|
+
return (
|
|
21
|
+
<OcSessionProvider>
|
|
22
|
+
<SignIn />
|
|
23
|
+
</OcSessionProvider>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function SignIn() {
|
|
28
|
+
const { account, status } = useOcSession();
|
|
29
|
+
if (status === 'authenticated') {
|
|
30
|
+
return <p>signed in as {account.address.slice(0, 8)}…</p>;
|
|
31
|
+
}
|
|
32
|
+
return (
|
|
33
|
+
<OcSignInButton
|
|
34
|
+
scope={['identity', 'attest_tier']}
|
|
35
|
+
sessionPolicy={{ duration_seconds: 7 * 86_400, refresh: 'sliding' }}
|
|
36
|
+
onSignin={(session) => console.log('class C envelope:', session)}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Session lifecycle
|
|
43
|
+
|
|
44
|
+
Sessions are the billable atom for me.ochk.io. A signin within a still-valid session is **free** — no Class C event, no charge. Site declares the policy at integration time; the OC verifier enforces it both client-side and server-side.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { oc } from '@orangecheck/me-client';
|
|
48
|
+
|
|
49
|
+
// open a session — Class C billable event, once per real session
|
|
50
|
+
const session = await oc.session.create({
|
|
51
|
+
scope: ['identity', 'payment'],
|
|
52
|
+
sessionPolicy: {
|
|
53
|
+
duration_seconds: 60 * 15, // 15 minutes — banking-shaped
|
|
54
|
+
refresh: 'sliding',
|
|
55
|
+
sensitive_actions: 're-auth',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// refresh inside the session — FREE, telemetry only
|
|
60
|
+
await oc.session.refresh(session.id);
|
|
61
|
+
|
|
62
|
+
// invalidate when the user signs out — FREE
|
|
63
|
+
await oc.session.invalidate(session.id);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Typical session policies
|
|
67
|
+
|
|
68
|
+
| kind | label | billable atom |
|
|
69
|
+
|------------------|------------------------------------|--------------------------------------------|
|
|
70
|
+
| banking-shaped | 15–60 minute sessions | every session |
|
|
71
|
+
| standard SaaS | 7–30 day sessions, sliding refresh | session creation only |
|
|
72
|
+
| mobile app | 90-day session with refresh | initial creation, sensitive-action re-auth |
|
|
73
|
+
|
|
74
|
+
## Payment authorization
|
|
75
|
+
|
|
76
|
+
Class B billable event. Sub-Stripe rates (0.5–1.0% percentage-based). The user receives ~65% of the OC fee as Lightning cashback on `/me/earn`.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { oc } from '@orangecheck/me-client';
|
|
80
|
+
|
|
81
|
+
const payment = await oc.payment.authorize({
|
|
82
|
+
identity: session.identity,
|
|
83
|
+
amount_sats: 240_000,
|
|
84
|
+
description: 'breez · march invoice',
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Developer telemetry
|
|
89
|
+
|
|
90
|
+
Non-billable events the SDK emits for site-side observability. Subscribe with `onTelemetry`. None of these create billable records.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { onTelemetry } from '@orangecheck/me-client';
|
|
94
|
+
|
|
95
|
+
const unsubscribe = onTelemetry((event) => {
|
|
96
|
+
// event.code: 'session.token_refresh' | 'session.intra_signin' |
|
|
97
|
+
// 'session.navigation' | 'auth.signin_failed' |
|
|
98
|
+
// 'auth.signin_cancelled' | 'auth.signin_rejected' |
|
|
99
|
+
// 'verify.passive_check'
|
|
100
|
+
console.log('[oc telemetry]', event);
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
See [me.ochk.io/integrate](https://me.ochk.io/integrate) for the full price-band table, the "what you do not pay for" panel, and the live SDK reference.
|
|
105
|
+
|
|
106
|
+
## Types
|
|
107
|
+
|
|
108
|
+
Every event class, subtype, and price band is exported as a TypeScript type so integrators can typecheck against the canonical taxonomy:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import type {
|
|
112
|
+
BillableEvent,
|
|
113
|
+
EventClass, // 'A' | 'B' | 'C'
|
|
114
|
+
EventSubtype, // every subtype across all three classes
|
|
115
|
+
SessionPolicy,
|
|
116
|
+
PaymentResult,
|
|
117
|
+
TelemetryEvent,
|
|
118
|
+
} from '@orangecheck/me-client';
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The canonical source of truth lives at [oc-me-web/src/lib/events/types.ts](https://github.com/orangecheck/oc-me-web/blob/main/src/lib/events/types.ts). This package mirrors that shape — any change there ships here in the next minor.
|
|
122
|
+
|
|
123
|
+
## Custom origin
|
|
124
|
+
|
|
125
|
+
For staging, self-hosted me-equivalents, or local dev:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { setOrigin } from '@orangecheck/me-client';
|
|
129
|
+
|
|
130
|
+
setOrigin('https://me.example-staging.com');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
type EventClass = 'A' | 'B' | 'C';
|
|
5
|
+
type ClassASubtype = 'account_creation' | 'account_recovery' | 'kyc_tier_upgrade' | 'payment_method_connected' | 'agent_delegation_issued' | 'recovery_method_updated';
|
|
6
|
+
type ClassBSubtype = 'payment_authorization' | 'scoped_action_authorization' | 'attest_verification_at_gate' | 'stamp_signing' | 'pledge_resolution';
|
|
7
|
+
type ClassCSubtype = 'session_creation';
|
|
8
|
+
type EventSubtype = ClassASubtype | ClassBSubtype | ClassCSubtype;
|
|
9
|
+
type AttestTier = 'anonymous' | 'bonded' | 'kyc_light' | 'kyc_strong';
|
|
10
|
+
interface BillableEvent {
|
|
11
|
+
id: string;
|
|
12
|
+
occurred_at: string;
|
|
13
|
+
class: EventClass;
|
|
14
|
+
subtype: EventSubtype;
|
|
15
|
+
site: {
|
|
16
|
+
domain: string;
|
|
17
|
+
display_name: string;
|
|
18
|
+
};
|
|
19
|
+
gross_fee_sats: number;
|
|
20
|
+
platform_fee_sats: number;
|
|
21
|
+
user_earned_sats: number;
|
|
22
|
+
verify_url: string;
|
|
23
|
+
}
|
|
24
|
+
interface SessionPolicy {
|
|
25
|
+
duration_seconds: number;
|
|
26
|
+
refresh: 'sliding' | 'rolling' | 'none';
|
|
27
|
+
sensitive_actions?: 're-auth' | 'none';
|
|
28
|
+
}
|
|
29
|
+
interface Session {
|
|
30
|
+
id: string;
|
|
31
|
+
identity: string;
|
|
32
|
+
opens_at: string;
|
|
33
|
+
expires_at: string;
|
|
34
|
+
policy: SessionPolicy;
|
|
35
|
+
scope: string[];
|
|
36
|
+
}
|
|
37
|
+
interface SignInOptions {
|
|
38
|
+
scope: string[];
|
|
39
|
+
sessionPolicy?: Partial<SessionPolicy>;
|
|
40
|
+
returnTo?: string;
|
|
41
|
+
}
|
|
42
|
+
interface PaymentAuthorizeOptions {
|
|
43
|
+
identity: string;
|
|
44
|
+
amount_sats?: number;
|
|
45
|
+
usd_cents?: number;
|
|
46
|
+
description: string;
|
|
47
|
+
external_ref?: string;
|
|
48
|
+
}
|
|
49
|
+
interface PaymentResult {
|
|
50
|
+
id: string;
|
|
51
|
+
status: 'authorized' | 'failed' | 'cancelled';
|
|
52
|
+
sats_charged?: number;
|
|
53
|
+
user_envelope_id?: string;
|
|
54
|
+
verify_url?: string;
|
|
55
|
+
}
|
|
56
|
+
interface TelemetryEvent {
|
|
57
|
+
code: 'session.intra_signin' | 'session.token_refresh' | 'session.navigation' | 'auth.signin_failed' | 'auth.signin_cancelled' | 'auth.signin_rejected' | 'verify.passive_check';
|
|
58
|
+
timestamp: string;
|
|
59
|
+
session_id?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface OcSignInButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onError'> {
|
|
63
|
+
scope: string[];
|
|
64
|
+
sessionPolicy?: Partial<SessionPolicy>;
|
|
65
|
+
returnTo?: string;
|
|
66
|
+
onSignin?: (session: Session) => void;
|
|
67
|
+
onSignInError?: (error: Error) => void;
|
|
68
|
+
label?: string;
|
|
69
|
+
}
|
|
70
|
+
declare function OcSignInButton({ scope, sessionPolicy, returnTo, onSignin, onSignInError, label, children, onClick, style, ...rest }: OcSignInButtonProps): react_jsx_runtime.JSX.Element;
|
|
71
|
+
|
|
72
|
+
declare function onTelemetry(listener: (e: TelemetryEvent) => void): () => void;
|
|
73
|
+
declare function create(opts: SignInOptions): Promise<Session>;
|
|
74
|
+
declare function refresh(sessionId: string): Promise<Session>;
|
|
75
|
+
declare function invalidate(sessionId: string): Promise<void>;
|
|
76
|
+
declare const session: {
|
|
77
|
+
create: typeof create;
|
|
78
|
+
refresh: typeof refresh;
|
|
79
|
+
invalidate: typeof invalidate;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
declare function authorize(opts: PaymentAuthorizeOptions): Promise<PaymentResult>;
|
|
83
|
+
declare const payment: {
|
|
84
|
+
authorize: typeof authorize;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
declare function setOrigin(url: string): void;
|
|
88
|
+
declare function getOrigin(): string;
|
|
89
|
+
declare class MeClientError extends Error {
|
|
90
|
+
readonly status: number;
|
|
91
|
+
constructor(message: string, status: number);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
declare const oc: {
|
|
95
|
+
session: {
|
|
96
|
+
create: (opts: SignInOptions) => Promise<Session>;
|
|
97
|
+
refresh: (sessionId: string) => Promise<Session>;
|
|
98
|
+
invalidate: (sessionId: string) => Promise<void>;
|
|
99
|
+
};
|
|
100
|
+
payment: {
|
|
101
|
+
authorize: (opts: PaymentAuthorizeOptions) => Promise<PaymentResult>;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export { type AttestTier, type BillableEvent, type ClassASubtype, type ClassBSubtype, type ClassCSubtype, type EventClass, type EventSubtype, MeClientError, OcSignInButton, type OcSignInButtonProps, type PaymentAuthorizeOptions, type PaymentResult, type Session, type SessionPolicy, type SignInOptions, type TelemetryEvent, getOrigin, oc, onTelemetry, payment, session, setOrigin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
type EventClass = 'A' | 'B' | 'C';
|
|
5
|
+
type ClassASubtype = 'account_creation' | 'account_recovery' | 'kyc_tier_upgrade' | 'payment_method_connected' | 'agent_delegation_issued' | 'recovery_method_updated';
|
|
6
|
+
type ClassBSubtype = 'payment_authorization' | 'scoped_action_authorization' | 'attest_verification_at_gate' | 'stamp_signing' | 'pledge_resolution';
|
|
7
|
+
type ClassCSubtype = 'session_creation';
|
|
8
|
+
type EventSubtype = ClassASubtype | ClassBSubtype | ClassCSubtype;
|
|
9
|
+
type AttestTier = 'anonymous' | 'bonded' | 'kyc_light' | 'kyc_strong';
|
|
10
|
+
interface BillableEvent {
|
|
11
|
+
id: string;
|
|
12
|
+
occurred_at: string;
|
|
13
|
+
class: EventClass;
|
|
14
|
+
subtype: EventSubtype;
|
|
15
|
+
site: {
|
|
16
|
+
domain: string;
|
|
17
|
+
display_name: string;
|
|
18
|
+
};
|
|
19
|
+
gross_fee_sats: number;
|
|
20
|
+
platform_fee_sats: number;
|
|
21
|
+
user_earned_sats: number;
|
|
22
|
+
verify_url: string;
|
|
23
|
+
}
|
|
24
|
+
interface SessionPolicy {
|
|
25
|
+
duration_seconds: number;
|
|
26
|
+
refresh: 'sliding' | 'rolling' | 'none';
|
|
27
|
+
sensitive_actions?: 're-auth' | 'none';
|
|
28
|
+
}
|
|
29
|
+
interface Session {
|
|
30
|
+
id: string;
|
|
31
|
+
identity: string;
|
|
32
|
+
opens_at: string;
|
|
33
|
+
expires_at: string;
|
|
34
|
+
policy: SessionPolicy;
|
|
35
|
+
scope: string[];
|
|
36
|
+
}
|
|
37
|
+
interface SignInOptions {
|
|
38
|
+
scope: string[];
|
|
39
|
+
sessionPolicy?: Partial<SessionPolicy>;
|
|
40
|
+
returnTo?: string;
|
|
41
|
+
}
|
|
42
|
+
interface PaymentAuthorizeOptions {
|
|
43
|
+
identity: string;
|
|
44
|
+
amount_sats?: number;
|
|
45
|
+
usd_cents?: number;
|
|
46
|
+
description: string;
|
|
47
|
+
external_ref?: string;
|
|
48
|
+
}
|
|
49
|
+
interface PaymentResult {
|
|
50
|
+
id: string;
|
|
51
|
+
status: 'authorized' | 'failed' | 'cancelled';
|
|
52
|
+
sats_charged?: number;
|
|
53
|
+
user_envelope_id?: string;
|
|
54
|
+
verify_url?: string;
|
|
55
|
+
}
|
|
56
|
+
interface TelemetryEvent {
|
|
57
|
+
code: 'session.intra_signin' | 'session.token_refresh' | 'session.navigation' | 'auth.signin_failed' | 'auth.signin_cancelled' | 'auth.signin_rejected' | 'verify.passive_check';
|
|
58
|
+
timestamp: string;
|
|
59
|
+
session_id?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface OcSignInButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onError'> {
|
|
63
|
+
scope: string[];
|
|
64
|
+
sessionPolicy?: Partial<SessionPolicy>;
|
|
65
|
+
returnTo?: string;
|
|
66
|
+
onSignin?: (session: Session) => void;
|
|
67
|
+
onSignInError?: (error: Error) => void;
|
|
68
|
+
label?: string;
|
|
69
|
+
}
|
|
70
|
+
declare function OcSignInButton({ scope, sessionPolicy, returnTo, onSignin, onSignInError, label, children, onClick, style, ...rest }: OcSignInButtonProps): react_jsx_runtime.JSX.Element;
|
|
71
|
+
|
|
72
|
+
declare function onTelemetry(listener: (e: TelemetryEvent) => void): () => void;
|
|
73
|
+
declare function create(opts: SignInOptions): Promise<Session>;
|
|
74
|
+
declare function refresh(sessionId: string): Promise<Session>;
|
|
75
|
+
declare function invalidate(sessionId: string): Promise<void>;
|
|
76
|
+
declare const session: {
|
|
77
|
+
create: typeof create;
|
|
78
|
+
refresh: typeof refresh;
|
|
79
|
+
invalidate: typeof invalidate;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
declare function authorize(opts: PaymentAuthorizeOptions): Promise<PaymentResult>;
|
|
83
|
+
declare const payment: {
|
|
84
|
+
authorize: typeof authorize;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
declare function setOrigin(url: string): void;
|
|
88
|
+
declare function getOrigin(): string;
|
|
89
|
+
declare class MeClientError extends Error {
|
|
90
|
+
readonly status: number;
|
|
91
|
+
constructor(message: string, status: number);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
declare const oc: {
|
|
95
|
+
session: {
|
|
96
|
+
create: (opts: SignInOptions) => Promise<Session>;
|
|
97
|
+
refresh: (sessionId: string) => Promise<Session>;
|
|
98
|
+
invalidate: (sessionId: string) => Promise<void>;
|
|
99
|
+
};
|
|
100
|
+
payment: {
|
|
101
|
+
authorize: (opts: PaymentAuthorizeOptions) => Promise<PaymentResult>;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export { type AttestTier, type BillableEvent, type ClassASubtype, type ClassBSubtype, type ClassCSubtype, type EventClass, type EventSubtype, MeClientError, OcSignInButton, type OcSignInButtonProps, type PaymentAuthorizeOptions, type PaymentResult, type Session, type SessionPolicy, type SignInOptions, type TelemetryEvent, getOrigin, oc, onTelemetry, payment, session, setOrigin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
function _interopNamespace(e) {
|
|
7
|
+
if (e && e.__esModule) return e;
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
25
|
+
|
|
26
|
+
// src/SignInButton.tsx
|
|
27
|
+
|
|
28
|
+
// src/transport.ts
|
|
29
|
+
var origin = "https://me.ochk.io";
|
|
30
|
+
function setOrigin(url) {
|
|
31
|
+
origin = url.replace(/\/$/, "");
|
|
32
|
+
}
|
|
33
|
+
function getOrigin() {
|
|
34
|
+
return origin;
|
|
35
|
+
}
|
|
36
|
+
async function api(path, opts = {}) {
|
|
37
|
+
const res = await fetch(`${origin}${path}`, {
|
|
38
|
+
method: opts.method ?? "GET",
|
|
39
|
+
credentials: "include",
|
|
40
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
41
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
|
|
42
|
+
signal: opts.signal
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
let message = `me.ochk.io ${res.status}`;
|
|
46
|
+
try {
|
|
47
|
+
const data = await res.json();
|
|
48
|
+
if (data?.error) message = data.error;
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
throw new MeClientError(message, res.status);
|
|
52
|
+
}
|
|
53
|
+
return await res.json();
|
|
54
|
+
}
|
|
55
|
+
var MeClientError = class extends Error {
|
|
56
|
+
constructor(message, status) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "MeClientError";
|
|
59
|
+
this.status = status;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/session.ts
|
|
64
|
+
var DEFAULT_POLICY = {
|
|
65
|
+
duration_seconds: 7 * 24 * 60 * 60,
|
|
66
|
+
// 7 days, standard SaaS shape
|
|
67
|
+
refresh: "sliding",
|
|
68
|
+
sensitive_actions: "re-auth"
|
|
69
|
+
};
|
|
70
|
+
var telemetrySubscribers = /* @__PURE__ */ new Set();
|
|
71
|
+
function onTelemetry(listener) {
|
|
72
|
+
telemetrySubscribers.add(listener);
|
|
73
|
+
return () => {
|
|
74
|
+
telemetrySubscribers.delete(listener);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function emitTelemetry(code, session_id) {
|
|
78
|
+
const e = {
|
|
79
|
+
code,
|
|
80
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
81
|
+
...session_id ? { session_id } : {}
|
|
82
|
+
};
|
|
83
|
+
for (const fn of telemetrySubscribers) {
|
|
84
|
+
try {
|
|
85
|
+
fn(e);
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function create(opts) {
|
|
91
|
+
const policy = { ...DEFAULT_POLICY, ...opts.sessionPolicy ?? {} };
|
|
92
|
+
return api("/api/session/create", {
|
|
93
|
+
method: "POST",
|
|
94
|
+
body: {
|
|
95
|
+
scope: opts.scope,
|
|
96
|
+
policy,
|
|
97
|
+
return_to: opts.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0)
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async function refresh(sessionId) {
|
|
102
|
+
emitTelemetry("session.token_refresh", sessionId);
|
|
103
|
+
return api("/api/session/refresh", {
|
|
104
|
+
method: "POST",
|
|
105
|
+
body: { session_id: sessionId }
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async function invalidate(sessionId) {
|
|
109
|
+
emitTelemetry("session.intra_signin", sessionId);
|
|
110
|
+
await api("/api/session/invalidate", {
|
|
111
|
+
method: "POST",
|
|
112
|
+
body: { session_id: sessionId }
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
var session = { create, refresh, invalidate };
|
|
116
|
+
function OcSignInButton({
|
|
117
|
+
scope,
|
|
118
|
+
sessionPolicy,
|
|
119
|
+
returnTo,
|
|
120
|
+
onSignin,
|
|
121
|
+
onSignInError,
|
|
122
|
+
label = "sign in with oc",
|
|
123
|
+
children,
|
|
124
|
+
onClick,
|
|
125
|
+
style,
|
|
126
|
+
...rest
|
|
127
|
+
}) {
|
|
128
|
+
const [pending, setPending] = React__namespace.useState(false);
|
|
129
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
130
|
+
"button",
|
|
131
|
+
{
|
|
132
|
+
type: "button",
|
|
133
|
+
onClick: async (event) => {
|
|
134
|
+
onClick?.(event);
|
|
135
|
+
if (event.defaultPrevented) return;
|
|
136
|
+
setPending(true);
|
|
137
|
+
try {
|
|
138
|
+
const result = await session.create({ scope, sessionPolicy, returnTo });
|
|
139
|
+
onSignin?.(result);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
onSignInError?.(err instanceof Error ? err : new Error(String(err)));
|
|
142
|
+
} finally {
|
|
143
|
+
setPending(false);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
style: {
|
|
147
|
+
display: "inline-flex",
|
|
148
|
+
alignItems: "center",
|
|
149
|
+
gap: 8,
|
|
150
|
+
padding: "10px 16px",
|
|
151
|
+
fontSize: 13,
|
|
152
|
+
fontWeight: 600,
|
|
153
|
+
fontFamily: 'ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace',
|
|
154
|
+
letterSpacing: "0.18em",
|
|
155
|
+
textTransform: "uppercase",
|
|
156
|
+
background: "#f97316",
|
|
157
|
+
color: "#0b0909",
|
|
158
|
+
border: "1px solid #f97316",
|
|
159
|
+
borderRadius: 4,
|
|
160
|
+
cursor: pending ? "progress" : "pointer",
|
|
161
|
+
opacity: pending ? 0.7 : 1,
|
|
162
|
+
...style ?? {}
|
|
163
|
+
},
|
|
164
|
+
disabled: pending || rest.disabled,
|
|
165
|
+
"data-oc-signin": true,
|
|
166
|
+
...rest,
|
|
167
|
+
children: [
|
|
168
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
169
|
+
"span",
|
|
170
|
+
{
|
|
171
|
+
"aria-hidden": true,
|
|
172
|
+
style: {
|
|
173
|
+
display: "inline-block",
|
|
174
|
+
width: 12,
|
|
175
|
+
height: 12,
|
|
176
|
+
background: "#0b0909"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
children ?? (pending ? "opening\u2026" : label)
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/payment.ts
|
|
187
|
+
async function authorize(opts) {
|
|
188
|
+
if (opts.amount_sats == null && opts.usd_cents == null) {
|
|
189
|
+
throw new Error("payment.authorize requires amount_sats or usd_cents");
|
|
190
|
+
}
|
|
191
|
+
return api("/api/payment/authorize", {
|
|
192
|
+
method: "POST",
|
|
193
|
+
body: opts
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
var payment = { authorize };
|
|
197
|
+
|
|
198
|
+
// src/index.ts
|
|
199
|
+
var oc = { session, payment };
|
|
200
|
+
|
|
201
|
+
exports.MeClientError = MeClientError;
|
|
202
|
+
exports.OcSignInButton = OcSignInButton;
|
|
203
|
+
exports.getOrigin = getOrigin;
|
|
204
|
+
exports.oc = oc;
|
|
205
|
+
exports.onTelemetry = onTelemetry;
|
|
206
|
+
exports.payment = payment;
|
|
207
|
+
exports.session = session;
|
|
208
|
+
exports.setOrigin = setOrigin;
|
|
209
|
+
//# sourceMappingURL=index.js.map
|
|
210
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transport.ts","../src/session.ts","../src/SignInButton.tsx","../src/payment.ts","../src/index.ts"],"names":["React","jsxs","jsx"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Tiny HTTP transport layer. The me.ochk.io origin is configurable so\n * staging deployments and self-hosted me-equivalents can swap it.\n */\n\nlet origin = 'https://me.ochk.io';\n\nexport function setOrigin(url: string): void {\n origin = url.replace(/\\/$/, '');\n}\n\nexport function getOrigin(): string {\n return origin;\n}\n\nexport interface FetchOptions {\n method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';\n body?: unknown;\n signal?: AbortSignal;\n}\n\nexport async function api<T>(path: string, opts: FetchOptions = {}): Promise<T> {\n const res = await fetch(`${origin}${path}`, {\n method: opts.method ?? 'GET',\n credentials: 'include',\n headers: { 'content-type': 'application/json', accept: 'application/json' },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: opts.signal,\n });\n if (!res.ok) {\n let message = `me.ochk.io ${res.status}`;\n try {\n const data = (await res.json()) as { error?: string };\n if (data?.error) message = data.error;\n } catch {\n /* ignore */\n }\n throw new MeClientError(message, res.status);\n }\n return (await res.json()) as T;\n}\n\nexport class MeClientError extends Error {\n public readonly status: number;\n constructor(message: string, status: number) {\n super(message);\n this.name = 'MeClientError';\n this.status = status;\n }\n}\n","import type { Session, SessionPolicy, SignInOptions, TelemetryEvent } from './types';\n\nimport { api } from './transport';\n\nconst DEFAULT_POLICY: SessionPolicy = {\n duration_seconds: 7 * 24 * 60 * 60, // 7 days, standard SaaS shape\n refresh: 'sliding',\n sensitive_actions: 're-auth',\n};\n\nconst telemetrySubscribers = new Set<(e: TelemetryEvent) => void>();\n\n/** Subscribe to non-billable developer telemetry events. Call the returned\n * function to unsubscribe. */\nexport function onTelemetry(listener: (e: TelemetryEvent) => void): () => void {\n telemetrySubscribers.add(listener);\n return () => {\n telemetrySubscribers.delete(listener);\n };\n}\n\nfunction emitTelemetry(code: TelemetryEvent['code'], session_id?: string): void {\n const e: TelemetryEvent = {\n code,\n timestamp: new Date().toISOString(),\n ...(session_id ? { session_id } : {}),\n };\n for (const fn of telemetrySubscribers) {\n try {\n fn(e);\n } catch {\n /* listener errors must not break the session flow */\n }\n }\n}\n\n/**\n * Open a sign-in flow at me.ochk.io. Returns the resulting Session once\n * the user consents (Class C billable event for the integrating site,\n * cashback for the user). Internally this opens the OC consent UI in a\n * popup; v1 implementations may also redirect.\n */\nasync function create(opts: SignInOptions): Promise<Session> {\n const policy: SessionPolicy = { ...DEFAULT_POLICY, ...(opts.sessionPolicy ?? {}) };\n return api<Session>('/api/session/create', {\n method: 'POST',\n body: {\n scope: opts.scope,\n policy,\n return_to: opts.returnTo ?? (typeof window !== 'undefined' ? window.location.href : undefined),\n },\n });\n}\n\n/**\n * Refresh a still-valid session inside its policy window. Free for the\n * integrating site (does NOT instantiate a billable event); emits a\n * `session.token_refresh` telemetry record.\n */\nasync function refresh(sessionId: string): Promise<Session> {\n emitTelemetry('session.token_refresh', sessionId);\n return api<Session>('/api/session/refresh', {\n method: 'POST',\n body: { session_id: sessionId },\n });\n}\n\n/** Invalidate a session. Free, telemetry-only. */\nasync function invalidate(sessionId: string): Promise<void> {\n emitTelemetry('session.intra_signin', sessionId);\n await api<{ ok: true }>('/api/session/invalidate', {\n method: 'POST',\n body: { session_id: sessionId },\n });\n}\n\nexport const session = { create, refresh, invalidate };\n","import * as React from 'react';\n\nimport { session } from './session';\nimport type { Session, SessionPolicy } from './types';\n\nexport interface OcSignInButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onError'> {\n scope: string[];\n sessionPolicy?: Partial<SessionPolicy>;\n returnTo?: string;\n onSignin?: (session: Session) => void;\n /** Called when the sign-in flow fails. Renamed from `onError` to avoid\n * shadowing the native button error handler. */\n onSignInError?: (error: Error) => void;\n /** Custom button text. Defaults to \"sign in with oc\". */\n label?: string;\n}\n\n/**\n * Drop-in Sign-in-with-OC button. Triggers the me.ochk.io consent flow\n * for the requested scope, opens (or refreshes) a session, and calls\n * onSignin(session) on success. Class C billable event for the\n * integrating site; cashback flows to the user.\n *\n * Mounting requires <OcSessionProvider> from @orangecheck/auth-client\n * somewhere up the React tree so the resulting session can be read by\n * useOcSession() across the rest of the app.\n */\nexport function OcSignInButton({\n scope,\n sessionPolicy,\n returnTo,\n onSignin,\n onSignInError,\n label = 'sign in with oc',\n children,\n onClick,\n style,\n ...rest\n}: OcSignInButtonProps) {\n const [pending, setPending] = React.useState(false);\n\n return (\n <button\n type=\"button\"\n onClick={async (event) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n setPending(true);\n try {\n const result = await session.create({ scope, sessionPolicy, returnTo });\n onSignin?.(result);\n } catch (err) {\n onSignInError?.(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setPending(false);\n }\n }}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 16px',\n fontSize: 13,\n fontWeight: 600,\n fontFamily: 'ui-monospace, \"JetBrains Mono\", \"SF Mono\", Menlo, monospace',\n letterSpacing: '0.18em',\n textTransform: 'uppercase',\n background: '#f97316',\n color: '#0b0909',\n border: '1px solid #f97316',\n borderRadius: 4,\n cursor: pending ? 'progress' : 'pointer',\n opacity: pending ? 0.7 : 1,\n ...(style ?? {}),\n }}\n disabled={pending || rest.disabled}\n data-oc-signin\n {...rest}\n >\n {/* compact OC mark */}\n <span\n aria-hidden\n style={{\n display: 'inline-block',\n width: 12,\n height: 12,\n background: '#0b0909',\n }}\n />\n {children ?? (pending ? 'opening…' : label)}\n </button>\n );\n}\n","import type { PaymentAuthorizeOptions, PaymentResult } from './types';\n\nimport { api } from './transport';\n\n/**\n * Authorize a payment through the user's OC identity. Class B billable\n * event for the integrating site (sub-Stripe rate, percentage-based);\n * cashback flows to the user as Lightning credit on /me/earn.\n *\n * The user is prompted by me.ochk.io to consent to the specific payment\n * before this resolves. If the user declines or cancels, the result has\n * status `'cancelled'`, a non-billable telemetry record is emitted, and\n * NO billable event is created.\n */\nasync function authorize(opts: PaymentAuthorizeOptions): Promise<PaymentResult> {\n if (opts.amount_sats == null && opts.usd_cents == null) {\n throw new Error('payment.authorize requires amount_sats or usd_cents');\n }\n return api<PaymentResult>('/api/payment/authorize', {\n method: 'POST',\n body: opts,\n });\n}\n\nexport const payment = { authorize };\n","/**\n * @orangecheck/me-client\n *\n * Drop-in client for me.ochk.io. Exports the canonical billable-event\n * taxonomy as TypeScript types, session lifecycle hooks, payment\n * authorization, the developer telemetry stream, and the React sign-in\n * button.\n *\n * Sign-in-with-OC. You pay for sessions and actions, not for clicks.\n */\n\nexport { OcSignInButton } from './SignInButton';\nexport type { OcSignInButtonProps } from './SignInButton';\n\nexport { session, onTelemetry } from './session';\nexport { payment } from './payment';\nexport { setOrigin, getOrigin, MeClientError } from './transport';\n\nexport type {\n EventClass,\n EventSubtype,\n ClassASubtype,\n ClassBSubtype,\n ClassCSubtype,\n AttestTier,\n BillableEvent,\n Session,\n SessionPolicy,\n SignInOptions,\n PaymentAuthorizeOptions,\n PaymentResult,\n TelemetryEvent,\n} from './types';\n\nimport { session } from './session';\nimport { payment } from './payment';\n\n/** Convenience namespace mirroring the public API surface in /integrate\n * code samples — `oc.session.create()`, `oc.payment.authorize()`. */\nexport const oc = { session, payment };\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/SignInButton.tsx
|
|
5
|
+
|
|
6
|
+
// src/transport.ts
|
|
7
|
+
var origin = "https://me.ochk.io";
|
|
8
|
+
function setOrigin(url) {
|
|
9
|
+
origin = url.replace(/\/$/, "");
|
|
10
|
+
}
|
|
11
|
+
function getOrigin() {
|
|
12
|
+
return origin;
|
|
13
|
+
}
|
|
14
|
+
async function api(path, opts = {}) {
|
|
15
|
+
const res = await fetch(`${origin}${path}`, {
|
|
16
|
+
method: opts.method ?? "GET",
|
|
17
|
+
credentials: "include",
|
|
18
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
19
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0,
|
|
20
|
+
signal: opts.signal
|
|
21
|
+
});
|
|
22
|
+
if (!res.ok) {
|
|
23
|
+
let message = `me.ochk.io ${res.status}`;
|
|
24
|
+
try {
|
|
25
|
+
const data = await res.json();
|
|
26
|
+
if (data?.error) message = data.error;
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
throw new MeClientError(message, res.status);
|
|
30
|
+
}
|
|
31
|
+
return await res.json();
|
|
32
|
+
}
|
|
33
|
+
var MeClientError = class extends Error {
|
|
34
|
+
constructor(message, status) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = "MeClientError";
|
|
37
|
+
this.status = status;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/session.ts
|
|
42
|
+
var DEFAULT_POLICY = {
|
|
43
|
+
duration_seconds: 7 * 24 * 60 * 60,
|
|
44
|
+
// 7 days, standard SaaS shape
|
|
45
|
+
refresh: "sliding",
|
|
46
|
+
sensitive_actions: "re-auth"
|
|
47
|
+
};
|
|
48
|
+
var telemetrySubscribers = /* @__PURE__ */ new Set();
|
|
49
|
+
function onTelemetry(listener) {
|
|
50
|
+
telemetrySubscribers.add(listener);
|
|
51
|
+
return () => {
|
|
52
|
+
telemetrySubscribers.delete(listener);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function emitTelemetry(code, session_id) {
|
|
56
|
+
const e = {
|
|
57
|
+
code,
|
|
58
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
59
|
+
...session_id ? { session_id } : {}
|
|
60
|
+
};
|
|
61
|
+
for (const fn of telemetrySubscribers) {
|
|
62
|
+
try {
|
|
63
|
+
fn(e);
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function create(opts) {
|
|
69
|
+
const policy = { ...DEFAULT_POLICY, ...opts.sessionPolicy ?? {} };
|
|
70
|
+
return api("/api/session/create", {
|
|
71
|
+
method: "POST",
|
|
72
|
+
body: {
|
|
73
|
+
scope: opts.scope,
|
|
74
|
+
policy,
|
|
75
|
+
return_to: opts.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0)
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async function refresh(sessionId) {
|
|
80
|
+
emitTelemetry("session.token_refresh", sessionId);
|
|
81
|
+
return api("/api/session/refresh", {
|
|
82
|
+
method: "POST",
|
|
83
|
+
body: { session_id: sessionId }
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async function invalidate(sessionId) {
|
|
87
|
+
emitTelemetry("session.intra_signin", sessionId);
|
|
88
|
+
await api("/api/session/invalidate", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: { session_id: sessionId }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
var session = { create, refresh, invalidate };
|
|
94
|
+
function OcSignInButton({
|
|
95
|
+
scope,
|
|
96
|
+
sessionPolicy,
|
|
97
|
+
returnTo,
|
|
98
|
+
onSignin,
|
|
99
|
+
onSignInError,
|
|
100
|
+
label = "sign in with oc",
|
|
101
|
+
children,
|
|
102
|
+
onClick,
|
|
103
|
+
style,
|
|
104
|
+
...rest
|
|
105
|
+
}) {
|
|
106
|
+
const [pending, setPending] = React.useState(false);
|
|
107
|
+
return /* @__PURE__ */ jsxs(
|
|
108
|
+
"button",
|
|
109
|
+
{
|
|
110
|
+
type: "button",
|
|
111
|
+
onClick: async (event) => {
|
|
112
|
+
onClick?.(event);
|
|
113
|
+
if (event.defaultPrevented) return;
|
|
114
|
+
setPending(true);
|
|
115
|
+
try {
|
|
116
|
+
const result = await session.create({ scope, sessionPolicy, returnTo });
|
|
117
|
+
onSignin?.(result);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
onSignInError?.(err instanceof Error ? err : new Error(String(err)));
|
|
120
|
+
} finally {
|
|
121
|
+
setPending(false);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
style: {
|
|
125
|
+
display: "inline-flex",
|
|
126
|
+
alignItems: "center",
|
|
127
|
+
gap: 8,
|
|
128
|
+
padding: "10px 16px",
|
|
129
|
+
fontSize: 13,
|
|
130
|
+
fontWeight: 600,
|
|
131
|
+
fontFamily: 'ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace',
|
|
132
|
+
letterSpacing: "0.18em",
|
|
133
|
+
textTransform: "uppercase",
|
|
134
|
+
background: "#f97316",
|
|
135
|
+
color: "#0b0909",
|
|
136
|
+
border: "1px solid #f97316",
|
|
137
|
+
borderRadius: 4,
|
|
138
|
+
cursor: pending ? "progress" : "pointer",
|
|
139
|
+
opacity: pending ? 0.7 : 1,
|
|
140
|
+
...style ?? {}
|
|
141
|
+
},
|
|
142
|
+
disabled: pending || rest.disabled,
|
|
143
|
+
"data-oc-signin": true,
|
|
144
|
+
...rest,
|
|
145
|
+
children: [
|
|
146
|
+
/* @__PURE__ */ jsx(
|
|
147
|
+
"span",
|
|
148
|
+
{
|
|
149
|
+
"aria-hidden": true,
|
|
150
|
+
style: {
|
|
151
|
+
display: "inline-block",
|
|
152
|
+
width: 12,
|
|
153
|
+
height: 12,
|
|
154
|
+
background: "#0b0909"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
),
|
|
158
|
+
children ?? (pending ? "opening\u2026" : label)
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/payment.ts
|
|
165
|
+
async function authorize(opts) {
|
|
166
|
+
if (opts.amount_sats == null && opts.usd_cents == null) {
|
|
167
|
+
throw new Error("payment.authorize requires amount_sats or usd_cents");
|
|
168
|
+
}
|
|
169
|
+
return api("/api/payment/authorize", {
|
|
170
|
+
method: "POST",
|
|
171
|
+
body: opts
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
var payment = { authorize };
|
|
175
|
+
|
|
176
|
+
// src/index.ts
|
|
177
|
+
var oc = { session, payment };
|
|
178
|
+
|
|
179
|
+
export { MeClientError, OcSignInButton, getOrigin, oc, onTelemetry, payment, session, setOrigin };
|
|
180
|
+
//# sourceMappingURL=index.mjs.map
|
|
181
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transport.ts","../src/session.ts","../src/SignInButton.tsx","../src/payment.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Tiny HTTP transport layer. The me.ochk.io origin is configurable so\n * staging deployments and self-hosted me-equivalents can swap it.\n */\n\nlet origin = 'https://me.ochk.io';\n\nexport function setOrigin(url: string): void {\n origin = url.replace(/\\/$/, '');\n}\n\nexport function getOrigin(): string {\n return origin;\n}\n\nexport interface FetchOptions {\n method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';\n body?: unknown;\n signal?: AbortSignal;\n}\n\nexport async function api<T>(path: string, opts: FetchOptions = {}): Promise<T> {\n const res = await fetch(`${origin}${path}`, {\n method: opts.method ?? 'GET',\n credentials: 'include',\n headers: { 'content-type': 'application/json', accept: 'application/json' },\n body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,\n signal: opts.signal,\n });\n if (!res.ok) {\n let message = `me.ochk.io ${res.status}`;\n try {\n const data = (await res.json()) as { error?: string };\n if (data?.error) message = data.error;\n } catch {\n /* ignore */\n }\n throw new MeClientError(message, res.status);\n }\n return (await res.json()) as T;\n}\n\nexport class MeClientError extends Error {\n public readonly status: number;\n constructor(message: string, status: number) {\n super(message);\n this.name = 'MeClientError';\n this.status = status;\n }\n}\n","import type { Session, SessionPolicy, SignInOptions, TelemetryEvent } from './types';\n\nimport { api } from './transport';\n\nconst DEFAULT_POLICY: SessionPolicy = {\n duration_seconds: 7 * 24 * 60 * 60, // 7 days, standard SaaS shape\n refresh: 'sliding',\n sensitive_actions: 're-auth',\n};\n\nconst telemetrySubscribers = new Set<(e: TelemetryEvent) => void>();\n\n/** Subscribe to non-billable developer telemetry events. Call the returned\n * function to unsubscribe. */\nexport function onTelemetry(listener: (e: TelemetryEvent) => void): () => void {\n telemetrySubscribers.add(listener);\n return () => {\n telemetrySubscribers.delete(listener);\n };\n}\n\nfunction emitTelemetry(code: TelemetryEvent['code'], session_id?: string): void {\n const e: TelemetryEvent = {\n code,\n timestamp: new Date().toISOString(),\n ...(session_id ? { session_id } : {}),\n };\n for (const fn of telemetrySubscribers) {\n try {\n fn(e);\n } catch {\n /* listener errors must not break the session flow */\n }\n }\n}\n\n/**\n * Open a sign-in flow at me.ochk.io. Returns the resulting Session once\n * the user consents (Class C billable event for the integrating site,\n * cashback for the user). Internally this opens the OC consent UI in a\n * popup; v1 implementations may also redirect.\n */\nasync function create(opts: SignInOptions): Promise<Session> {\n const policy: SessionPolicy = { ...DEFAULT_POLICY, ...(opts.sessionPolicy ?? {}) };\n return api<Session>('/api/session/create', {\n method: 'POST',\n body: {\n scope: opts.scope,\n policy,\n return_to: opts.returnTo ?? (typeof window !== 'undefined' ? window.location.href : undefined),\n },\n });\n}\n\n/**\n * Refresh a still-valid session inside its policy window. Free for the\n * integrating site (does NOT instantiate a billable event); emits a\n * `session.token_refresh` telemetry record.\n */\nasync function refresh(sessionId: string): Promise<Session> {\n emitTelemetry('session.token_refresh', sessionId);\n return api<Session>('/api/session/refresh', {\n method: 'POST',\n body: { session_id: sessionId },\n });\n}\n\n/** Invalidate a session. Free, telemetry-only. */\nasync function invalidate(sessionId: string): Promise<void> {\n emitTelemetry('session.intra_signin', sessionId);\n await api<{ ok: true }>('/api/session/invalidate', {\n method: 'POST',\n body: { session_id: sessionId },\n });\n}\n\nexport const session = { create, refresh, invalidate };\n","import * as React from 'react';\n\nimport { session } from './session';\nimport type { Session, SessionPolicy } from './types';\n\nexport interface OcSignInButtonProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onError'> {\n scope: string[];\n sessionPolicy?: Partial<SessionPolicy>;\n returnTo?: string;\n onSignin?: (session: Session) => void;\n /** Called when the sign-in flow fails. Renamed from `onError` to avoid\n * shadowing the native button error handler. */\n onSignInError?: (error: Error) => void;\n /** Custom button text. Defaults to \"sign in with oc\". */\n label?: string;\n}\n\n/**\n * Drop-in Sign-in-with-OC button. Triggers the me.ochk.io consent flow\n * for the requested scope, opens (or refreshes) a session, and calls\n * onSignin(session) on success. Class C billable event for the\n * integrating site; cashback flows to the user.\n *\n * Mounting requires <OcSessionProvider> from @orangecheck/auth-client\n * somewhere up the React tree so the resulting session can be read by\n * useOcSession() across the rest of the app.\n */\nexport function OcSignInButton({\n scope,\n sessionPolicy,\n returnTo,\n onSignin,\n onSignInError,\n label = 'sign in with oc',\n children,\n onClick,\n style,\n ...rest\n}: OcSignInButtonProps) {\n const [pending, setPending] = React.useState(false);\n\n return (\n <button\n type=\"button\"\n onClick={async (event) => {\n onClick?.(event);\n if (event.defaultPrevented) return;\n setPending(true);\n try {\n const result = await session.create({ scope, sessionPolicy, returnTo });\n onSignin?.(result);\n } catch (err) {\n onSignInError?.(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setPending(false);\n }\n }}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 8,\n padding: '10px 16px',\n fontSize: 13,\n fontWeight: 600,\n fontFamily: 'ui-monospace, \"JetBrains Mono\", \"SF Mono\", Menlo, monospace',\n letterSpacing: '0.18em',\n textTransform: 'uppercase',\n background: '#f97316',\n color: '#0b0909',\n border: '1px solid #f97316',\n borderRadius: 4,\n cursor: pending ? 'progress' : 'pointer',\n opacity: pending ? 0.7 : 1,\n ...(style ?? {}),\n }}\n disabled={pending || rest.disabled}\n data-oc-signin\n {...rest}\n >\n {/* compact OC mark */}\n <span\n aria-hidden\n style={{\n display: 'inline-block',\n width: 12,\n height: 12,\n background: '#0b0909',\n }}\n />\n {children ?? (pending ? 'opening…' : label)}\n </button>\n );\n}\n","import type { PaymentAuthorizeOptions, PaymentResult } from './types';\n\nimport { api } from './transport';\n\n/**\n * Authorize a payment through the user's OC identity. Class B billable\n * event for the integrating site (sub-Stripe rate, percentage-based);\n * cashback flows to the user as Lightning credit on /me/earn.\n *\n * The user is prompted by me.ochk.io to consent to the specific payment\n * before this resolves. If the user declines or cancels, the result has\n * status `'cancelled'`, a non-billable telemetry record is emitted, and\n * NO billable event is created.\n */\nasync function authorize(opts: PaymentAuthorizeOptions): Promise<PaymentResult> {\n if (opts.amount_sats == null && opts.usd_cents == null) {\n throw new Error('payment.authorize requires amount_sats or usd_cents');\n }\n return api<PaymentResult>('/api/payment/authorize', {\n method: 'POST',\n body: opts,\n });\n}\n\nexport const payment = { authorize };\n","/**\n * @orangecheck/me-client\n *\n * Drop-in client for me.ochk.io. Exports the canonical billable-event\n * taxonomy as TypeScript types, session lifecycle hooks, payment\n * authorization, the developer telemetry stream, and the React sign-in\n * button.\n *\n * Sign-in-with-OC. You pay for sessions and actions, not for clicks.\n */\n\nexport { OcSignInButton } from './SignInButton';\nexport type { OcSignInButtonProps } from './SignInButton';\n\nexport { session, onTelemetry } from './session';\nexport { payment } from './payment';\nexport { setOrigin, getOrigin, MeClientError } from './transport';\n\nexport type {\n EventClass,\n EventSubtype,\n ClassASubtype,\n ClassBSubtype,\n ClassCSubtype,\n AttestTier,\n BillableEvent,\n Session,\n SessionPolicy,\n SignInOptions,\n PaymentAuthorizeOptions,\n PaymentResult,\n TelemetryEvent,\n} from './types';\n\nimport { session } from './session';\nimport { payment } from './payment';\n\n/** Convenience namespace mirroring the public API surface in /integrate\n * code samples — `oc.session.create()`, `oc.payment.authorize()`. */\nexport const oc = { session, payment };\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@orangecheck/me-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Drop-in client for me.ochk.io. Sign-in-with-OC button, session lifecycle hooks (oc.session.create / refresh / invalidate), payment authorization, and the canonical billable-event taxonomy as types.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"orangecheck",
|
|
7
|
+
"bitcoin",
|
|
8
|
+
"identity",
|
|
9
|
+
"sign-in-with-oc",
|
|
10
|
+
"react",
|
|
11
|
+
"sso",
|
|
12
|
+
"lightning"
|
|
13
|
+
],
|
|
14
|
+
"author": "OrangeCheck",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"homepage": "https://me.ochk.io",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/orangecheck/oc-packages.git",
|
|
20
|
+
"directory": "me-client"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/orangecheck/oc-packages/issues"
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"module": "./dist/index.mjs",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.mjs",
|
|
32
|
+
"require": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"dev": "tsup --watch",
|
|
43
|
+
"test": "vitest run",
|
|
44
|
+
"type-check": "tsc --noEmit",
|
|
45
|
+
"clean": "rm -rf dist",
|
|
46
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@orangecheck/auth-client": "^0.4.0",
|
|
50
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
51
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@orangecheck/auth-client": "^0.4.0",
|
|
55
|
+
"@testing-library/react": "^16.3.2",
|
|
56
|
+
"@types/node": "^22.0.0",
|
|
57
|
+
"@types/react": "^18.3.12",
|
|
58
|
+
"@types/react-dom": "^18.3.1",
|
|
59
|
+
"jsdom": "^29.0.2",
|
|
60
|
+
"react": "^18.3.1",
|
|
61
|
+
"react-dom": "^18.3.1",
|
|
62
|
+
"tsup": "^8.3.5",
|
|
63
|
+
"typescript": "^5.6.3",
|
|
64
|
+
"vitest": "^3.0.0"
|
|
65
|
+
},
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
|
+
}
|
|
69
|
+
}
|