@qelos/integrator-nuxt 4.0.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 ADDED
@@ -0,0 +1,20 @@
1
+ Polyform Shield License 1.0.0
2
+
3
+ Copyright (c) 2025 Velocitech LTD
4
+
5
+ Your use of this software is governed by the Polyform Shield License 1.0.0.
6
+
7
+ You may obtain a copy of the License at:
8
+ https://polyformproject.org/licenses/shield/1.0.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+
12
+ Additional Terms – Exception Notice:
13
+
14
+ The author of this software grants additional permissions beyond the Polyform Shield License 1.0.0:
15
+
16
+ ✅ All uses of the software are permitted — including modification, distribution, commercial use, and sublicensing — as long as the software is not used to create, offer, or operate any product or service that competes directly with Qelos (https://qelos.io), a no-code platform for building AI-assisted SaaS applications.
17
+
18
+ ❌ "Direct competition" includes platforms, tools, or services that enable users to visually or programmatically create SaaS applications with the assistance of artificial intelligence.
19
+
20
+ This exception does not modify the terms of the original Polyform Shield License for other users and applies only by explicit permission of the licensor.
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ # @qelos/integrator-nuxt
2
+
3
+ Nuxt 3 integrator for [Qelos](https://qelos.io). Adds a server middleware that
4
+ identifies the current user and active workspace via the
5
+ [`@qelos/sdk`](https://www.npmjs.com/package/@qelos/sdk) before your own
6
+ Nitro/Nuxt route handlers run, and exposes them through the H3 event context.
7
+
8
+ A token-refresh hook keeps the access token fresh: when the SDK detects an
9
+ expired access token, it transparently refreshes it — either through the
10
+ request's refresh token or, for cookie-only sessions, through the access
11
+ cookie itself — and writes the rotated cookie pair back on the response.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pnpm add @qelos/integrator-nuxt
17
+ ```
18
+
19
+ ## Configure
20
+
21
+ ```ts
22
+ // nuxt.config.ts
23
+ export default defineNuxtConfig({
24
+ modules: ['@qelos/integrator-nuxt'],
25
+ qelos: {
26
+ appUrl: 'https://your-qelos-app.com',
27
+ // Optional — defaults shown:
28
+ accessTokenCookie: 'q_access_token',
29
+ refreshTokenCookie: 'q_refresh_token',
30
+ requireAuth: false,
31
+ skipPaths: ['/api/_auth', '/health'],
32
+ },
33
+ });
34
+ ```
35
+
36
+ You can also override any field at runtime via `NUXT_QELOS_*` env vars (Nuxt
37
+ runtime-config conventions apply).
38
+
39
+ ## Use in server routes
40
+
41
+ The recommended wrapper, `defineQelosEventHandler`, gives you a typed `qelos`
42
+ context as a second argument and asserts the middleware ran:
43
+
44
+ ```ts
45
+ // server/api/products.ts
46
+ import { defineQelosEventHandler } from '@qelos/integrator-nuxt';
47
+
48
+ export default defineQelosEventHandler(async ({ qelos }) => {
49
+ return qelos.sdk.entities('products').getList();
50
+ });
51
+ ```
52
+
53
+ Pass `{ requireAuth: true }` to short-circuit anonymous requests with `401`:
54
+
55
+ ```ts
56
+ // server/api/me.ts
57
+ import { defineQelosEventHandler } from '@qelos/integrator-nuxt';
58
+
59
+ export default defineQelosEventHandler(({ qelos }) => {
60
+ return { user: qelos.user, workspace: qelos.workspace };
61
+ }, { requireAuth: true });
62
+ ```
63
+
64
+ Or use `defineEventHandler` directly — `event.context.qelos` is also populated:
65
+
66
+ ```ts
67
+ // server/api/me.ts
68
+ export default defineEventHandler((event) => {
69
+ const { user, workspace, sdk } = event.context.qelos!;
70
+ if (!user) {
71
+ throw createError({ statusCode: 401 });
72
+ }
73
+ return { user, workspace };
74
+ });
75
+ ```
76
+
77
+ `event.context.qelos` is typed as `QelosRequestContext`:
78
+
79
+ | field | description |
80
+ |--------------|------------------------------------------------------------|
81
+ | `user` | `IUser` from `@qelos/sdk` or `null` when anonymous. |
82
+ | `workspace` | The active `IWorkspace` for the request, or `null`. |
83
+ | `workspaces` | All workspaces the user has access to. |
84
+ | `sdk` | A request-scoped `QelosSDK` instance bound to the tokens. |
85
+ | `tokens` | The current access/refresh token pair (mutated on refresh). |
86
+
87
+ ## Use in components / pages
88
+
89
+ `useQelos()` is auto-imported in your Vue components and reads the identity
90
+ that was resolved by the server middleware. Values are seeded during SSR and
91
+ hydrated on the client through the Nuxt payload, so no extra round-trip is
92
+ needed.
93
+
94
+ ```vue
95
+ <script setup lang="ts">
96
+ const { user, workspace, workspaces, isAuthenticated } = useQelos();
97
+ </script>
98
+
99
+ <template>
100
+ <div v-if="isAuthenticated">
101
+ Hi {{ user.firstName }} — workspace: {{ workspace?.name }}
102
+ </div>
103
+ </template>
104
+ ```
105
+
106
+ For data calls from the browser, prefer hitting your own server routes (so
107
+ refresh-token rotation stays on the server):
108
+
109
+ ```vue
110
+ <script setup lang="ts">
111
+ const { data: products } = await useFetch('/api/products');
112
+ </script>
113
+ ```
114
+
115
+ If you need direct browser access to the Qelos API, use
116
+ [`@qelos/web-sdk`](https://www.npmjs.com/package/@qelos/web-sdk) instead — the
117
+ server-side SDK in this package is bound to per-request cookies and isn't
118
+ intended for client use.
119
+
120
+ ## Token refresh
121
+
122
+ When the access token is rejected, the SDK tries to recover, in order:
123
+
124
+ 1. The **refresh token** (`q_refresh_token`) via
125
+ `sdk.authentication.refreshToken()` — issues a new access + refresh pair.
126
+ 2. The **cookie token** (the access token cookie itself) via
127
+ `sdk.authentication.refreshCookieToken()` — used for cookie-only sessions
128
+ that do not carry a separate refresh token (e.g. social-auth flows).
129
+
130
+ After a successful refresh the middleware fires the `onTokenRefresh` hook.
131
+ The default implementation writes the new tokens back to the response cookies
132
+ (`HttpOnly`, `SameSite=Lax`).
133
+
134
+ ### Manual cookie refresh
135
+
136
+ Long-lived integrator-hosted sessions can also call the SDK directly to
137
+ proactively refresh the cookie token, e.g. from a Nitro route handler:
138
+
139
+ ```ts
140
+ // server/api/session/refresh.post.ts
141
+ import { defineQelosEventHandler } from '@qelos/integrator-nuxt';
142
+
143
+ export default defineQelosEventHandler(async ({ qelos }) => {
144
+ const result = await qelos.sdk.authentication.refreshCookieToken();
145
+ // result.headers['set-cookie'] — fresh cookie value to forward
146
+ return { user: result.payload.user };
147
+ });
148
+ ```
149
+
150
+ ## Custom workspace resolution / refresh hook
151
+
152
+ If the default behavior (first workspace from `sdk.workspaces.getList()` and a
153
+ cookie-based refresh writeback) doesn't fit your app, register your own
154
+ middleware instead of (or in addition to) the module's default handler:
155
+
156
+ ```ts
157
+ // server/middleware/qelos.ts
158
+ import { createQelosMiddleware } from '@qelos/integrator-nuxt';
159
+
160
+ export default createQelosMiddleware({
161
+ config: useRuntimeConfig().qelos,
162
+ resolveWorkspace: ({ user, workspaces }) =>
163
+ workspaces.find(w => w._id === user.metadata?.activeWorkspace) || workspaces[0] || null,
164
+ onTokenRefresh: async ({ event, newTokens }) => {
165
+ // your own persistence — DB, signed cookie, KV, etc.
166
+ setCookie(event, 'q_access_token', newTokens.accessToken, { httpOnly: true });
167
+ setCookie(event, 'q_refresh_token', newTokens.refreshToken, { httpOnly: true });
168
+ },
169
+ });
170
+ ```
171
+
172
+ To skip the module's default registration, set `qelos.disableMiddleware: true`
173
+ in `nuxt.config.ts`.
174
+
175
+ ## API token mode
176
+
177
+ For service-to-service deployments where every request shares one Qelos API
178
+ token, set `apiToken` and the middleware will skip the cookie/refresh flow:
179
+
180
+ ```ts
181
+ qelos: {
182
+ appUrl: 'https://your-qelos-app.com',
183
+ apiToken: process.env.QELOS_API_TOKEN,
184
+ }
185
+ ```
@@ -0,0 +1,32 @@
1
+ import { type EventHandlerRequest, type EventHandlerResponse, type H3Event } from 'h3';
2
+ import type { QelosRequestContext } from './types';
3
+ export interface QelosEventContext<T extends EventHandlerRequest = EventHandlerRequest> {
4
+ event: H3Event<T>;
5
+ qelos: QelosRequestContext;
6
+ }
7
+ export interface DefineQelosEventHandlerOptions {
8
+ /**
9
+ * Reject the request with `401 Unauthorized` when no authenticated user is
10
+ * resolved on `event.context.qelos`. Defaults to `false`, mirroring the
11
+ * module-level `qelos.requireAuth` behavior.
12
+ */
13
+ requireAuth?: boolean;
14
+ }
15
+ export type QelosEventHandler<Request extends EventHandlerRequest = EventHandlerRequest, Response extends EventHandlerResponse = EventHandlerResponse> = (ctx: QelosEventContext<Request>) => Response;
16
+ /**
17
+ * Wrap a Nitro/H3 event handler so that the Qelos request context (user,
18
+ * workspace, workspaces, sdk, tokens) is available as a second argument and
19
+ * already typed. Requires the `@qelos/integrator-nuxt` module to be enabled
20
+ * (or `createQelosMiddleware` to be registered manually) so that
21
+ * `event.context.qelos` is populated.
22
+ *
23
+ * ```ts
24
+ * // server/api/products.ts
25
+ * import { defineQelosEventHandler } from '@qelos/integrator-nuxt';
26
+ *
27
+ * export default defineQelosEventHandler(async ({ qelos }) => {
28
+ * return qelos.sdk.entities('products').getList();
29
+ * });
30
+ * ```
31
+ */
32
+ export declare function defineQelosEventHandler<Request extends EventHandlerRequest = EventHandlerRequest, Response extends EventHandlerResponse = EventHandlerResponse>(handler: QelosEventHandler<Request, Response>, options?: DefineQelosEventHandlerOptions): import("h3").EventHandler<Request, any>;
@@ -0,0 +1,33 @@
1
+ import { defineEventHandler, createError, } from 'h3';
2
+ /**
3
+ * Wrap a Nitro/H3 event handler so that the Qelos request context (user,
4
+ * workspace, workspaces, sdk, tokens) is available as a second argument and
5
+ * already typed. Requires the `@qelos/integrator-nuxt` module to be enabled
6
+ * (or `createQelosMiddleware` to be registered manually) so that
7
+ * `event.context.qelos` is populated.
8
+ *
9
+ * ```ts
10
+ * // server/api/products.ts
11
+ * import { defineQelosEventHandler } from '@qelos/integrator-nuxt';
12
+ *
13
+ * export default defineQelosEventHandler(async ({ qelos }) => {
14
+ * return qelos.sdk.entities('products').getList();
15
+ * });
16
+ * ```
17
+ */
18
+ export function defineQelosEventHandler(handler, options = {}) {
19
+ return defineEventHandler(async (event) => {
20
+ const qelos = event.context.qelos;
21
+ if (!qelos) {
22
+ throw createError({
23
+ statusCode: 500,
24
+ statusMessage: '[@qelos/integrator-nuxt] event.context.qelos is missing. Add the module to nuxt.config or register createQelosMiddleware manually.',
25
+ });
26
+ }
27
+ if (options.requireAuth && !qelos.user) {
28
+ throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
29
+ }
30
+ return handler({ event, qelos });
31
+ });
32
+ }
33
+ //# sourceMappingURL=event-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-handler.js","sourceRoot":"","sources":["../src/event-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,WAAW,GAIZ,MAAM,IAAI,CAAC;AAsBZ;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB,CAIrC,OAA6C,EAC7C,UAA0C,EAAE;IAE5C,OAAO,kBAAkB,CAAU,KAAK,EAAE,KAAK,EAAE,EAAE;QACjD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,WAAW,CAAC;gBAChB,UAAU,EAAE,GAAG;gBACf,aAAa,EACX,oIAAoI;aACvI,CAAC,CAAC;QACL,CAAC;QACD,IAAI,OAAO,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACvC,MAAM,WAAW,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC,CAAC;QACxE,CAAC;QACD,OAAO,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAa,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { type H3Event } from 'h3';
2
+ import type { IUser } from '@qelos/sdk/dist/authentication';
3
+ import type { IWorkspace } from '@qelos/sdk/workspaces';
4
+ import type { QelosNuxtRuntimeConfig, TokenRefreshHook } from './types';
5
+ export interface CreateMiddlewareOptions {
6
+ config: QelosNuxtRuntimeConfig;
7
+ /**
8
+ * Hook invoked after a successful token refresh. The default implementation
9
+ * writes the new tokens back to the response cookies.
10
+ */
11
+ onTokenRefresh?: TokenRefreshHook;
12
+ /**
13
+ * Resolve the active workspace for a request. Defaults to picking the first
14
+ * workspace returned from `sdk.workspaces.getList()`.
15
+ */
16
+ resolveWorkspace?: (params: {
17
+ event: H3Event;
18
+ user: IUser;
19
+ workspaces: IWorkspace[];
20
+ }) => IWorkspace | null | Promise<IWorkspace | null>;
21
+ }
22
+ export declare function createQelosMiddleware(options: CreateMiddlewareOptions): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
23
+ export type QelosMiddleware = ReturnType<typeof createQelosMiddleware>;
@@ -0,0 +1,96 @@
1
+ import { defineEventHandler, getCookie, getRequestHeader, setCookie, createError, } from 'h3';
2
+ import { createRequestSdk } from './sdk-factory';
3
+ const DEFAULT_ACCESS_COOKIE = 'q_access_token';
4
+ const DEFAULT_REFRESH_COOKIE = 'q_refresh_token';
5
+ function readTokens(event, config) {
6
+ const accessCookie = config.accessTokenCookie || DEFAULT_ACCESS_COOKIE;
7
+ const refreshCookie = config.refreshTokenCookie || DEFAULT_REFRESH_COOKIE;
8
+ const cookieAccess = getCookie(event, accessCookie);
9
+ const cookieRefresh = getCookie(event, refreshCookie);
10
+ const authHeader = getRequestHeader(event, 'authorization');
11
+ const headerAccess = authHeader && authHeader.toLowerCase().startsWith('bearer ')
12
+ ? authHeader.slice(7).trim()
13
+ : undefined;
14
+ return {
15
+ accessToken: headerAccess || cookieAccess || undefined,
16
+ refreshToken: cookieRefresh || undefined,
17
+ };
18
+ }
19
+ function writeTokensToCookies(event, config, tokens) {
20
+ const accessCookie = config.accessTokenCookie || DEFAULT_ACCESS_COOKIE;
21
+ const refreshCookie = config.refreshTokenCookie || DEFAULT_REFRESH_COOKIE;
22
+ const cookieOptions = {
23
+ httpOnly: true,
24
+ secure: true,
25
+ sameSite: 'lax',
26
+ path: '/',
27
+ };
28
+ setCookie(event, accessCookie, tokens.accessToken, cookieOptions);
29
+ if (tokens.refreshToken) {
30
+ setCookie(event, refreshCookie, tokens.refreshToken, cookieOptions);
31
+ }
32
+ }
33
+ function shouldSkip(event, config) {
34
+ if (!config.skipPaths?.length) {
35
+ return false;
36
+ }
37
+ const path = event.path || event.node?.req?.url || '';
38
+ return config.skipPaths.some((prefix) => path.startsWith(prefix));
39
+ }
40
+ export function createQelosMiddleware(options) {
41
+ const { config, resolveWorkspace } = options;
42
+ const onTokenRefresh = options.onTokenRefresh
43
+ || (async ({ event, newTokens }) => {
44
+ writeTokensToCookies(event, config, newTokens);
45
+ });
46
+ return defineEventHandler(async (event) => {
47
+ if (shouldSkip(event, config)) {
48
+ return;
49
+ }
50
+ const tokens = readTokens(event, config);
51
+ const sdk = createRequestSdk({ config, tokens, event, onTokenRefresh });
52
+ const ctx = {
53
+ user: null,
54
+ workspace: null,
55
+ workspaces: [],
56
+ sdk,
57
+ tokens,
58
+ };
59
+ event.context.qelos = ctx;
60
+ const hasAuthMaterial = Boolean(config.apiToken || tokens.accessToken || tokens.refreshToken);
61
+ if (!hasAuthMaterial) {
62
+ if (config.requireAuth) {
63
+ throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
64
+ }
65
+ return;
66
+ }
67
+ try {
68
+ ctx.user = await sdk.authentication.getLoggedInUser();
69
+ }
70
+ catch (err) {
71
+ if (config.requireAuth) {
72
+ throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
73
+ }
74
+ return;
75
+ }
76
+ try {
77
+ ctx.workspaces = await sdk.workspaces.getList();
78
+ }
79
+ catch {
80
+ ctx.workspaces = [];
81
+ }
82
+ if (ctx.user && ctx.workspaces.length) {
83
+ if (resolveWorkspace) {
84
+ ctx.workspace = await resolveWorkspace({
85
+ event,
86
+ user: ctx.user,
87
+ workspaces: ctx.workspaces,
88
+ }) || null;
89
+ }
90
+ else {
91
+ ctx.workspace = ctx.workspaces[0] || null;
92
+ }
93
+ }
94
+ });
95
+ }
96
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { QelosNuxtRuntimeConfig } from './types';
2
+ export interface QelosNuxtModuleOptions extends QelosNuxtRuntimeConfig {
3
+ /**
4
+ * Disable registration of the default server middleware. Use this if you
5
+ * want to call `createQelosMiddleware` yourself from your own server route.
6
+ */
7
+ disableMiddleware?: boolean;
8
+ }
9
+ declare const _default: import("nuxt/schema").NuxtModule<QelosNuxtModuleOptions, QelosNuxtModuleOptions, false>;
10
+ export default _default;
11
+ export { createQelosMiddleware } from './middleware';
12
+ export { createRequestSdk } from './sdk-factory';
13
+ export { defineQelosEventHandler } from './event-handler';
14
+ export type { DefineQelosEventHandlerOptions, QelosEventContext, QelosEventHandler, } from './event-handler';
15
+ export type { QelosNuxtRuntimeConfig, QelosRequestContext, QelosTokenPair, ResolvedTokens, TokenRefreshContext, TokenRefreshHook, } from './types';
package/dist/module.js ADDED
@@ -0,0 +1,49 @@
1
+ import { defineNuxtModule, addServerHandler, addImportsDir, createResolver, addTypeTemplate, } from '@nuxt/kit';
2
+ export default defineNuxtModule({
3
+ meta: {
4
+ name: '@qelos/integrator-nuxt',
5
+ configKey: 'qelos',
6
+ compatibility: {
7
+ nuxt: '^3.0.0',
8
+ },
9
+ },
10
+ defaults: {
11
+ appUrl: '',
12
+ },
13
+ setup(options, nuxt) {
14
+ if (!options.appUrl) {
15
+ throw new Error('[@qelos/integrator-nuxt] `qelos.appUrl` must be set in nuxt.config');
16
+ }
17
+ const { resolve } = createResolver(import.meta.url);
18
+ const { disableMiddleware, ...runtimeConfig } = options;
19
+ nuxt.options.runtimeConfig.qelos = {
20
+ ...nuxt.options.runtimeConfig.qelos,
21
+ ...runtimeConfig,
22
+ };
23
+ if (!disableMiddleware) {
24
+ addServerHandler({
25
+ handler: resolve('./runtime/server-handler'),
26
+ middleware: true,
27
+ });
28
+ }
29
+ addImportsDir(resolve('./runtime/composables'));
30
+ addTypeTemplate({
31
+ filename: 'types/qelos-nuxt.d.ts',
32
+ getContents: () => `
33
+ import type { QelosRequestContext } from '@qelos/integrator-nuxt/types';
34
+
35
+ declare module 'h3' {
36
+ interface H3EventContext {
37
+ qelos?: QelosRequestContext;
38
+ }
39
+ }
40
+
41
+ export {};
42
+ `,
43
+ });
44
+ },
45
+ });
46
+ export { createQelosMiddleware } from './middleware';
47
+ export { createRequestSdk } from './sdk-factory';
48
+ export { defineQelosEventHandler } from './event-handler';
49
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,eAAe,GAChB,MAAM,WAAW,CAAC;AAWnB,eAAe,gBAAgB,CAAyB;IACtD,IAAI,EAAE;QACJ,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EAAE,OAAO;QAClB,aAAa,EAAE;YACb,IAAI,EAAE,QAAQ;SACf;KACF;IACD,QAAQ,EAAE;QACR,MAAM,EAAE,EAAE;KACX;IACD,KAAK,CAAC,OAAO,EAAE,IAAI;QACjB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAEpD,MAAM,EAAE,iBAAiB,EAAE,GAAG,aAAa,EAAE,GAAG,OAAO,CAAC;QAExD,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,GAAG;YACjC,GAAI,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,KAA6C;YAC5E,GAAG,aAAa;SACjB,CAAC;QAEF,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACvB,gBAAgB,CAAC;gBACf,OAAO,EAAE,OAAO,CAAC,0BAA0B,CAAC;gBAC5C,UAAU,EAAE,IAAI;aACjB,CAAC,CAAC;QACL,CAAC;QAED,aAAa,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAEhD,eAAe,CAAC;YACd,QAAQ,EAAE,uBAAuB;YACjC,WAAW,EAAE,GAAG,EAAE,CAAC;;;;;;;;;;CAUxB;SACI,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { IUser } from '@qelos/sdk/dist/authentication';
2
+ import type { IWorkspace } from '@qelos/sdk/workspaces';
3
+ interface Ref<T> {
4
+ value: T;
5
+ }
6
+ export interface UseQelosReturn {
7
+ /** The authenticated user (or `null` when anonymous). Hydrated from SSR. */
8
+ user: Ref<IUser | null>;
9
+ /** The active workspace for the user (or `null`). Hydrated from SSR. */
10
+ workspace: Ref<IWorkspace | null>;
11
+ /** All workspaces the user has access to. Hydrated from SSR. */
12
+ workspaces: Ref<IWorkspace[]>;
13
+ /** Convenience: `true` when `user` is set. */
14
+ isAuthenticated: Ref<boolean>;
15
+ }
16
+ /**
17
+ * Read the current Qelos identity (user / workspace / workspaces) on the
18
+ * client. Values are seeded on the server by the Qelos server middleware and
19
+ * sent to the client via Nuxt's payload. SDK calls from the browser should go
20
+ * through your own server routes (use `defineQelosEventHandler`) rather than
21
+ * directly, so refresh-token rotation stays server-side.
22
+ *
23
+ * ```vue
24
+ * <script setup lang="ts">
25
+ * const { user, workspace, isAuthenticated } = useQelos();
26
+ * </script>
27
+ * ```
28
+ */
29
+ export declare function useQelos(): UseQelosReturn;
30
+ export {};
@@ -0,0 +1,38 @@
1
+ // Auto-imported as `useQelos` by the @qelos/integrator-nuxt module. This file
2
+ // is bundled by Nuxt/Nitro in the consuming app, so `#imports` resolves at
3
+ // the consumer's build time.
4
+ // @ts-ignore - resolved by Nuxt
5
+ import { useState, useRequestEvent } from '#imports';
6
+ function getServerContext() {
7
+ if (typeof window !== 'undefined')
8
+ return null;
9
+ try {
10
+ const event = useRequestEvent();
11
+ return (event && event.context && event.context.qelos) || null;
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ /**
18
+ * Read the current Qelos identity (user / workspace / workspaces) on the
19
+ * client. Values are seeded on the server by the Qelos server middleware and
20
+ * sent to the client via Nuxt's payload. SDK calls from the browser should go
21
+ * through your own server routes (use `defineQelosEventHandler`) rather than
22
+ * directly, so refresh-token rotation stays server-side.
23
+ *
24
+ * ```vue
25
+ * <script setup lang="ts">
26
+ * const { user, workspace, isAuthenticated } = useQelos();
27
+ * </script>
28
+ * ```
29
+ */
30
+ export function useQelos() {
31
+ const ctx = getServerContext();
32
+ const user = useState('qelos:user', () => ctx?.user ?? null);
33
+ const workspace = useState('qelos:workspace', () => ctx?.workspace ?? null);
34
+ const workspaces = useState('qelos:workspaces', () => ctx?.workspaces ?? []);
35
+ const isAuthenticated = useState('qelos:isAuthenticated', () => Boolean(ctx?.user));
36
+ return { user, workspace, workspaces, isAuthenticated };
37
+ }
38
+ //# sourceMappingURL=useQelos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQelos.js","sourceRoot":"","sources":["../../../src/runtime/composables/useQelos.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2EAA2E;AAC3E,6BAA6B;AAC7B,gCAAgC;AAChC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAsBrD,SAAS,gBAAgB;IACvB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,CAAe,YAAY,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC;IAC3E,MAAM,SAAS,GAAG,QAAQ,CAAoB,iBAAiB,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,SAAS,IAAI,IAAI,CAAC,CAAC;IAC/F,MAAM,UAAU,GAAG,QAAQ,CAAe,kBAAkB,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC;IAC3F,MAAM,eAAe,GAAG,QAAQ,CAC9B,uBAAuB,EACvB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CACzB,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
2
+ export default _default;
@@ -0,0 +1,10 @@
1
+ // This file is registered as a Nuxt server middleware by `module.ts`.
2
+ // It is bundled by Nitro, so `#imports` resolves at the consuming-app build
3
+ // time (not at this package's `tsc` build).
4
+ // @ts-ignore - resolved by Nitro
5
+ import { useRuntimeConfig } from '#imports';
6
+ import { createQelosMiddleware } from '../middleware';
7
+ const runtimeConfig = useRuntimeConfig();
8
+ const config = (runtimeConfig.qelos || {});
9
+ export default createQelosMiddleware({ config });
10
+ //# sourceMappingURL=server-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-handler.js","sourceRoot":"","sources":["../../src/runtime/server-handler.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,4EAA4E;AAC5E,4CAA4C;AAC5C,iCAAiC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;AACzC,MAAM,MAAM,GAAG,CAAC,aAAa,CAAC,KAAK,IAAI,EAAE,CAA2B,CAAC;AAErE,eAAe,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ import QelosSDK from '@qelos/sdk';
2
+ import type { QelosNuxtRuntimeConfig, QelosTokenPair, TokenRefreshHook } from './types';
3
+ import type { H3Event } from 'h3';
4
+ export interface CreateSdkParams {
5
+ config: QelosNuxtRuntimeConfig;
6
+ /**
7
+ * Tokens for the current request. The factory mutates this object in
8
+ * place when a token refresh occurs, so callers can read the latest pair
9
+ * after the SDK has been used.
10
+ */
11
+ tokens: QelosTokenPair;
12
+ event: H3Event;
13
+ onTokenRefresh?: TokenRefreshHook;
14
+ }
15
+ export declare function createRequestSdk({ config, tokens, event, onTokenRefresh }: CreateSdkParams): QelosSDK;