@saacms/stripe-bridge 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/README.md +43 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +112 -0
- package/dist/signature.d.ts +28 -0
- package/dist/signature.d.ts.map +1 -0
- package/dist/webhook-handler.d.ts +88 -0
- package/dist/webhook-handler.d.ts.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @saacms/stripe-bridge
|
|
2
|
+
|
|
3
|
+
The reusable Stripe-webhook → saacms-Collection seam. Productionises the
|
|
4
|
+
commerce-bridge spike's GO verdict (`docs/spikes/commerce-bridge.md`) as a
|
|
5
|
+
host-mountable handler.
|
|
6
|
+
|
|
7
|
+
- **Zero runtime deps** beyond `@saacms/core`. No `stripe` SDK, no network.
|
|
8
|
+
Signature verification is Web Crypto HMAC-SHA256 only.
|
|
9
|
+
- **Idempotency is ADR-0030 declarative `unique`** — the target collection
|
|
10
|
+
declares `unique: [["stripeEventId"]]`; a redelivered Stripe `event.id`
|
|
11
|
+
yields a runtime `409`, which this handler maps to a `200` ack (no
|
|
12
|
+
duplicate row, no second `afterChange`). The spike's host-side
|
|
13
|
+
filter-precheck workaround is **superseded** and not reproduced here.
|
|
14
|
+
- **Pure** — the saacms runtime is injected as `opts.fetch` (the host passes
|
|
15
|
+
`app.fetch`) and the clock as `opts.now`, so the handler is fully
|
|
16
|
+
in-process testable.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { createStripeWebhookHandler } from "@saacms/stripe-bridge"
|
|
20
|
+
|
|
21
|
+
const handler = createStripeWebhookHandler({
|
|
22
|
+
fetch: app.fetch, // the saacms runtime
|
|
23
|
+
collectionSlug: "orders",
|
|
24
|
+
signingSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
25
|
+
mapEvent: (event) =>
|
|
26
|
+
event.type === "payment_intent.succeeded"
|
|
27
|
+
? { collection: "orders", body: buildOrder(event) }
|
|
28
|
+
: null, // ignored event types → 200 ack, no write
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Mount at POST /api/stripe/webhook in any host (CF Worker, Next, Astro…).
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## HTTP status contract (the load-bearing idempotency invariant)
|
|
35
|
+
|
|
36
|
+
| Situation | Handler status |
|
|
37
|
+
|---|---|
|
|
38
|
+
| Signature missing/malformed/stale/mismatched | `400` (no write; Stripe must not retry) |
|
|
39
|
+
| Validly signed but body not JSON / no string `id`+`type` | `400` (no write) |
|
|
40
|
+
| `mapEvent` → `null` (unhandled event type) | `200` ack (no write) |
|
|
41
|
+
| Runtime create `2xx` | `200` (created) |
|
|
42
|
+
| Runtime create `409` (ADR-0030 redelivery) | `200` ack (duplicate — idempotent success) |
|
|
43
|
+
| Any other runtime status (5xx / validation / …) | a `5xx` (transient → Stripe retries) |
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @saacms/stripe-bridge — the reusable Stripe-webhook → saacms-Collection seam.
|
|
3
|
+
*
|
|
4
|
+
* Productionises the commerce-bridge spike's GO verdict
|
|
5
|
+
* (`docs/spikes/commerce-bridge.md`) as a host-mountable handler. Idempotency
|
|
6
|
+
* is the ADR-0030 declarative-`unique` primitive (a redelivered Stripe
|
|
7
|
+
* `event.id` → runtime `409` → handler `200` ack), NOT the spike's superseded
|
|
8
|
+
* host-side filter pre-check.
|
|
9
|
+
*
|
|
10
|
+
* Zero runtime dependencies beyond `@saacms/core`: no `stripe` SDK, no network.
|
|
11
|
+
* Signature verification is Web Crypto HMAC-SHA256 only. The saacms runtime is
|
|
12
|
+
* injected (`opts.fetch` = `app.fetch`), so the whole handler is pure and
|
|
13
|
+
* fully in-process testable.
|
|
14
|
+
*/
|
|
15
|
+
export { createStripeWebhookHandler } from "./webhook-handler.ts";
|
|
16
|
+
export type { StripeEventLike, MappedCreate, StripeWebhookOptions, WebhookOutcome, } from "./webhook-handler.ts";
|
|
17
|
+
export { hmacSha256Hex, timingSafeEqualHex, signStripePayload, verifyStripeSignature, } from "./signature.ts";
|
|
18
|
+
export type { SigFailure, SigResult } from "./signature.ts";
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAA;AACjE,YAAY,EACV,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,cAAc,GACf,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/signature.ts
|
|
2
|
+
async function hmacSha256Hex(secret, message) {
|
|
3
|
+
const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
4
|
+
const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(message));
|
|
5
|
+
return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
6
|
+
}
|
|
7
|
+
function timingSafeEqualHex(a, b) {
|
|
8
|
+
if (a.length !== b.length)
|
|
9
|
+
return false;
|
|
10
|
+
let diff = 0;
|
|
11
|
+
for (let i = 0;i < a.length; i++) {
|
|
12
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
13
|
+
}
|
|
14
|
+
return diff === 0;
|
|
15
|
+
}
|
|
16
|
+
async function signStripePayload(rawBody, secret, timestampSeconds) {
|
|
17
|
+
const mac = await hmacSha256Hex(secret, `${timestampSeconds}.${rawBody}`);
|
|
18
|
+
return `t=${timestampSeconds},v1=${mac}`;
|
|
19
|
+
}
|
|
20
|
+
async function verifyStripeSignature(rawBody, header, secret, nowSeconds, toleranceSeconds = 300) {
|
|
21
|
+
if (header == null || header.trim() === "") {
|
|
22
|
+
return { ok: false, reason: "missing-signature-header" };
|
|
23
|
+
}
|
|
24
|
+
let t;
|
|
25
|
+
let v1;
|
|
26
|
+
for (const part of header.split(",")) {
|
|
27
|
+
const [k, val] = part.split("=", 2);
|
|
28
|
+
if (k === "t" && val != null)
|
|
29
|
+
t = Number(val);
|
|
30
|
+
if (k === "v1" && val != null)
|
|
31
|
+
v1 = val;
|
|
32
|
+
}
|
|
33
|
+
if (t == null || Number.isNaN(t) || v1 == null) {
|
|
34
|
+
return { ok: false, reason: "malformed-signature-header" };
|
|
35
|
+
}
|
|
36
|
+
if (Math.abs(nowSeconds - t) > toleranceSeconds) {
|
|
37
|
+
return { ok: false, reason: "timestamp-out-of-tolerance" };
|
|
38
|
+
}
|
|
39
|
+
const expected = await hmacSha256Hex(secret, `${t}.${rawBody}`);
|
|
40
|
+
if (!timingSafeEqualHex(expected, v1)) {
|
|
41
|
+
return { ok: false, reason: "signature-mismatch" };
|
|
42
|
+
}
|
|
43
|
+
return { ok: true };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/webhook-handler.ts
|
|
47
|
+
var PATH_PREFIX = "/api/saacms/v1/";
|
|
48
|
+
var INTERNAL_ORIGIN = "http://saacms.internal";
|
|
49
|
+
function reply(status, outcome, detail) {
|
|
50
|
+
return new Response(JSON.stringify({ ok: status < 400, outcome, detail }), {
|
|
51
|
+
status,
|
|
52
|
+
headers: { "Content-Type": "application/json" }
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function createStripeWebhookHandler(opts) {
|
|
56
|
+
const tolerance = opts.toleranceSeconds ?? 300;
|
|
57
|
+
const clock = opts.now ?? (() => Math.floor(Date.now() / 1000));
|
|
58
|
+
return async function handleStripeWebhook(req) {
|
|
59
|
+
const rawBody = await req.text();
|
|
60
|
+
const sigHeader = req.headers.get("Stripe-Signature") ?? undefined;
|
|
61
|
+
const verified = await verifyStripeSignature(rawBody, sigHeader, opts.signingSecret, clock(), tolerance);
|
|
62
|
+
if (!verified.ok) {
|
|
63
|
+
const reason = verified.reason;
|
|
64
|
+
return reply(400, "signature-invalid", reason);
|
|
65
|
+
}
|
|
66
|
+
let parsed;
|
|
67
|
+
try {
|
|
68
|
+
parsed = JSON.parse(rawBody);
|
|
69
|
+
} catch {
|
|
70
|
+
return reply(400, "malformed-event", "body is not valid JSON");
|
|
71
|
+
}
|
|
72
|
+
const ev = parsed;
|
|
73
|
+
if (typeof ev.id !== "string" || typeof ev.type !== "string") {
|
|
74
|
+
return reply(400, "malformed-event", "missing string id/type");
|
|
75
|
+
}
|
|
76
|
+
const event = parsed;
|
|
77
|
+
const mapped = opts.mapEvent(event);
|
|
78
|
+
if (mapped == null) {
|
|
79
|
+
return reply(200, "ignored", `unhandled event type: ${event.type}`);
|
|
80
|
+
}
|
|
81
|
+
const body = { ...mapped.body };
|
|
82
|
+
if (body["stripeEventId"] == null)
|
|
83
|
+
body["stripeEventId"] = event.id;
|
|
84
|
+
const collection = mapped.collection || opts.collectionSlug;
|
|
85
|
+
const createReq = new Request(`${INTERNAL_ORIGIN}${PATH_PREFIX}${collection}`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "Content-Type": "application/json" },
|
|
88
|
+
body: JSON.stringify(body)
|
|
89
|
+
});
|
|
90
|
+
let createRes;
|
|
91
|
+
try {
|
|
92
|
+
createRes = await opts.fetch(createReq);
|
|
93
|
+
} catch {
|
|
94
|
+
return reply(502, "upstream-error", "runtime fetch threw");
|
|
95
|
+
}
|
|
96
|
+
if (createRes.status >= 200 && createRes.status < 300) {
|
|
97
|
+
return reply(200, "created");
|
|
98
|
+
}
|
|
99
|
+
if (createRes.status === 409) {
|
|
100
|
+
return reply(200, "duplicate");
|
|
101
|
+
}
|
|
102
|
+
const status = createRes.status >= 500 ? createRes.status : 502;
|
|
103
|
+
return reply(status, "upstream-error", `runtime status ${createRes.status}`);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
verifyStripeSignature,
|
|
108
|
+
timingSafeEqualHex,
|
|
109
|
+
signStripePayload,
|
|
110
|
+
hmacSha256Hex,
|
|
111
|
+
createStripeWebhookHandler
|
|
112
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stripe `Stripe-Signature` verification — Web Crypto only.
|
|
3
|
+
*
|
|
4
|
+
* Lifted verbatim (where sound) from the proven commerce-bridge spike
|
|
5
|
+
* (`examples/astro-cf-demo/src/__tests__/spike/commerce-bridge.spike.test.ts`).
|
|
6
|
+
*
|
|
7
|
+
* Stripe's scheme: the header is `t=<unix-seconds>,v1=<hex-hmac>`; the signed
|
|
8
|
+
* payload is `${t}.${rawBody}`; the MAC is HMAC-SHA256 hex with the endpoint
|
|
9
|
+
* secret. A correct verifier also rejects timestamps outside a tolerance
|
|
10
|
+
* window (Stripe's default is 300s) — that is replay protection, so it is
|
|
11
|
+
* implemented here. No `stripe` SDK, no network: this is exactly the code a
|
|
12
|
+
* host adapter mounts in front of the saacms runtime.
|
|
13
|
+
*/
|
|
14
|
+
/** HMAC-SHA256 of `message` under `secret`, lowercase hex. */
|
|
15
|
+
export declare function hmacSha256Hex(secret: string, message: string): Promise<string>;
|
|
16
|
+
/** Length-checked constant-time hex compare (no early-out on first mismatch). */
|
|
17
|
+
export declare function timingSafeEqualHex(a: string, b: string): boolean;
|
|
18
|
+
/** Build a real `Stripe-Signature` header value for a raw body. */
|
|
19
|
+
export declare function signStripePayload(rawBody: string, secret: string, timestampSeconds: number): Promise<string>;
|
|
20
|
+
export type SigFailure = "missing-signature-header" | "malformed-signature-header" | "timestamp-out-of-tolerance" | "signature-mismatch";
|
|
21
|
+
export type SigResult = {
|
|
22
|
+
ok: true;
|
|
23
|
+
} | {
|
|
24
|
+
ok: false;
|
|
25
|
+
reason: SigFailure;
|
|
26
|
+
};
|
|
27
|
+
export declare function verifyStripeSignature(rawBody: string, header: string | undefined, secret: string, nowSeconds: number, toleranceSeconds?: number): Promise<SigResult>;
|
|
28
|
+
//# sourceMappingURL=signature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../src/signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,8DAA8D;AAC9D,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAOhE;AAED,mEAAmE;AACnE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,MAAM,MAAM,UAAU,GAClB,0BAA0B,GAC1B,4BAA4B,GAC5B,4BAA4B,GAC5B,oBAAoB,CAAA;AAExB,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAA;AAExE,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,gBAAgB,SAAM,GACrB,OAAO,CAAC,SAAS,CAAC,CAsBpB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createStripeWebhookHandler` — the reusable, host-mountable Stripe webhook
|
|
3
|
+
* handler that drives a saacms Collection create through the runtime.
|
|
4
|
+
*
|
|
5
|
+
* This productionises the commerce-bridge spike's proven seam
|
|
6
|
+
* (`docs/spikes/commerce-bridge.md`, GO verdict). The one spike CAVEAT — a
|
|
7
|
+
* non-atomic, host-side `?filter[stripeEventId]=` dedup pre-check — is
|
|
8
|
+
* **superseded** here: idempotency is now the ADR-0030 declarative-`unique`
|
|
9
|
+
* primitive (the target collection declares `unique: [["stripeEventId"]]`,
|
|
10
|
+
* the runtime returns `409` on a redelivery, this handler maps that to a
|
|
11
|
+
* `200` ack). There is no read-then-write pre-check anywhere in this module.
|
|
12
|
+
*
|
|
13
|
+
* Purity: the handler touches no global and opens no socket. The saacms
|
|
14
|
+
* runtime is injected as `opts.fetch` (the host passes `app.fetch`); the clock
|
|
15
|
+
* is injected as `opts.now`. It is therefore fully in-process testable.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The minimal structural shape this handler needs from a parsed Stripe event.
|
|
19
|
+
* Deliberately not the full `Stripe.Event` — no `stripe` SDK dependency. A
|
|
20
|
+
* real event carries far more; `mapEvent` reads whatever it needs off
|
|
21
|
+
* `data.object`.
|
|
22
|
+
*/
|
|
23
|
+
export interface StripeEventLike {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
readonly type: string;
|
|
26
|
+
readonly data?: {
|
|
27
|
+
readonly object?: Record<string, unknown>;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** What `mapEvent` returns to drive a create — or `null` to ignore the event. */
|
|
31
|
+
export interface MappedCreate {
|
|
32
|
+
/** Target collection slug. Falls back to `opts.collectionSlug` if empty. */
|
|
33
|
+
readonly collection: string;
|
|
34
|
+
/**
|
|
35
|
+
* The create body. MUST carry `stripeEventId` (Stripe's `event.id`) — the
|
|
36
|
+
* natural idempotency key the target collection declares
|
|
37
|
+
* `unique: [["stripeEventId"]]` on. If absent, the handler defensively
|
|
38
|
+
* injects `event.id`.
|
|
39
|
+
*/
|
|
40
|
+
readonly body: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export interface StripeWebhookOptions {
|
|
43
|
+
/**
|
|
44
|
+
* The injected saacms runtime. The host passes `app.fetch` from
|
|
45
|
+
* `createSaacmsRuntime(...)`. The handler never reaches the network itself.
|
|
46
|
+
*/
|
|
47
|
+
readonly fetch: (req: Request) => Promise<Response> | Response;
|
|
48
|
+
/** Conventional target collection slug (used when `mapEvent` returns no `collection`). */
|
|
49
|
+
readonly collectionSlug: string;
|
|
50
|
+
/** The Stripe endpoint signing secret (`whsec_...`). */
|
|
51
|
+
readonly signingSecret: string;
|
|
52
|
+
/**
|
|
53
|
+
* Map a verified, parsed Stripe event to a create. Return `null` for event
|
|
54
|
+
* types this endpoint does not handle (Stripe sends many types to one URL).
|
|
55
|
+
*/
|
|
56
|
+
readonly mapEvent: (event: StripeEventLike) => MappedCreate | null;
|
|
57
|
+
/** Replay/tolerance window in seconds. Stripe default is 300. */
|
|
58
|
+
readonly toleranceSeconds?: number;
|
|
59
|
+
/** Injected clock (unix seconds). Defaults to wallclock — override in tests. */
|
|
60
|
+
readonly now?: () => number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The handler's own outcome tag — surfaced in the JSON response body purely
|
|
64
|
+
* for host observability. Stripe itself only reads the HTTP status code.
|
|
65
|
+
*/
|
|
66
|
+
export type WebhookOutcome = "created" | "duplicate" | "ignored" | "signature-invalid" | "malformed-event" | "upstream-error";
|
|
67
|
+
/**
|
|
68
|
+
* Build the host-mountable `(Request) => Promise<Response>`.
|
|
69
|
+
*
|
|
70
|
+
* ── The load-bearing HTTP status contract (idempotency lives here) ──────────
|
|
71
|
+
*
|
|
72
|
+
* • Signature missing/malformed/stale/mismatched → **400** (no fetch, no
|
|
73
|
+
* write). A signature failure is permanent; Stripe must NOT retry.
|
|
74
|
+
* • Validly-signed body that is not JSON, or has no
|
|
75
|
+
* string `id`/`type` → **400** (no write).
|
|
76
|
+
* • `mapEvent` returns `null` (unhandled type) → **200** ack, no write
|
|
77
|
+
* (so Stripe stops retrying an event we ignore).
|
|
78
|
+
* • Runtime create returns 2xx → **200** (created).
|
|
79
|
+
* • Runtime create returns **409** (ADR-0030
|
|
80
|
+
* declarative-`unique` redelivery) → **200** ack
|
|
81
|
+
* ("duplicate"). The order already exists ⇒ this redelivery is an
|
|
82
|
+
* idempotent success ⇒ Stripe must NOT retry-storm. NO second row, NO
|
|
83
|
+
* second `afterChange` (the runtime never ran the event phase on 409).
|
|
84
|
+
* • Any other runtime status (5xx, validation, …) → a **5xx** so Stripe
|
|
85
|
+
* DOES retry (treated as transient). Distinct from the 409→200 path.
|
|
86
|
+
*/
|
|
87
|
+
export declare function createStripeWebhookHandler(opts: StripeWebhookOptions): (req: Request) => Promise<Response>;
|
|
88
|
+
//# sourceMappingURL=webhook-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../src/webhook-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAA;CAC9D;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAA;IAC9D,0FAA0F;IAC1F,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,YAAY,GAAG,IAAI,CAAA;IAClE,iEAAiE;IACjE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAClC,gFAAgF;IAChF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAC5B;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,WAAW,GACX,SAAS,GACT,mBAAmB,GACnB,iBAAiB,GACjB,gBAAgB,CAAA;AAcpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,oBAAoB,GACzB,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAgFrC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@saacms/stripe-bridge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"import": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc --build",
|
|
18
|
+
"typecheck": "tsc --build --noEmit",
|
|
19
|
+
"prepack": "cp package.json package.json.pack-bak && bun run ../../scripts/prepack-pkg.ts",
|
|
20
|
+
"postpack": "mv package.json.pack-bak package.json"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@saacms/core": "workspace:*"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@saacms/storage-libsql": "workspace:*",
|
|
30
|
+
"@types/bun": "latest",
|
|
31
|
+
"effect": "^3.10.0",
|
|
32
|
+
"typescript": "^5.7.0"
|
|
33
|
+
},
|
|
34
|
+
"main": "./dist/index.js",
|
|
35
|
+
"types": "./dist/index.d.ts"
|
|
36
|
+
}
|