@allstak/nuxt 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/CHANGELOG.md ADDED
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@allstak/nuxt` are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] — 2026-05-29
9
+
10
+ Initial release of the official AllStak SDK for Nuxt 3 / Nuxt 4, shipped as a
11
+ Nuxt module built with `@nuxt/module-builder`.
12
+
13
+ ### Added
14
+
15
+ - `@allstak/nuxt` Nuxt module (`defineNuxtModule`, config key `allstak`,
16
+ compatibility `nuxt >= 3.7`, Nuxt 4 supported). Register it in
17
+ `nuxt.config.ts` `modules: ['@allstak/nuxt']`.
18
+ - Client runtime plugin (registered via `addPlugin`, `mode: 'client'`,
19
+ `order: 0`) that:
20
+ - delegates init and the Vue-side glue to `@allstak/vue`'s `AllStakPlugin`,
21
+ stamping the wrapper identity (`sdkName: 'allstak-nuxt'`, `sdkVersion`) so
22
+ backend ingest can distinguish Nuxt traffic;
23
+ - sets `attachErrorHandler: false` so it does not hijack
24
+ `app.config.errorHandler` — error capture is driven through Nuxt hooks;
25
+ - captures uncaught client errors via the `vue:error` and `app:error` Nuxt
26
+ hooks through a shared `reportNuxtError` helper, filtering expected
27
+ 3xx/4xx statuses;
28
+ - opens a `navigation` span and breadcrumb per route change (`page:start` →
29
+ `page:finish`), guarded by a tree-shakeable tracing flag.
30
+ - Nitro server plugin (registered via `addServerPlugin`) that:
31
+ - calls `AllStak.init` on the server, reading `runtimeConfig.public.allstak`
32
+ with a `process.env` fallback, stamped with the Nuxt SDK identity;
33
+ - captures unhandled server errors via the Nitro `error` hook with the H3
34
+ event context, ignoring expected 3xx/4xx;
35
+ - wraps every request in an `http.server` span (`request` → finished on
36
+ `beforeResponse` / `afterResponse` with the response status);
37
+ - injects trace meta tags into the SSR `<head>` via `render:html` for
38
+ client↔server trace continuity, skipped for pre-rendered / SWR-cached
39
+ responses.
40
+ - Module options: `enabled`, `client`, `server`, `injectTraceMetaTags`,
41
+ `autoInjectServerAllStak` (`'plugin'` | `'top-level-import'` |
42
+ `'experimental_dynamic-import'`), `experimental_entrypointWrappedFunctions`,
43
+ `sourceMapsUploadOptions`, and `debug`.
44
+ - `client.config` / `server.config`-equivalent init configuration via the
45
+ `allstak.client` / `allstak.server` module options (sampling, filters, and
46
+ every option `AllStak.init` accepts).
47
+ - `runtimeConfig.public.allstak` read for `apiKey` / `host` / `environment` /
48
+ `release` / `dist`, with sensible defaults seeded by the module.
49
+ - Client surface (`@allstak/nuxt/client`) re-exporting the full `@allstak/vue`
50
+ API; server surface (`@allstak/nuxt/server`) re-exporting the full
51
+ `@allstak/js` API. Both export `SDK_NAME` and `SDK_VERSION`.
52
+ - The SDK version is injected at build time from `package.json`
53
+ (`__ALLSTAK_NUXT_VERSION__`), never hand-written, so it cannot drift.
54
+
55
+ [0.1.0]: https://github.com/AllStak/allstak-nuxt/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 AllStak
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,108 @@
1
+ # @allstak/nuxt
2
+
3
+ [![npm](https://img.shields.io/npm/v/@allstak/nuxt.svg)](https://www.npmjs.com/package/@allstak/nuxt)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+
6
+ Official AllStak SDK for **Nuxt 3 / Nuxt 4**, shipped as a Nuxt module. Captures uncaught errors on the client and the Nitro server, wraps every server request in a span, opens navigation spans on route changes, stitches client↔server traces through SSR meta tags, and surfaces the full AllStak observability API on both runtimes.
7
+
8
+ Built on `@allstak/vue` (client) and `@allstak/js` (server).
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @allstak/nuxt
14
+ # or
15
+ pnpm add @allstak/nuxt
16
+ # or
17
+ yarn add @allstak/nuxt
18
+ ```
19
+
20
+ `nuxt@^3.7 || ^4` is a peer dependency. Nuxt >= 3.14 is recommended.
21
+
22
+ ## Setup
23
+
24
+ Register the module and provide your ingest key through `runtimeConfig`:
25
+
26
+ ```ts
27
+ // nuxt.config.ts
28
+ export default defineNuxtConfig({
29
+ modules: ['@allstak/nuxt'],
30
+
31
+ runtimeConfig: {
32
+ public: {
33
+ allstak: {
34
+ apiKey: process.env.ALLSTAK_API_KEY,
35
+ environment: process.env.NODE_ENV,
36
+ release: process.env.APP_VERSION,
37
+ },
38
+ },
39
+ },
40
+
41
+ // Optional — client.config / server.config-equivalent options:
42
+ allstak: {
43
+ client: { tracesSampleRate: 1.0 },
44
+ server: { tracesSampleRate: 1.0 },
45
+ },
46
+ });
47
+ ```
48
+
49
+ That single entry wires:
50
+
51
+ - **Client** — delegates init and the Vue glue to `@allstak/vue`'s plugin (with `attachErrorHandler: false` so Nuxt's own error handling isn't hijacked), captures errors via the `vue:error` and `app:error` hooks, and opens a `navigation` span per route change.
52
+ - **Server (Nitro)** — calls `AllStak.init` on the server, captures unhandled errors via the Nitro `error` hook, wraps each request in an `http.server` span, and injects trace meta tags into the SSR `<head>`.
53
+ - Reads `runtimeConfig.public.allstak` for `apiKey` / `host` / `environment` / `release` / `dist` so secrets never have to live in `nuxt.config`.
54
+
55
+ ## Using the API
56
+
57
+ The full client API is available from `@allstak/nuxt/client`, and the full server API from `@allstak/nuxt/server`:
58
+
59
+ ```ts
60
+ // A client component
61
+ import { AllStak, useAllStak } from '@allstak/nuxt/client';
62
+
63
+ const allstak = useAllStak();
64
+ allstak.captureException(new Error('payment declined'));
65
+ ```
66
+
67
+ ```ts
68
+ // server/api/orders.post.ts
69
+ import { AllStak } from '@allstak/nuxt/server';
70
+
71
+ export default defineEventHandler(async (event) => {
72
+ AllStak.addBreadcrumb({ type: 'order', message: 'creating order' });
73
+ // ...
74
+ });
75
+ ```
76
+
77
+ ## Module options
78
+
79
+ Configured under the `allstak` key in `nuxt.config.ts`:
80
+
81
+ | Option | Default | Purpose |
82
+ |---|---|---|
83
+ | `enabled` | `true` | Master switch. `false` registers nothing. |
84
+ | `client` | `true` | Wire the client plugin. Pass an object to set client init config (sampling, filters, …). |
85
+ | `server` | `true` | Wire the Nitro server plugin. Pass an object to set server init config. |
86
+ | `injectTraceMetaTags` | `true` | Inject SSR trace meta tags for client↔server trace continuity (skipped for pre-render / SWR). |
87
+ | `autoInjectServerAllStak` | `'plugin'` | Server-init strategy: `'plugin'`, `'top-level-import'`, or `'experimental_dynamic-import'`. |
88
+ | `experimental_entrypointWrappedFunctions` | `['default','handler','server']` | Entry functions wrapped by the dynamic-import strategy. |
89
+ | `sourceMapsUploadOptions` | — | Source-map upload config (`org`, `project`, `authToken`, `release`, `sourcemaps`). Enables `sourcemap.client: 'hidden'`. |
90
+ | `debug` | `false` | Verbose module + runtime logging. |
91
+
92
+ ### Public runtime config
93
+
94
+ The browser-safe ingest config lives under `runtimeConfig.public.allstak`:
95
+
96
+ | Field | Purpose |
97
+ |---|---|
98
+ | `apiKey` | Publishable ingest key. |
99
+ | `host` | Override the AllStak ingest host. |
100
+ | `environment` | Deployment environment (e.g. `production`). |
101
+ | `release` | Release identifier for grouping. |
102
+ | `dist` | Build distribution identifier. |
103
+
104
+ The server plugin also reads `ALLSTAK_API_KEY` / `ALLSTAK_HOST` / `APP_VERSION` from `process.env` as a fallback for the build-artifact and top-level-import server-init strategies, where runtime config may not be assembled yet.
105
+
106
+ ## License
107
+
108
+ MIT
@@ -0,0 +1,156 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+ import { AllStakConfig } from '@allstak/js';
3
+
4
+ /**
5
+ * Strategy for getting the server-side AllStak init to run. The Nitro server
6
+ * boots before Nuxt's runtime config is fully assembled, so the server SDK is
7
+ * configured from `runtimeConfig.public.allstak` (read inside the server
8
+ * plugin) and these strategies control how/when the server plugin is wired:
9
+ *
10
+ * - `'plugin'` (default): register a Nitro plugin via `addServerPlugin()`.
11
+ * The plugin calls `AllStak.init` lazily on the first server request /
12
+ * Nitro `error` hook. This is the portable default that works on every
13
+ * Nitro preset (node-server, edge, serverless).
14
+ * - `'top-level-import'`: in addition to the plugin, the module can be
15
+ * instructed (by the host build) to import the server config at the top
16
+ * of the Nitro entry. Surfaced for forward compatibility.
17
+ * - `'experimental_dynamic-import'`: wrap the Nitro entrypoint functions so
18
+ * the server SDK is imported dynamically before the handler runs.
19
+ */
20
+ type AutoInjectServerStrategy = 'plugin' | 'top-level-import' | 'experimental_dynamic-import';
21
+ /**
22
+ * Source-map upload options surfaced for the build-integration
23
+ * story. Upload itself is performed by `@allstak/js`'s source-map tooling /
24
+ * the AllStak CLI against the AllStak source-map ingest endpoint; the module
25
+ * threads these values through so a host can configure them in one place.
26
+ */
27
+ interface SourceMapsUploadOptions {
28
+ /** Enable source-map upload during production build. Default: `false`. */
29
+ enabled?: boolean;
30
+ /** AllStak organisation slug. */
31
+ org?: string;
32
+ /** AllStak project slug. */
33
+ project?: string;
34
+ /** Upload auth token. Prefer an env var over committing this. */
35
+ authToken?: string;
36
+ /** Override the upload endpoint (defaults to the configured AllStak host). */
37
+ url?: string;
38
+ /** Suppress upload logging. Default: `false`. */
39
+ silent?: boolean;
40
+ /** Release name the maps are associated with. */
41
+ release?: {
42
+ name?: string;
43
+ };
44
+ /** Which emitted assets to scan, ignore, and clean up after upload. */
45
+ sourcemaps?: {
46
+ assets?: string | string[];
47
+ ignore?: string | string[];
48
+ filesToDeleteAfterUpload?: string | string[];
49
+ };
50
+ }
51
+ /**
52
+ * `@allstak/nuxt` module options. Configured under the `allstak` key in
53
+ * `nuxt.config.ts`:
54
+ *
55
+ * export default defineNuxtConfig({
56
+ * modules: ['@allstak/nuxt'],
57
+ * allstak: {
58
+ * enabled: true,
59
+ * client: { tracesSampleRate: 1.0 },
60
+ * server: { tracesSampleRate: 1.0 },
61
+ * },
62
+ * runtimeConfig: {
63
+ * public: {
64
+ * allstak: {
65
+ * apiKey: process.env.ALLSTAK_API_KEY,
66
+ * environment: process.env.NODE_ENV,
67
+ * release: process.env.APP_VERSION,
68
+ * },
69
+ * },
70
+ * },
71
+ * });
72
+ */
73
+ interface AllStakModuleOptions {
74
+ /** Master switch. When `false` the module registers nothing. Default: `true`. */
75
+ enabled?: boolean;
76
+ /**
77
+ * Wire the client runtime plugin (error capture + router/navigation spans).
78
+ * Default: `true`.
79
+ */
80
+ client?: boolean | AllStakClientConfig;
81
+ /**
82
+ * Wire the Nitro server plugin (unhandled-error capture + per-request span +
83
+ * SSR trace meta-tag injection). Default: `true`.
84
+ */
85
+ server?: boolean | AllStakServerConfig;
86
+ /**
87
+ * Inject `<meta name="allstak-trace">` / `<meta name="baggage">` tags into
88
+ * the SSR HTML head so the client can continue the server's trace. Default:
89
+ * `true`. Skipped automatically for pre-rendered / SWR-cached responses.
90
+ */
91
+ injectTraceMetaTags?: boolean;
92
+ /** Server-init injection strategy. Default: `'plugin'`. */
93
+ autoInjectServerAllStak?: AutoInjectServerStrategy;
94
+ /**
95
+ * Entry function names wrapped by the `'experimental_dynamic-import'`
96
+ * strategy. Default: `['default', 'handler', 'server']`.
97
+ */
98
+ experimental_entrypointWrappedFunctions?: string[];
99
+ /** Source-map upload configuration (build-time). */
100
+ sourceMapsUploadOptions?: SourceMapsUploadOptions;
101
+ /** Emit verbose module + runtime logging. Default: `false`. */
102
+ debug?: boolean;
103
+ }
104
+ /**
105
+ * Client-side init config. A subset/passthrough of `AllStakConfig` so a host
106
+ * can set sampling, filters, and tracing in `nuxt.config.ts`. Secrets
107
+ * (`apiKey`) should live in `runtimeConfig.public.allstak`, not here.
108
+ */
109
+ type AllStakClientConfig = Partial<AllStakConfig>;
110
+ /** Server-side init config. Same passthrough shape as the client. */
111
+ type AllStakServerConfig = Partial<AllStakConfig>;
112
+ /**
113
+ * Shape the module writes into `runtimeConfig.public.allstak`. Only the public
114
+ * fields belong here — they ship to the browser. The `apiKey` is a publishable
115
+ * ingest key (the same key the other browser SDKs use), not a server secret.
116
+ */
117
+ interface AllStakPublicRuntimeConfig {
118
+ apiKey?: string;
119
+ host?: string;
120
+ environment?: string;
121
+ release?: string;
122
+ dist?: string;
123
+ }
124
+
125
+ declare const SDK_NAME = "allstak-nuxt";
126
+ declare const SDK_VERSION: string;
127
+ /**
128
+ * `@allstak/nuxt` — the official AllStak Nuxt module.
129
+ *
130
+ * Wiring it is a one-liner in `nuxt.config.ts`:
131
+ *
132
+ * export default defineNuxtConfig({
133
+ * modules: ['@allstak/nuxt'],
134
+ * runtimeConfig: {
135
+ * public: {
136
+ * allstak: { apiKey: process.env.ALLSTAK_API_KEY },
137
+ * },
138
+ * },
139
+ * });
140
+ *
141
+ * The module:
142
+ * - registers a client plugin that delegates to `@allstak/vue`'s
143
+ * `AllStakPlugin` (init + Vue glue) and wires Nuxt error hooks + router
144
+ * navigation spans;
145
+ * - registers a Nitro server plugin that calls `AllStak.init` on the server,
146
+ * captures unhandled server errors, wraps every request in a span, and
147
+ * injects SSR trace meta tags;
148
+ * - reads `runtimeConfig.public.allstak` for `apiKey`/`environment`/`release`
149
+ * /`host`/`dist` so secrets never have to live in `nuxt.config`;
150
+ * - exposes split client/server configuration through the
151
+ * `allstak.client` / `allstak.server` module options.
152
+ */
153
+ declare const _default: _nuxt_schema.NuxtModule<AllStakModuleOptions, AllStakModuleOptions, false>;
154
+
155
+ export { SDK_NAME, SDK_VERSION, _default as default };
156
+ export type { AllStakClientConfig, AllStakModuleOptions, AllStakPublicRuntimeConfig, AllStakServerConfig, AutoInjectServerStrategy, SourceMapsUploadOptions };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@allstak/nuxt",
3
+ "version": "0.1.0",
4
+ "configKey": "allstak",
5
+ "compatibility": {
6
+ "nuxt": ">=3.7.0"
7
+ },
8
+ "builder": {
9
+ "@nuxt/module-builder": "1.0.2",
10
+ "unbuild": "3.6.1"
11
+ }
12
+ }
@@ -0,0 +1,104 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { defineNuxtModule, useLogger, createResolver, addPlugin, addServerPlugin, addTemplate } from '@nuxt/kit';
3
+
4
+ const name = "@allstak/nuxt";
5
+ const version = "0.1.0";
6
+
7
+ const SDK_NAME = "allstak-nuxt";
8
+ const SDK_VERSION = typeof __ALLSTAK_NUXT_VERSION__ === "string" ? __ALLSTAK_NUXT_VERSION__ : version;
9
+ const module$1 = defineNuxtModule({
10
+ meta: {
11
+ name: name,
12
+ version: version,
13
+ configKey: "allstak",
14
+ compatibility: {
15
+ // Hooks/APIs used (nuxtApp.hook('vue:error'), addServerPlugin, render:html)
16
+ // are stable from 3.7; recommend >= 3.14. Nuxt 4 supported.
17
+ nuxt: ">=3.7.0"
18
+ }
19
+ },
20
+ defaults: {
21
+ enabled: true,
22
+ client: true,
23
+ server: true,
24
+ injectTraceMetaTags: true,
25
+ autoInjectServerAllStak: "plugin",
26
+ experimental_entrypointWrappedFunctions: ["default", "handler", "server"],
27
+ debug: false
28
+ },
29
+ setup(options, nuxt) {
30
+ const logger = useLogger(name);
31
+ if (options.enabled === false) {
32
+ if (options.debug) logger.info("disabled via `allstak.enabled: false` \u2014 skipping setup");
33
+ return;
34
+ }
35
+ const resolver = createResolver(import.meta.url);
36
+ const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
37
+ nuxt.options.build.transpile.push(runtimeDir);
38
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
39
+ const existingPublic = nuxt.options.runtimeConfig.public.allstak;
40
+ nuxt.options.runtimeConfig.public.allstak = {
41
+ apiKey: "",
42
+ ...existingPublic ?? {}
43
+ };
44
+ const clientConfig = typeof options.client === "object" ? options.client : {};
45
+ const serverConfig = typeof options.server === "object" ? options.server : {};
46
+ const defines = {
47
+ __ALLSTAK_NUXT_VERSION__: JSON.stringify(version),
48
+ __ALLSTAK_CLIENT_CONFIG__: JSON.stringify(clientConfig),
49
+ __ALLSTAK_SERVER_CONFIG__: JSON.stringify(serverConfig),
50
+ __ALLSTAK_INJECT_META__: JSON.stringify(options.injectTraceMetaTags !== false)
51
+ };
52
+ nuxt.options.vite = nuxt.options.vite || {};
53
+ nuxt.options.vite.define = { ...nuxt.options.vite.define ?? {}, ...defines };
54
+ nuxt.options.nitro = nuxt.options.nitro || {};
55
+ nuxt.options.nitro.replace = { ...nuxt.options.nitro.replace ?? {}, ...defines };
56
+ if (options.client !== false) {
57
+ addPlugin({
58
+ src: resolver.resolve("./runtime/plugins/allstak.client"),
59
+ mode: "client",
60
+ order: 0
61
+ });
62
+ if (options.debug) logger.info("registered client plugin");
63
+ }
64
+ if (options.server !== false) {
65
+ const strategy = options.autoInjectServerAllStak ?? "plugin";
66
+ addServerPlugin(resolver.resolve("./runtime/server/plugin"));
67
+ if (strategy === "top-level-import") {
68
+ nuxt.options.nitro.plugins = nuxt.options.nitro.plugins || [];
69
+ nuxt.options.nitro.plugins.push(resolver.resolve("./runtime/server/plugin"));
70
+ if (options.debug) logger.info("server strategy 'top-level-import' wired");
71
+ } else if (strategy === "experimental_dynamic-import") {
72
+ nuxt.options.nitro.virtual = nuxt.options.nitro.virtual || {};
73
+ const wrapped = options.experimental_entrypointWrappedFunctions ?? ["default", "handler", "server"];
74
+ nuxt.options.nitro.virtual["#allstak-entrypoint-functions"] = `export const wrapped = ${JSON.stringify(wrapped)};`;
75
+ if (options.debug) {
76
+ logger.info(
77
+ `server strategy 'experimental_dynamic-import' wrapping: ${wrapped.join(", ")}`
78
+ );
79
+ }
80
+ }
81
+ if (options.debug) logger.info("registered Nitro server plugin");
82
+ }
83
+ if (options.sourceMapsUploadOptions?.enabled) {
84
+ addTemplate({
85
+ filename: "allstak-sourcemaps-options.mjs",
86
+ getContents: () => `export default ${JSON.stringify(options.sourceMapsUploadOptions ?? {}, null, 2)};
87
+ `
88
+ });
89
+ if (typeof nuxt.options.sourcemap === "object") {
90
+ if (nuxt.options.sourcemap.client === void 0) {
91
+ nuxt.options.sourcemap.client = "hidden";
92
+ }
93
+ } else if (nuxt.options.sourcemap === void 0) {
94
+ nuxt.options.sourcemap = { client: "hidden", server: true };
95
+ }
96
+ if (options.debug) logger.info("source-map upload options surfaced");
97
+ }
98
+ if (options.debug) {
99
+ logger.success(`${name}@${version} ready (configKey: allstak)`);
100
+ }
101
+ }
102
+ });
103
+
104
+ export { SDK_NAME, SDK_VERSION, module$1 as default };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Client surface of `@allstak/nuxt`.
3
+ *
4
+ * Mirrors the framework convention where the client entry re-exports the full
5
+ * Vue SDK plus the framework-specific helpers. Importable in client code as:
6
+ *
7
+ * import { AllStak, useAllStak } from '@allstak/nuxt/client';
8
+ */
9
+ export * from '@allstak/vue';
10
+ export { SDK_NAME, SDK_VERSION } from './version.js';
@@ -0,0 +1,2 @@
1
+ export * from "@allstak/vue";
2
+ export { SDK_NAME, SDK_VERSION } from "./version.js";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Client runtime plugin (registered via `addPlugin`, order 0 so it runs early).
3
+ *
4
+ * Responsibilities:
5
+ * 1. `app.use(AllStakPlugin, …)` — delegate init + the Vue-side glue to the
6
+ * shared `@allstak/vue` SDK. `attachErrorHandler: false` so we do NOT
7
+ * hijack `app.config.errorHandler` (which would swallow Nuxt's own 500
8
+ * handling); error capture is driven through the Nuxt hooks below instead.
9
+ * 2. `vue:error` / `app:error` Nuxt hooks → `reportNuxtError` (filtering
10
+ * expected 3xx/4xx).
11
+ * 3. Router instrumentation → a `navigation` span per route change, finished
12
+ * on `page:finish`, guarded by the tree-shakeable `__ALLSTAK_TRACING__`
13
+ * flag.
14
+ */
15
+ declare const _default: any;
16
+ export default _default;
@@ -0,0 +1,83 @@
1
+ import { AllStakPlugin } from "@allstak/vue";
2
+ import { AllStak } from "@allstak/js";
3
+ import { defineNuxtPlugin, useRuntimeConfig } from "#imports";
4
+ import { SDK_NAME, SDK_VERSION } from "../version.js";
5
+ const baseClientConfig = typeof __ALLSTAK_CLIENT_CONFIG__ !== "undefined" ? __ALLSTAK_CLIENT_CONFIG__ : {};
6
+ function isExpectedHttpStatus(error) {
7
+ const status = error?.statusCode ?? error?.status;
8
+ return typeof status === "number" && status >= 300 && status < 500;
9
+ }
10
+ function reportNuxtError(args) {
11
+ const { error, instance, info, source } = args;
12
+ if (isExpectedHttpStatus(error)) return;
13
+ try {
14
+ AllStak.captureException(error instanceof Error ? error : new Error(String(error)), {
15
+ framework: "nuxt",
16
+ source,
17
+ componentName: instance?.$options?.name,
18
+ lifecycleHook: info
19
+ });
20
+ } catch {
21
+ }
22
+ }
23
+ export default defineNuxtPlugin({
24
+ name: "allstak:client",
25
+ enforce: "pre",
26
+ setup(nuxtApp) {
27
+ const runtime = useRuntimeConfig();
28
+ const publicConfig = runtime.public?.allstak ?? {};
29
+ const options = {
30
+ ...baseClientConfig,
31
+ apiKey: publicConfig.apiKey ?? baseClientConfig.apiKey ?? "",
32
+ host: publicConfig.host ?? baseClientConfig.host,
33
+ environment: publicConfig.environment ?? baseClientConfig.environment,
34
+ release: publicConfig.release ?? baseClientConfig.release,
35
+ dist: publicConfig.dist ?? baseClientConfig.dist,
36
+ sdkName: SDK_NAME,
37
+ sdkVersion: SDK_VERSION,
38
+ // Drive capture through Nuxt hooks, not Vue's errorHandler.
39
+ attachErrorHandler: false
40
+ };
41
+ nuxtApp.vueApp.use(AllStakPlugin, options);
42
+ nuxtApp.hook("vue:error", (error, instance, info) => {
43
+ reportNuxtError({ error, instance, info, source: "vue:error" });
44
+ });
45
+ nuxtApp.hook("app:error", (error) => {
46
+ reportNuxtError({ error, source: "app:error" });
47
+ });
48
+ const tracingFlag = globalThis.__ALLSTAK_TRACING__;
49
+ if (tracingFlag === false) return;
50
+ let activeNavSpan = null;
51
+ nuxtApp.hook("page:start", () => {
52
+ const to = nuxtApp.$router?.currentRoute?.value;
53
+ try {
54
+ AllStak.addBreadcrumb({
55
+ type: "navigation",
56
+ message: `\u2192 ${String(to?.fullPath ?? "")}`,
57
+ level: "info",
58
+ data: {
59
+ name: String(to?.name ?? ""),
60
+ fullPath: String(to?.fullPath ?? "")
61
+ }
62
+ });
63
+ activeNavSpan = AllStak.startSpan("navigation", {
64
+ description: String(to?.fullPath ?? ""),
65
+ attributes: {
66
+ "allstak.origin": "auto.navigation.nuxt",
67
+ "route.name": String(to?.name ?? ""),
68
+ "route.path": String(to?.path ?? "")
69
+ }
70
+ });
71
+ } catch {
72
+ }
73
+ });
74
+ nuxtApp.hook("page:finish", () => {
75
+ try {
76
+ activeNavSpan?.finish("ok");
77
+ } catch {
78
+ } finally {
79
+ activeNavSpan = null;
80
+ }
81
+ });
82
+ }
83
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Nitro server plugin (registered via `addServerPlugin`).
3
+ *
4
+ * 1. `AllStak.init` on the server, stamped with the Nuxt SDK identity.
5
+ * 2. `request` → open a per-request span; `afterResponse` / `beforeResponse`
6
+ * → finish it with the response status, so every server request is wrapped
7
+ * in a span.
8
+ * 3. `error` hook → `captureException` with the H3 event context (ignoring
9
+ * expected 3xx/4xx).
10
+ * 4. `render:html` → inject trace meta tags into the SSR `<head>` so the
11
+ * client continues the server's trace. Skipped for pre-render / SWR.
12
+ */
13
+ declare const _default: import("nitropack").NitroAppPlugin;
14
+ export default _default;
@@ -0,0 +1,116 @@
1
+ import { AllStak } from "@allstak/js";
2
+ import { defineNitroPlugin, useRuntimeConfig } from "nitropack/runtime";
3
+ import { SDK_NAME, SDK_VERSION } from "../version.js";
4
+ const baseServerConfig = typeof __ALLSTAK_SERVER_CONFIG__ !== "undefined" ? __ALLSTAK_SERVER_CONFIG__ : {};
5
+ const injectMetaTags = typeof __ALLSTAK_INJECT_META__ !== "undefined" ? __ALLSTAK_INJECT_META__ : true;
6
+ const INIT_FLAG = "__allstakNuxtServerInitialised__";
7
+ function isExpectedHttpStatus(error) {
8
+ const status = error?.statusCode ?? error?.status;
9
+ return typeof status === "number" && status >= 300 && status < 500;
10
+ }
11
+ function ensureInit() {
12
+ const g = globalThis;
13
+ if (g[INIT_FLAG]) return;
14
+ g[INIT_FLAG] = true;
15
+ let publicConfig = {};
16
+ try {
17
+ const runtime = useRuntimeConfig();
18
+ publicConfig = runtime?.public?.allstak ?? {};
19
+ } catch {
20
+ }
21
+ const apiKey = publicConfig.apiKey ?? baseServerConfig.apiKey ?? process.env.ALLSTAK_API_KEY ?? "";
22
+ try {
23
+ AllStak.init({
24
+ ...baseServerConfig,
25
+ apiKey,
26
+ host: publicConfig.host ?? baseServerConfig.host ?? process.env.ALLSTAK_HOST,
27
+ environment: publicConfig.environment ?? baseServerConfig.environment ?? process.env.NODE_ENV,
28
+ release: publicConfig.release ?? baseServerConfig.release ?? process.env.APP_VERSION,
29
+ dist: publicConfig.dist ?? baseServerConfig.dist,
30
+ sdkName: SDK_NAME,
31
+ sdkVersion: SDK_VERSION
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ });
34
+ } catch {
35
+ }
36
+ }
37
+ export default defineNitroPlugin((nitroApp) => {
38
+ ensureInit();
39
+ const SPAN_KEY = "_allstakSpan";
40
+ const ev = (event) => event;
41
+ nitroApp.hooks.hook("request", (event) => {
42
+ const e = ev(event);
43
+ try {
44
+ const method = e?.method ?? e?.node?.req?.method ?? "GET";
45
+ const path = e?.path ?? e?.node?.req?.url ?? "/";
46
+ const span = AllStak.startSpan("http.server", {
47
+ description: `${method} ${path}`,
48
+ attributes: {
49
+ "allstak.origin": "auto.http.server.nuxt",
50
+ "http.method": String(method),
51
+ "http.target": String(path)
52
+ }
53
+ });
54
+ e.context[SPAN_KEY] = span;
55
+ } catch {
56
+ }
57
+ });
58
+ const finishSpan = (event, status) => {
59
+ const e = ev(event);
60
+ try {
61
+ const span = e?.context?.[SPAN_KEY];
62
+ if (!span) return;
63
+ if (typeof status === "number") span.setTag("http.status_code", String(status));
64
+ span.finish(typeof status === "number" && status >= 500 ? "error" : "ok");
65
+ delete e.context[SPAN_KEY];
66
+ } catch {
67
+ }
68
+ };
69
+ nitroApp.hooks.hook("beforeResponse", (event, response) => {
70
+ const e = ev(event);
71
+ const status = e?.node?.res?.statusCode ?? response?.status;
72
+ finishSpan(event, status);
73
+ });
74
+ nitroApp.hooks.hook("afterResponse", (event) => {
75
+ finishSpan(event, ev(event)?.node?.res?.statusCode);
76
+ });
77
+ nitroApp.hooks.hook("error", (error, context) => {
78
+ if (isExpectedHttpStatus(error)) return;
79
+ const e = context?.event ? ev(context.event) : void 0;
80
+ try {
81
+ if (e) finishSpan(e, 500);
82
+ AllStak.captureException(error instanceof Error ? error : new Error(String(error)), {
83
+ framework: "nuxt",
84
+ source: "nitro:error",
85
+ method: e?.method ?? e?.node?.req?.method,
86
+ path: e?.path ?? e?.node?.req?.url
87
+ });
88
+ } catch {
89
+ }
90
+ });
91
+ if (injectMetaTags) {
92
+ const looseHook = nitroApp.hooks.hook;
93
+ looseHook("render:html", (html, context) => {
94
+ try {
95
+ const e = context?.event ? ev(context.event) : void 0;
96
+ if (isPrerenderOrSwr(e)) return;
97
+ const traceId = AllStak.getTraceId?.();
98
+ const spanId = AllStak.getCurrentSpanId?.();
99
+ if (!traceId) return;
100
+ const traceParent = spanId ? `${traceId}-${spanId}` : traceId;
101
+ html.head.push(`<meta name="allstak-trace" content="${escapeAttr(traceParent)}">`);
102
+ html.head.push(`<meta name="baggage" content="${escapeAttr(`allstak-trace_id=${traceId}`)}">`);
103
+ } catch {
104
+ }
105
+ });
106
+ }
107
+ });
108
+ function isPrerenderOrSwr(event) {
109
+ if (!event) return true;
110
+ const rules = event.context?.routeRules;
111
+ if (rules && (rules.swr || rules.isr)) return true;
112
+ return false;
113
+ }
114
+ function escapeAttr(value) {
115
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
116
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Server surface of `@allstak/nuxt`.
3
+ *
4
+ * Mirrors the framework convention where the server entry re-exports the full
5
+ * core (node) SDK so server route handlers and Nitro plugins can call the
6
+ * manual API directly:
7
+ *
8
+ * import { AllStak } from '@allstak/nuxt/server';
9
+ * export default defineEventHandler(() => { AllStak.addBreadcrumb({ ... }); });
10
+ */
11
+ export * from '@allstak/js';
12
+ export { SDK_NAME, SDK_VERSION } from './version.js';
@@ -0,0 +1,2 @@
1
+ export * from "@allstak/js";
2
+ export { SDK_NAME, SDK_VERSION } from "./version.js";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Runtime-side view of the public runtime config the module writes into
3
+ * `runtimeConfig.public.allstak`. Kept in the runtime tree (not `src/types.ts`)
4
+ * so the bundled runtime plugins stay self-contained and don't drag the
5
+ * module's build-time types into the client/server bundles.
6
+ *
7
+ * Only public fields live here — they ship to the browser. The `apiKey` is a
8
+ * publishable ingest key (the same key the other browser SDKs use), not a
9
+ * server secret.
10
+ */
11
+ export interface AllStakPublicRuntimeConfig {
12
+ apiKey?: string;
13
+ host?: string;
14
+ environment?: string;
15
+ release?: string;
16
+ dist?: string;
17
+ }
File without changes
@@ -0,0 +1,2 @@
1
+ export declare const SDK_NAME = "allstak-nuxt";
2
+ export declare const SDK_VERSION: string;
@@ -0,0 +1,2 @@
1
+ export const SDK_NAME = "allstak-nuxt";
2
+ export const SDK_VERSION = typeof __ALLSTAK_NUXT_VERSION__ === "string" ? __ALLSTAK_NUXT_VERSION__ : "0.0.0-dev";
@@ -0,0 +1,9 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { type SDK_NAME, type SDK_VERSION, default } from './module.mjs'
8
+
9
+ export { type AllStakClientConfig, type AllStakModuleOptions, type AllStakPublicRuntimeConfig, type AllStakServerConfig, type AutoInjectServerStrategy, type SourceMapsUploadOptions } from './module.mjs'
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@allstak/nuxt",
3
+ "version": "0.1.0",
4
+ "description": "Official AllStak SDK for Nuxt 3 / Nuxt 4 \u2014 error tracking, structured logs, distributed tracing, and observability for the client and the Nitro server.",
5
+ "keywords": [
6
+ "nuxt",
7
+ "nuxt3",
8
+ "nuxt4",
9
+ "nuxt-module",
10
+ "nitro",
11
+ "observability",
12
+ "error-tracking",
13
+ "logging",
14
+ "monitoring",
15
+ "tracing",
16
+ "allstak"
17
+ ],
18
+ "type": "module",
19
+ "homepage": "https://allstak.sa",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/AllStak/allstak-nuxt.git"
23
+ },
24
+ "bugs": "https://github.com/AllStak/allstak-nuxt/issues",
25
+ "license": "MIT",
26
+ "author": "AllStak <sdk@allstak.sa>",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/types.d.mts",
30
+ "import": "./dist/module.mjs"
31
+ }
32
+ },
33
+ "main": "./dist/module.mjs",
34
+ "types": "./dist/types.d.mts",
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "CHANGELOG.md",
39
+ "LICENSE"
40
+ ],
41
+ "scripts": {
42
+ "prepack": "nuxt-module-build build",
43
+ "build": "nuxt-module-build build",
44
+ "typecheck": "nuxt-module-build prepare && tsc --noEmit -p .nuxt/tsconfig.json",
45
+ "test": "vitest run"
46
+ },
47
+ "peerDependencies": {
48
+ "nuxt": "^3.7.0 || ^4.0.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "nuxt": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "dependencies": {
56
+ "@allstak/js": "^0.3.0",
57
+ "@allstak/vue": "^0.1.0",
58
+ "@nuxt/kit": "^3.7.0 || ^4.0.0"
59
+ },
60
+ "devDependencies": {
61
+ "@nuxt/module-builder": "^1.0.0",
62
+ "@types/node": "^22.0.0",
63
+ "nuxt": "^3.18.0",
64
+ "typescript": "^5.8.3",
65
+ "vitest": "^2.1.0",
66
+ "vue": "^3.4.0"
67
+ },
68
+ "publishConfig": {
69
+ "access": "public",
70
+ "provenance": true,
71
+ "registry": "https://registry.npmjs.org/"
72
+ }
73
+ }