@camada/sveltekit 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 +176 -0
- package/dist/index.cjs +115 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +25 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 camada
|
|
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,176 @@
|
|
|
1
|
+
# @camada/sveltekit
|
|
2
|
+
|
|
3
|
+
camada for [SvelteKit](https://svelte.dev/docs/kit) as a `handle` hook: enforces the tenant
|
|
4
|
+
snapshot inline (your ordered custom rules, then block, allow, challenge), serves a first-party
|
|
5
|
+
proof-of-work challenge page and beacon, records the outcomes your actions know (`track()`), and
|
|
6
|
+
ships wire events off the response path — through `waitUntil` on Cloudflare, in the background
|
|
7
|
+
on Node. Fails open by design — a camada outage or bug never 5xxes your app.
|
|
8
|
+
|
|
9
|
+
Not yet on npm — consumed via a `file:` dependency from a sibling checkout.
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
// src/hooks.server.ts
|
|
15
|
+
import { camada } from '@camada/sveltekit';
|
|
16
|
+
|
|
17
|
+
export const handle = camada(); // reads CAMADA_KEY / CAMADA_INGEST_URL / CAMADA_SNAPSHOT_URL from the env
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
With other hooks, put it first so a blocked request never reaches them:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { sequence } from '@sveltejs/kit/hooks';
|
|
24
|
+
export const handle = sequence(camada(), auth, i18n);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Env (printed by camada onboarding / `npm run seed` in dev):
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
CAMADA_KEY=<ingest_token>.<snap_token>
|
|
31
|
+
CAMADA_INGEST_URL=http://localhost:8787 # dev only; defaults to production ingest
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
On adapter-node that is `process.env`; on adapter-cloudflare it is the Worker bindings
|
|
35
|
+
(`event.platform.env`), read per request. Both are merged, and an app that reads its own config
|
|
36
|
+
can pass the values instead:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export const handle = camada({ key: MY_KEY, ingestUrl: MY_INGEST });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Call `camada()` once, at module scope. Engines are cached per resolved configuration inside the
|
|
43
|
+
handle, so a Worker isolate serving several tenants never enforces one tenant's snapshot on
|
|
44
|
+
another, nor signs its cookies with another's secret. Without `CAMADA_KEY` the handle is inert
|
|
45
|
+
(one log line, no requests, no enforcement), so an unprovisioned environment behaves exactly as
|
|
46
|
+
if camada were not installed.
|
|
47
|
+
|
|
48
|
+
## What it does per request
|
|
49
|
+
|
|
50
|
+
1. Refreshes the snapshot off-path (lazy mode by default — no interval timers, so the handle is
|
|
51
|
+
safe on edge and serverless adapters; `mode: 'timer'` polls on an unref'd interval for a
|
|
52
|
+
long-lived adapter-node process). Every poll and event batch carries
|
|
53
|
+
`x-camada-sdk: @camada/sveltekit/<version>`, and polls ask for snapshot v5 — the container
|
|
54
|
+
that carries your ordered custom rules.
|
|
55
|
+
2. Resolves the client from `event.getClientAddress()` — the address your adapter vouches for
|
|
56
|
+
(adapter-node honours its `ADDRESS_HEADER` / `XFF_DEPTH`, adapter-cloudflare reads
|
|
57
|
+
`cf-connecting-ip`) — falling back to `X-Forwarded-For` only under your tenant's
|
|
58
|
+
trusted-proxy config. A client header alone is never the ip. When the adapter has no address
|
|
59
|
+
(prerendering) the ip is null: ip rules and the challenge stand down, the event still ships —
|
|
60
|
+
so a `vite build` with prerendered routes ships one first-visit page event per route from the
|
|
61
|
+
build machine; set `CAMADA_DISABLED=1` in the build env to suppress them. A server-side
|
|
62
|
+
`event.fetch` to the app's own routes (`isSubRequest`) is left alone: one page view is one
|
|
63
|
+
event, and the page-level verdict is not re-applied to your own `load()`.
|
|
64
|
+
3. Runs your ordered custom rules, then the allow, block and challenge lists.
|
|
65
|
+
4. **Block** → `403` with `x-block-reason` before `resolve()`; the event still ships, with
|
|
66
|
+
`st: 403` and `blk: <reason>` so the analyst counts SDK blocks apart from your own 403s.
|
|
67
|
+
5. **Skip** → a skip rule or the allow list wins over a wider block.
|
|
68
|
+
6. **Challenge** → a `403` proof-of-work page (HTML navigations) or `403 {"error":"challenge_required"}`
|
|
69
|
+
(everything else); `POST /__camada/challenge` verifies the solution, sets `_cch` and 302s back.
|
|
70
|
+
7. Otherwise `resolve(event)` runs, and the settled response ships one batched, redacted event
|
|
71
|
+
with its real status (Authorization and Cookie values never leave the process; credential-looking
|
|
72
|
+
query values are scrubbed — see `@camada/core`).
|
|
73
|
+
|
|
74
|
+
A first visit gets the shared `_sfp` session cookie through `event.cookies.set()` — SvelteKit
|
|
75
|
+
adds the header to whatever `resolve()` returns, so a `redirect()` or a raw `Response` passes
|
|
76
|
+
through untouched.
|
|
77
|
+
|
|
78
|
+
## Options
|
|
79
|
+
|
|
80
|
+
| option | default | meaning |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `key` | `env.CAMADA_KEY` | `<ingest_token>.<snap_token>`; without it the handle is inert |
|
|
83
|
+
| `ingestUrl` | `env.CAMADA_INGEST_URL` | ingest base; batches go to `<ingestUrl>/e` |
|
|
84
|
+
| `snapshotUrl` | `<ingestUrl>/snapshot` | snapshot endpoint |
|
|
85
|
+
| `trustedProxy` | server config | `none` / `vercel` / `hops:N` / `cidrs:a,b`, or the parsed object |
|
|
86
|
+
| `challenge` | `true` | serve the proof-of-work page for `challenge` verdicts |
|
|
87
|
+
| `challengePath` | `/__camada/challenge` | where that page posts its solution |
|
|
88
|
+
| `snapshotVersion` | `5` | `4` drops the custom rules, `3` the allow/challenge sides too |
|
|
89
|
+
| `scriptPath` | `/_cam/b.js` | where the first-party beacon script is served |
|
|
90
|
+
| `fpPath` | `/_cam/fp` | where that script posts the beacon; keep it in `scriptPath`'s directory |
|
|
91
|
+
| `mode` | `lazy` (or `timer`) | `timer` polls the snapshot on an unref'd interval (long-lived process); `lazy` refreshes it per request off-path. `CAMADA_SERVERLESS=1` forces `lazy` |
|
|
92
|
+
| `env` | `process.env` + `platform.env` | overrides the host env (tests, and apps that read config themselves) |
|
|
93
|
+
|
|
94
|
+
`CAMADA_CHALLENGE=0` in the env switches the challenge off without a code change.
|
|
95
|
+
|
|
96
|
+
`CAMADA_DISABLED=1` in the env switches everything off, checked per request.
|
|
97
|
+
|
|
98
|
+
## The first-party beacon
|
|
99
|
+
|
|
100
|
+
Bots that never run JavaScript are the cheapest to catch. Return the tag from a server `load`
|
|
101
|
+
and put it in the page's `<head>`; the handle does the rest:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
// src/routes/+page.server.ts
|
|
105
|
+
import { scriptTag } from '@camada/sveltekit';
|
|
106
|
+
export const load = (event) => ({ camada: scriptTag(event) });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```svelte
|
|
110
|
+
<!-- src/routes/+page.svelte -->
|
|
111
|
+
<script>
|
|
112
|
+
let { data } = $props();
|
|
113
|
+
</script>
|
|
114
|
+
<svelte:head>{@html data.camada}</svelte:head>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`scriptTag(event)` returns `<script src="/_cam/b.js?r=<rid>" async></script>` — the `rid` is this
|
|
118
|
+
request's event id, so the analyst joins the beacon to the page view. The handle serves the
|
|
119
|
+
script at `GET /_cam/b.js` (cacheable, 1 h) and relays `POST /_cam/fp` (≤ 32 KB, answers 204)
|
|
120
|
+
onto the event batch as a `sig: 1` row stamped with the client ip camada resolved — never the
|
|
121
|
+
one the body claims. Both endpoints sit behind the verdict: a blocked client gets 403 there too.
|
|
122
|
+
The tag is `''` when camada is off for the request or the project turned the beacon off in its
|
|
123
|
+
settings, and the endpoints stand down with it.
|
|
124
|
+
|
|
125
|
+
## App-context events
|
|
126
|
+
|
|
127
|
+
The wire shows a `POST /login`; only your action knows whether it failed. Tell camada:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
// src/routes/login/+page.server.ts
|
|
131
|
+
import { fail, redirect } from '@sveltejs/kit';
|
|
132
|
+
import { track } from '@camada/sveltekit';
|
|
133
|
+
|
|
134
|
+
export const actions = {
|
|
135
|
+
default: async (event) => {
|
|
136
|
+
const form = await event.request.formData();
|
|
137
|
+
const ok = await signIn(form);
|
|
138
|
+
if (!ok) { track(event, 'login_failed', { user: String(form.get('email')) }); return fail(401); } // await optional
|
|
139
|
+
redirect(303, '/');
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`track(event, name, { user? })` ships `{ et, uid, rid, sid, ip, ts }` joined to this request's
|
|
145
|
+
event. The user identifier is HMAC-hashed in-process with the ingest token — the raw value never
|
|
146
|
+
leaves the process. It never throws and is a no-op where the handle did not run. The event name
|
|
147
|
+
is free-form; the analyst's rules read this vocabulary:
|
|
148
|
+
|
|
149
|
+
| event | when |
|
|
150
|
+
|---|---|
|
|
151
|
+
| `login_failed` / `login_succeeded` | a credential check settled |
|
|
152
|
+
| `signup` | an account was created |
|
|
153
|
+
| `password_reset` | a reset was requested |
|
|
154
|
+
| `mfa_failed` | a second factor was rejected |
|
|
155
|
+
| `payment_failed` / `payment_succeeded` | a charge settled |
|
|
156
|
+
| `coupon_failed` | a promo code was rejected |
|
|
157
|
+
|
|
158
|
+
## What this tap can see
|
|
159
|
+
|
|
160
|
+
This is the in-app position: the beacon, the client hints the browser sends, the real status
|
|
161
|
+
your routes answered, the `_sfp` session, and the app context `track()` adds. What SvelteKit
|
|
162
|
+
vouches for depends on the adapter. `getClientAddress()` is the one address the handle trusts;
|
|
163
|
+
on adapter-cloudflare `event.platform.cf` also supplies `asn`, `country`,
|
|
164
|
+
`tlsClientExtensionsSha1` and `httpProtocol`: the handle evaluates ASN, country and
|
|
165
|
+
TLS-fingerprint rules on them locally and stamps `asn`, `cc`, `tlsx` and the visitor's own
|
|
166
|
+
protocol on the event, but the analyst's capability mask for `sdk-sveltekit` does not credit
|
|
167
|
+
them (it is one mask for every adapter), so rules on those conditions are reported as not
|
|
168
|
+
enforceable by this SDK and the shipped facts are enrichment only. On adapter-node and the
|
|
169
|
+
other adapters none of those exist — the request arrives as a `Request` with no connection
|
|
170
|
+
facts, no client protocol, and headers already normalised — so those rules do not fire and the
|
|
171
|
+
analyst never scores their absence as evidence. No forwarded header is ever read for any of them.
|
|
172
|
+
|
|
173
|
+
## Fail open
|
|
174
|
+
|
|
175
|
+
Every entry point runs inside camada's guard. A dead ingest, a corrupt snapshot, a bug in this
|
|
176
|
+
package: telemetry is lost, the request is not.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name2 in all)
|
|
10
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
camada: () => camada,
|
|
34
|
+
resetCamada: () => resetCamada,
|
|
35
|
+
scriptTag: () => scriptTag,
|
|
36
|
+
track: () => track
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(index_exports);
|
|
39
|
+
|
|
40
|
+
// src/camada.ts
|
|
41
|
+
var import_core = require("@camada/core");
|
|
42
|
+
var import_fetch = require("@camada/core/fetch");
|
|
43
|
+
var import_iife_string = __toESM(require("@camada/browser/iife-string"), 1);
|
|
44
|
+
|
|
45
|
+
// package.json
|
|
46
|
+
var name = "@camada/sveltekit";
|
|
47
|
+
var version = "0.1.0";
|
|
48
|
+
|
|
49
|
+
// src/version.ts
|
|
50
|
+
var SDK_ID = `${name}/${version}`;
|
|
51
|
+
|
|
52
|
+
// src/camada.ts
|
|
53
|
+
var VAR = "__camada";
|
|
54
|
+
var instances = /* @__PURE__ */ new Set();
|
|
55
|
+
var slotOf = (event) => (0, import_core.guarded)(() => event.locals[VAR], void 0);
|
|
56
|
+
function contextOf(event) {
|
|
57
|
+
const platform = event.platform;
|
|
58
|
+
const env = (0, import_core.guarded)(() => ({ ...globalThis.process?.env, ...platform?.env }), {});
|
|
59
|
+
let peer = null;
|
|
60
|
+
try {
|
|
61
|
+
peer = event.getClientAddress();
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
const exec = platform?.context;
|
|
65
|
+
const cf = platform?.cf;
|
|
66
|
+
return {
|
|
67
|
+
peer,
|
|
68
|
+
env,
|
|
69
|
+
waitUntil: exec ? (p) => exec.waitUntil(p) : void 0,
|
|
70
|
+
asn: cf?.asn ?? null,
|
|
71
|
+
country: cf?.country ?? null,
|
|
72
|
+
tlsx: cf?.tlsClientExtensionsSha1 ?? null,
|
|
73
|
+
// The connection terminates at Cloudflare, so `cf.httpProtocol` is the visitor's own hop.
|
|
74
|
+
httpVersion: cf?.httpProtocol ? cf.httpProtocol.replace(/^HTTP\//i, "") : null
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function camada(opts = {}) {
|
|
78
|
+
const cam = (0, import_fetch.createFetchCamada)({ tap: import_core.TAP_SVELTEKIT, sdk: SDK_ID, iife: import_iife_string.default }, opts);
|
|
79
|
+
instances.add(cam);
|
|
80
|
+
return async ({ event, resolve }) => {
|
|
81
|
+
if (event.isSubRequest) return resolve(event);
|
|
82
|
+
const r = await cam.before(event.request, contextOf(event));
|
|
83
|
+
if (!r) return resolve(event);
|
|
84
|
+
if (r.response) return r.response;
|
|
85
|
+
const vars = r.vars;
|
|
86
|
+
(0, import_core.guarded)(() => {
|
|
87
|
+
event.locals[VAR] = vars;
|
|
88
|
+
if (vars.sessionCookie && vars.sid) {
|
|
89
|
+
event.cookies.set(import_fetch.SESSION_COOKIE, vars.sid, {
|
|
90
|
+
path: "/",
|
|
91
|
+
maxAge: import_fetch.SESSION_MAX_AGE,
|
|
92
|
+
httpOnly: true,
|
|
93
|
+
sameSite: "lax",
|
|
94
|
+
secure: new URL(event.request.url).protocol === "https:"
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}, void 0);
|
|
98
|
+
const res = await resolve(event);
|
|
99
|
+
cam.after(event.request, vars, res.status);
|
|
100
|
+
return res;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
var track = (event, et, data) => (0, import_fetch.track)(slotOf(event), et, data);
|
|
104
|
+
var scriptTag = (event) => (0, import_fetch.scriptTag)(slotOf(event));
|
|
105
|
+
function resetCamada() {
|
|
106
|
+
for (const cam of instances) cam.reset();
|
|
107
|
+
}
|
|
108
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
109
|
+
0 && (module.exports = {
|
|
110
|
+
camada,
|
|
111
|
+
resetCamada,
|
|
112
|
+
scriptTag,
|
|
113
|
+
track
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/camada.ts","../package.json","../src/version.ts"],"sourcesContent":["// @camada/sveltekit — the one-line install for a SvelteKit app, in src/hooks.server.ts:\n// export const handle = camada(); // env: CAMADA_KEY (+ CAMADA_INGEST_URL / CAMADA_SNAPSHOT_URL in dev)\n// scriptTag(event) // the first-party beacon, returned from a load() into the page\n// track(event, 'login_failed', { user }) // an outcome the wire cannot show\nexport { camada, track, scriptTag, resetCamada, type CamadaSvelteKitOptions, type CamadaSvelteKitVars } from './camada.js';\n","// @camada/sveltekit — the `handle` hook that binds @camada/core/fetch to a SvelteKit app. The\n// pipeline (verdict, block, challenge, beacon, the event) lives in core; this file only says what\n// SvelteKit knows: the adapter-resolved client address, the per-request env and execution context\n// an edge adapter hands over, the cookie API, and `event.locals` as the per-request slot.\nimport type { Handle, RequestEvent } from '@sveltejs/kit';\nimport { guarded, TAP_SVELTEKIT } from '@camada/core';\nimport { createFetchCamada, SESSION_COOKIE, SESSION_MAX_AGE, track as coreTrack, scriptTag as coreScriptTag, type FetchCamada, type FetchCamadaOptions, type FetchRequestContext, type FetchVars } from '@camada/core/fetch';\nimport iife from '@camada/browser/iife-string';\nimport { SDK_ID } from './version.js';\n\nexport type CamadaSvelteKitOptions = FetchCamadaOptions;\nexport type CamadaSvelteKitVars = FetchVars;\n\nconst VAR = '__camada'; // private: track() and scriptTag() are the API, not event.locals\nconst instances = new Set<FetchCamada>();\n\ntype Env = Record<string, string | undefined>;\n\n/** What adapter-cloudflare puts on `event.platform`; every field is optional because other adapters put nothing there. */\ninterface CfPlatform {\n env?: Env;\n context?: { waitUntil(p: Promise<unknown>): void };\n cf?: { asn?: number; country?: string; tlsClientExtensionsSha1?: string; httpProtocol?: string };\n}\n\nconst slotOf = (event: RequestEvent): FetchVars | undefined =>\n guarded(() => (event.locals as Record<string, unknown>)[VAR] as FetchVars | undefined, undefined);\n\n/** Everything the runtime vouches for. Nothing here is read from a client header. */\nfunction contextOf(event: RequestEvent): FetchRequestContext {\n const platform = event.platform as CfPlatform | undefined;\n // process.env on adapter-node (and nodejs_compat Workers), the bindings on adapter-cloudflare;\n // a binding can be a KV namespace, so the merge is guarded and core reads only CAMADA_* keys.\n const env = guarded<Env>(() => ({ ...(globalThis as { process?: { env?: Env } }).process?.env, ...platform?.env }), {});\n let peer: string | null = null;\n try { peer = event.getClientAddress(); } catch { /* prerendering, or an adapter with no socket: nothing vouched for */ }\n const exec = platform?.context;\n const cf = platform?.cf;\n return {\n peer, env,\n waitUntil: exec ? (p) => exec.waitUntil(p) : undefined,\n asn: cf?.asn ?? null,\n country: cf?.country ?? null,\n tlsx: cf?.tlsClientExtensionsSha1 ?? null,\n // The connection terminates at Cloudflare, so `cf.httpProtocol` is the visitor's own hop.\n httpVersion: cf?.httpProtocol ? cf.httpProtocol.replace(/^HTTP\\//i, '') : null,\n };\n}\n\n/**\n * The `handle` for `src/hooks.server.ts`: `export const handle = camada()`, or first in a\n * `sequence()`. Reads `CAMADA_KEY` (+ `CAMADA_INGEST_URL` / `CAMADA_SNAPSHOT_URL`) from\n * `process.env` or the Cloudflare bindings; inert without a key. One pipeline per call.\n */\nexport function camada(opts: CamadaSvelteKitOptions = {}): Handle {\n const cam = createFetchCamada({ tap: TAP_SVELTEKIT, sdk: SDK_ID, iife }, opts); // mode defaults to lazy in core: the handle may run on edge/serverless\n instances.add(cam);\n return async ({ event, resolve }) => {\n // A server-side `event.fetch` to the app's own routes runs the handle again on a fresh event\n // with no user-agent or accept: one page view is one event, and the page-level verdict must\n // not be re-applied to the server's own load().\n if (event.isSubRequest) return resolve(event);\n const r = await cam.before(event.request, contextOf(event));\n if (!r) return resolve(event);\n if (r.response) return r.response;\n const vars = r.vars;\n guarded(() => {\n (event.locals as Record<string, unknown>)[VAR] = vars;\n // The framework cookie API: SvelteKit adds the header to whatever resolve() returns, so a\n // redirect or an immutable Response needs no rebuilding here.\n if (vars.sessionCookie && vars.sid) {\n event.cookies.set(SESSION_COOKIE, vars.sid, {\n path: '/', maxAge: SESSION_MAX_AGE, httpOnly: true, sameSite: 'lax', secure: new URL(event.request.url).protocol === 'https:',\n });\n }\n }, undefined);\n const res = await resolve(event);\n cam.after(event.request, vars, res.status);\n return res;\n };\n}\n\n/**\n * Records an outcome the app knows and the wire cannot show (`login_failed`, `signup`,\n * `payment_failed`, ...), joined to this request's event by rid and session. The user identifier\n * is HMAC-hashed in-process. Never throws; a no-op where the handle did not run for this event.\n */\nexport const track = (event: RequestEvent, et: string, data?: { user?: string }): Promise<void> => coreTrack(slotOf(event), et, data);\n\n/** The beacon `<script>` tag for this request's page; `''` where the handle did not run or the tenant turned the beacon off. */\nexport const scriptTag = (event: RequestEvent): string => coreScriptTag(slotOf(event));\n\n/** Test/reset hook: stops and drops every engine of every handle this module created. */\nexport function resetCamada(): void {\n for (const cam of instances) cam.reset();\n}\n","{\n \"name\": \"@camada/sveltekit\",\n \"version\": \"0.1.0\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=20\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"check\": \"tsc --noEmit\"\n },\n \"description\": \"camada SDK for SvelteKit (hooks.server.ts handle): snapshot enforcement, first-party challenge and beacon, track() outcomes, waitUntil-flushed event shipping on Cloudflare\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"dependencies\": {\n \"@camada/core\": \"file:../camada-core\",\n \"@camada/browser\": \"file:../camada-browser\"\n },\n \"peerDependencies\": {\n \"@sveltejs/kit\": \">=2\"\n },\n \"devDependencies\": {\n \"@sveltejs/kit\": \"^2.0.0\",\n \"@types/node\": \"^22.10.0\",\n \"svelte\": \"^5.0.0\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.7.2\",\n \"vite\": \"^8.0.0\",\n \"vitest\": \"^3.0.0\"\n }\n}\n","// The SDK's wire identity (SDK-03): x-camada-sdk: <package>/<version>. Named JSON imports are\n// inlined and tree-shaken by tsup at build, so dist carries two literals — no runtime fs read.\nimport { name, version } from '../package.json';\nexport const SDK_ID = `${name}/${version}`;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,kBAAuC;AACvC,mBAAwM;AACxM,yBAAiB;;;ACNf,WAAQ;AACR,cAAW;;;ACCN,IAAM,SAAS,GAAG,IAAI,IAAI,OAAO;;;AFUxC,IAAM,MAAM;AACZ,IAAM,YAAY,oBAAI,IAAiB;AAWvC,IAAM,SAAS,CAAC,cACd,qBAAQ,MAAO,MAAM,OAAmC,GAAG,GAA4B,MAAS;AAGlG,SAAS,UAAU,OAA0C;AAC3D,QAAM,WAAW,MAAM;AAGvB,QAAM,UAAM,qBAAa,OAAO,EAAE,GAAI,WAA2C,SAAS,KAAK,GAAG,UAAU,IAAI,IAAI,CAAC,CAAC;AACtH,MAAI,OAAsB;AAC1B,MAAI;AAAE,WAAO,MAAM,iBAAiB;AAAA,EAAG,QAAQ;AAAA,EAAwE;AACvH,QAAM,OAAO,UAAU;AACvB,QAAM,KAAK,UAAU;AACrB,SAAO;AAAA,IACL;AAAA,IAAM;AAAA,IACN,WAAW,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,IAAI;AAAA,IAC7C,KAAK,IAAI,OAAO;AAAA,IAChB,SAAS,IAAI,WAAW;AAAA,IACxB,MAAM,IAAI,2BAA2B;AAAA;AAAA,IAErC,aAAa,IAAI,eAAe,GAAG,aAAa,QAAQ,YAAY,EAAE,IAAI;AAAA,EAC5E;AACF;AAOO,SAAS,OAAO,OAA+B,CAAC,GAAW;AAChE,QAAM,UAAM,gCAAkB,EAAE,KAAK,2BAAe,KAAK,QAAQ,yBAAAA,QAAK,GAAG,IAAI;AAC7E,YAAU,IAAI,GAAG;AACjB,SAAO,OAAO,EAAE,OAAO,QAAQ,MAAM;AAInC,QAAI,MAAM,aAAc,QAAO,QAAQ,KAAK;AAC5C,UAAM,IAAI,MAAM,IAAI,OAAO,MAAM,SAAS,UAAU,KAAK,CAAC;AAC1D,QAAI,CAAC,EAAG,QAAO,QAAQ,KAAK;AAC5B,QAAI,EAAE,SAAU,QAAO,EAAE;AACzB,UAAM,OAAO,EAAE;AACf,6BAAQ,MAAM;AACZ,MAAC,MAAM,OAAmC,GAAG,IAAI;AAGjD,UAAI,KAAK,iBAAiB,KAAK,KAAK;AAClC,cAAM,QAAQ,IAAI,6BAAgB,KAAK,KAAK;AAAA,UAC1C,MAAM;AAAA,UAAK,QAAQ;AAAA,UAAiB,UAAU;AAAA,UAAM,UAAU;AAAA,UAAO,QAAQ,IAAI,IAAI,MAAM,QAAQ,GAAG,EAAE,aAAa;AAAA,QACvH,CAAC;AAAA,MACH;AAAA,IACF,GAAG,MAAS;AACZ,UAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,QAAI,MAAM,MAAM,SAAS,MAAM,IAAI,MAAM;AACzC,WAAO;AAAA,EACT;AACF;AAOO,IAAM,QAAQ,CAAC,OAAqB,IAAY,aAA4C,aAAAC,OAAU,OAAO,KAAK,GAAG,IAAI,IAAI;AAG7H,IAAM,YAAY,CAAC,cAAgC,aAAAC,WAAc,OAAO,KAAK,CAAC;AAG9E,SAAS,cAAoB;AAClC,aAAW,OAAO,UAAW,KAAI,MAAM;AACzC;","names":["iife","coreTrack","coreScriptTag"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Handle, RequestEvent } from '@sveltejs/kit';
|
|
2
|
+
import { FetchCamadaOptions, FetchVars } from '@camada/core/fetch';
|
|
3
|
+
|
|
4
|
+
type CamadaSvelteKitOptions = FetchCamadaOptions;
|
|
5
|
+
type CamadaSvelteKitVars = FetchVars;
|
|
6
|
+
/**
|
|
7
|
+
* The `handle` for `src/hooks.server.ts`: `export const handle = camada()`, or first in a
|
|
8
|
+
* `sequence()`. Reads `CAMADA_KEY` (+ `CAMADA_INGEST_URL` / `CAMADA_SNAPSHOT_URL`) from
|
|
9
|
+
* `process.env` or the Cloudflare bindings; inert without a key. One pipeline per call.
|
|
10
|
+
*/
|
|
11
|
+
declare function camada(opts?: CamadaSvelteKitOptions): Handle;
|
|
12
|
+
/**
|
|
13
|
+
* Records an outcome the app knows and the wire cannot show (`login_failed`, `signup`,
|
|
14
|
+
* `payment_failed`, ...), joined to this request's event by rid and session. The user identifier
|
|
15
|
+
* is HMAC-hashed in-process. Never throws; a no-op where the handle did not run for this event.
|
|
16
|
+
*/
|
|
17
|
+
declare const track: (event: RequestEvent, et: string, data?: {
|
|
18
|
+
user?: string;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
/** The beacon `<script>` tag for this request's page; `''` where the handle did not run or the tenant turned the beacon off. */
|
|
21
|
+
declare const scriptTag: (event: RequestEvent) => string;
|
|
22
|
+
/** Test/reset hook: stops and drops every engine of every handle this module created. */
|
|
23
|
+
declare function resetCamada(): void;
|
|
24
|
+
|
|
25
|
+
export { type CamadaSvelteKitOptions, type CamadaSvelteKitVars, camada, resetCamada, scriptTag, track };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Handle, RequestEvent } from '@sveltejs/kit';
|
|
2
|
+
import { FetchCamadaOptions, FetchVars } from '@camada/core/fetch';
|
|
3
|
+
|
|
4
|
+
type CamadaSvelteKitOptions = FetchCamadaOptions;
|
|
5
|
+
type CamadaSvelteKitVars = FetchVars;
|
|
6
|
+
/**
|
|
7
|
+
* The `handle` for `src/hooks.server.ts`: `export const handle = camada()`, or first in a
|
|
8
|
+
* `sequence()`. Reads `CAMADA_KEY` (+ `CAMADA_INGEST_URL` / `CAMADA_SNAPSHOT_URL`) from
|
|
9
|
+
* `process.env` or the Cloudflare bindings; inert without a key. One pipeline per call.
|
|
10
|
+
*/
|
|
11
|
+
declare function camada(opts?: CamadaSvelteKitOptions): Handle;
|
|
12
|
+
/**
|
|
13
|
+
* Records an outcome the app knows and the wire cannot show (`login_failed`, `signup`,
|
|
14
|
+
* `payment_failed`, ...), joined to this request's event by rid and session. The user identifier
|
|
15
|
+
* is HMAC-hashed in-process. Never throws; a no-op where the handle did not run for this event.
|
|
16
|
+
*/
|
|
17
|
+
declare const track: (event: RequestEvent, et: string, data?: {
|
|
18
|
+
user?: string;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
/** The beacon `<script>` tag for this request's page; `''` where the handle did not run or the tenant turned the beacon off. */
|
|
21
|
+
declare const scriptTag: (event: RequestEvent) => string;
|
|
22
|
+
/** Test/reset hook: stops and drops every engine of every handle this module created. */
|
|
23
|
+
declare function resetCamada(): void;
|
|
24
|
+
|
|
25
|
+
export { type CamadaSvelteKitOptions, type CamadaSvelteKitVars, camada, resetCamada, scriptTag, track };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/camada.ts
|
|
2
|
+
import { guarded, TAP_SVELTEKIT } from "@camada/core";
|
|
3
|
+
import { createFetchCamada, SESSION_COOKIE, SESSION_MAX_AGE, track as coreTrack, scriptTag as coreScriptTag } from "@camada/core/fetch";
|
|
4
|
+
import iife from "@camada/browser/iife-string";
|
|
5
|
+
|
|
6
|
+
// package.json
|
|
7
|
+
var name = "@camada/sveltekit";
|
|
8
|
+
var version = "0.1.0";
|
|
9
|
+
|
|
10
|
+
// src/version.ts
|
|
11
|
+
var SDK_ID = `${name}/${version}`;
|
|
12
|
+
|
|
13
|
+
// src/camada.ts
|
|
14
|
+
var VAR = "__camada";
|
|
15
|
+
var instances = /* @__PURE__ */ new Set();
|
|
16
|
+
var slotOf = (event) => guarded(() => event.locals[VAR], void 0);
|
|
17
|
+
function contextOf(event) {
|
|
18
|
+
const platform = event.platform;
|
|
19
|
+
const env = guarded(() => ({ ...globalThis.process?.env, ...platform?.env }), {});
|
|
20
|
+
let peer = null;
|
|
21
|
+
try {
|
|
22
|
+
peer = event.getClientAddress();
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
const exec = platform?.context;
|
|
26
|
+
const cf = platform?.cf;
|
|
27
|
+
return {
|
|
28
|
+
peer,
|
|
29
|
+
env,
|
|
30
|
+
waitUntil: exec ? (p) => exec.waitUntil(p) : void 0,
|
|
31
|
+
asn: cf?.asn ?? null,
|
|
32
|
+
country: cf?.country ?? null,
|
|
33
|
+
tlsx: cf?.tlsClientExtensionsSha1 ?? null,
|
|
34
|
+
// The connection terminates at Cloudflare, so `cf.httpProtocol` is the visitor's own hop.
|
|
35
|
+
httpVersion: cf?.httpProtocol ? cf.httpProtocol.replace(/^HTTP\//i, "") : null
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function camada(opts = {}) {
|
|
39
|
+
const cam = createFetchCamada({ tap: TAP_SVELTEKIT, sdk: SDK_ID, iife }, opts);
|
|
40
|
+
instances.add(cam);
|
|
41
|
+
return async ({ event, resolve }) => {
|
|
42
|
+
if (event.isSubRequest) return resolve(event);
|
|
43
|
+
const r = await cam.before(event.request, contextOf(event));
|
|
44
|
+
if (!r) return resolve(event);
|
|
45
|
+
if (r.response) return r.response;
|
|
46
|
+
const vars = r.vars;
|
|
47
|
+
guarded(() => {
|
|
48
|
+
event.locals[VAR] = vars;
|
|
49
|
+
if (vars.sessionCookie && vars.sid) {
|
|
50
|
+
event.cookies.set(SESSION_COOKIE, vars.sid, {
|
|
51
|
+
path: "/",
|
|
52
|
+
maxAge: SESSION_MAX_AGE,
|
|
53
|
+
httpOnly: true,
|
|
54
|
+
sameSite: "lax",
|
|
55
|
+
secure: new URL(event.request.url).protocol === "https:"
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}, void 0);
|
|
59
|
+
const res = await resolve(event);
|
|
60
|
+
cam.after(event.request, vars, res.status);
|
|
61
|
+
return res;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
var track = (event, et, data) => coreTrack(slotOf(event), et, data);
|
|
65
|
+
var scriptTag = (event) => coreScriptTag(slotOf(event));
|
|
66
|
+
function resetCamada() {
|
|
67
|
+
for (const cam of instances) cam.reset();
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
camada,
|
|
71
|
+
resetCamada,
|
|
72
|
+
scriptTag,
|
|
73
|
+
track
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/camada.ts","../package.json","../src/version.ts"],"sourcesContent":["// @camada/sveltekit — the `handle` hook that binds @camada/core/fetch to a SvelteKit app. The\n// pipeline (verdict, block, challenge, beacon, the event) lives in core; this file only says what\n// SvelteKit knows: the adapter-resolved client address, the per-request env and execution context\n// an edge adapter hands over, the cookie API, and `event.locals` as the per-request slot.\nimport type { Handle, RequestEvent } from '@sveltejs/kit';\nimport { guarded, TAP_SVELTEKIT } from '@camada/core';\nimport { createFetchCamada, SESSION_COOKIE, SESSION_MAX_AGE, track as coreTrack, scriptTag as coreScriptTag, type FetchCamada, type FetchCamadaOptions, type FetchRequestContext, type FetchVars } from '@camada/core/fetch';\nimport iife from '@camada/browser/iife-string';\nimport { SDK_ID } from './version.js';\n\nexport type CamadaSvelteKitOptions = FetchCamadaOptions;\nexport type CamadaSvelteKitVars = FetchVars;\n\nconst VAR = '__camada'; // private: track() and scriptTag() are the API, not event.locals\nconst instances = new Set<FetchCamada>();\n\ntype Env = Record<string, string | undefined>;\n\n/** What adapter-cloudflare puts on `event.platform`; every field is optional because other adapters put nothing there. */\ninterface CfPlatform {\n env?: Env;\n context?: { waitUntil(p: Promise<unknown>): void };\n cf?: { asn?: number; country?: string; tlsClientExtensionsSha1?: string; httpProtocol?: string };\n}\n\nconst slotOf = (event: RequestEvent): FetchVars | undefined =>\n guarded(() => (event.locals as Record<string, unknown>)[VAR] as FetchVars | undefined, undefined);\n\n/** Everything the runtime vouches for. Nothing here is read from a client header. */\nfunction contextOf(event: RequestEvent): FetchRequestContext {\n const platform = event.platform as CfPlatform | undefined;\n // process.env on adapter-node (and nodejs_compat Workers), the bindings on adapter-cloudflare;\n // a binding can be a KV namespace, so the merge is guarded and core reads only CAMADA_* keys.\n const env = guarded<Env>(() => ({ ...(globalThis as { process?: { env?: Env } }).process?.env, ...platform?.env }), {});\n let peer: string | null = null;\n try { peer = event.getClientAddress(); } catch { /* prerendering, or an adapter with no socket: nothing vouched for */ }\n const exec = platform?.context;\n const cf = platform?.cf;\n return {\n peer, env,\n waitUntil: exec ? (p) => exec.waitUntil(p) : undefined,\n asn: cf?.asn ?? null,\n country: cf?.country ?? null,\n tlsx: cf?.tlsClientExtensionsSha1 ?? null,\n // The connection terminates at Cloudflare, so `cf.httpProtocol` is the visitor's own hop.\n httpVersion: cf?.httpProtocol ? cf.httpProtocol.replace(/^HTTP\\//i, '') : null,\n };\n}\n\n/**\n * The `handle` for `src/hooks.server.ts`: `export const handle = camada()`, or first in a\n * `sequence()`. Reads `CAMADA_KEY` (+ `CAMADA_INGEST_URL` / `CAMADA_SNAPSHOT_URL`) from\n * `process.env` or the Cloudflare bindings; inert without a key. One pipeline per call.\n */\nexport function camada(opts: CamadaSvelteKitOptions = {}): Handle {\n const cam = createFetchCamada({ tap: TAP_SVELTEKIT, sdk: SDK_ID, iife }, opts); // mode defaults to lazy in core: the handle may run on edge/serverless\n instances.add(cam);\n return async ({ event, resolve }) => {\n // A server-side `event.fetch` to the app's own routes runs the handle again on a fresh event\n // with no user-agent or accept: one page view is one event, and the page-level verdict must\n // not be re-applied to the server's own load().\n if (event.isSubRequest) return resolve(event);\n const r = await cam.before(event.request, contextOf(event));\n if (!r) return resolve(event);\n if (r.response) return r.response;\n const vars = r.vars;\n guarded(() => {\n (event.locals as Record<string, unknown>)[VAR] = vars;\n // The framework cookie API: SvelteKit adds the header to whatever resolve() returns, so a\n // redirect or an immutable Response needs no rebuilding here.\n if (vars.sessionCookie && vars.sid) {\n event.cookies.set(SESSION_COOKIE, vars.sid, {\n path: '/', maxAge: SESSION_MAX_AGE, httpOnly: true, sameSite: 'lax', secure: new URL(event.request.url).protocol === 'https:',\n });\n }\n }, undefined);\n const res = await resolve(event);\n cam.after(event.request, vars, res.status);\n return res;\n };\n}\n\n/**\n * Records an outcome the app knows and the wire cannot show (`login_failed`, `signup`,\n * `payment_failed`, ...), joined to this request's event by rid and session. The user identifier\n * is HMAC-hashed in-process. Never throws; a no-op where the handle did not run for this event.\n */\nexport const track = (event: RequestEvent, et: string, data?: { user?: string }): Promise<void> => coreTrack(slotOf(event), et, data);\n\n/** The beacon `<script>` tag for this request's page; `''` where the handle did not run or the tenant turned the beacon off. */\nexport const scriptTag = (event: RequestEvent): string => coreScriptTag(slotOf(event));\n\n/** Test/reset hook: stops and drops every engine of every handle this module created. */\nexport function resetCamada(): void {\n for (const cam of instances) cam.reset();\n}\n","{\n \"name\": \"@camada/sveltekit\",\n \"version\": \"0.1.0\",\n \"license\": \"MIT\",\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=20\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"check\": \"tsc --noEmit\"\n },\n \"description\": \"camada SDK for SvelteKit (hooks.server.ts handle): snapshot enforcement, first-party challenge and beacon, track() outcomes, waitUntil-flushed event shipping on Cloudflare\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"dependencies\": {\n \"@camada/core\": \"file:../camada-core\",\n \"@camada/browser\": \"file:../camada-browser\"\n },\n \"peerDependencies\": {\n \"@sveltejs/kit\": \">=2\"\n },\n \"devDependencies\": {\n \"@sveltejs/kit\": \"^2.0.0\",\n \"@types/node\": \"^22.10.0\",\n \"svelte\": \"^5.0.0\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.7.2\",\n \"vite\": \"^8.0.0\",\n \"vitest\": \"^3.0.0\"\n }\n}\n","// The SDK's wire identity (SDK-03): x-camada-sdk: <package>/<version>. Named JSON imports are\n// inlined and tree-shaken by tsup at build, so dist carries two literals — no runtime fs read.\nimport { name, version } from '../package.json';\nexport const SDK_ID = `${name}/${version}`;\n"],"mappings":";AAKA,SAAS,SAAS,qBAAqB;AACvC,SAAS,mBAAmB,gBAAgB,iBAAiB,SAAS,WAAW,aAAa,qBAA0G;AACxM,OAAO,UAAU;;;ACNf,WAAQ;AACR,cAAW;;;ACCN,IAAM,SAAS,GAAG,IAAI,IAAI,OAAO;;;AFUxC,IAAM,MAAM;AACZ,IAAM,YAAY,oBAAI,IAAiB;AAWvC,IAAM,SAAS,CAAC,UACd,QAAQ,MAAO,MAAM,OAAmC,GAAG,GAA4B,MAAS;AAGlG,SAAS,UAAU,OAA0C;AAC3D,QAAM,WAAW,MAAM;AAGvB,QAAM,MAAM,QAAa,OAAO,EAAE,GAAI,WAA2C,SAAS,KAAK,GAAG,UAAU,IAAI,IAAI,CAAC,CAAC;AACtH,MAAI,OAAsB;AAC1B,MAAI;AAAE,WAAO,MAAM,iBAAiB;AAAA,EAAG,QAAQ;AAAA,EAAwE;AACvH,QAAM,OAAO,UAAU;AACvB,QAAM,KAAK,UAAU;AACrB,SAAO;AAAA,IACL;AAAA,IAAM;AAAA,IACN,WAAW,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,IAAI;AAAA,IAC7C,KAAK,IAAI,OAAO;AAAA,IAChB,SAAS,IAAI,WAAW;AAAA,IACxB,MAAM,IAAI,2BAA2B;AAAA;AAAA,IAErC,aAAa,IAAI,eAAe,GAAG,aAAa,QAAQ,YAAY,EAAE,IAAI;AAAA,EAC5E;AACF;AAOO,SAAS,OAAO,OAA+B,CAAC,GAAW;AAChE,QAAM,MAAM,kBAAkB,EAAE,KAAK,eAAe,KAAK,QAAQ,KAAK,GAAG,IAAI;AAC7E,YAAU,IAAI,GAAG;AACjB,SAAO,OAAO,EAAE,OAAO,QAAQ,MAAM;AAInC,QAAI,MAAM,aAAc,QAAO,QAAQ,KAAK;AAC5C,UAAM,IAAI,MAAM,IAAI,OAAO,MAAM,SAAS,UAAU,KAAK,CAAC;AAC1D,QAAI,CAAC,EAAG,QAAO,QAAQ,KAAK;AAC5B,QAAI,EAAE,SAAU,QAAO,EAAE;AACzB,UAAM,OAAO,EAAE;AACf,YAAQ,MAAM;AACZ,MAAC,MAAM,OAAmC,GAAG,IAAI;AAGjD,UAAI,KAAK,iBAAiB,KAAK,KAAK;AAClC,cAAM,QAAQ,IAAI,gBAAgB,KAAK,KAAK;AAAA,UAC1C,MAAM;AAAA,UAAK,QAAQ;AAAA,UAAiB,UAAU;AAAA,UAAM,UAAU;AAAA,UAAO,QAAQ,IAAI,IAAI,MAAM,QAAQ,GAAG,EAAE,aAAa;AAAA,QACvH,CAAC;AAAA,MACH;AAAA,IACF,GAAG,MAAS;AACZ,UAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,QAAI,MAAM,MAAM,SAAS,MAAM,IAAI,MAAM;AACzC,WAAO;AAAA,EACT;AACF;AAOO,IAAM,QAAQ,CAAC,OAAqB,IAAY,SAA4C,UAAU,OAAO,KAAK,GAAG,IAAI,IAAI;AAG7H,IAAM,YAAY,CAAC,UAAgC,cAAc,OAAO,KAAK,CAAC;AAG9E,SAAS,cAAoB;AAClC,aAAW,OAAO,UAAW,KAAI,MAAM;AACzC;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@camada/sveltekit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"description": "camada SDK for SvelteKit (hooks.server.ts handle): snapshot enforcement, first-party challenge and beacon, track() outcomes, waitUntil-flushed event shipping on Cloudflare",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"require": "./dist/index.cjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.cjs",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@camada/core": "^0.3.0",
|
|
27
|
+
"@camada/browser": "^0.2.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@sveltejs/kit": ">=2"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/camada-app/camada-sveltekit.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/camada-app/camada-sveltekit#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/camada-app/camada-sveltekit/issues"
|
|
39
|
+
}
|
|
40
|
+
}
|