@croct/plug-nuxt 0.0.1 → 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.
Files changed (47) hide show
  1. package/dist/module.cjs +5 -0
  2. package/dist/module.d.mts +3 -0
  3. package/dist/module.d.ts +3 -0
  4. package/dist/module.json +12 -0
  5. package/dist/module.mjs +128 -0
  6. package/dist/runtime/components/Personalization.d.ts +44 -0
  7. package/dist/runtime/components/Personalization.js +46 -0
  8. package/dist/runtime/components/Slot.d.ts +45 -0
  9. package/dist/runtime/components/Slot.js +52 -0
  10. package/dist/runtime/composables/useContent.d.ts +15 -0
  11. package/dist/runtime/composables/useContent.js +19 -0
  12. package/dist/runtime/composables/useCroct.d.ts +1 -0
  13. package/dist/runtime/composables/useCroct.js +1 -0
  14. package/dist/runtime/composables/useEvaluation.d.ts +12 -0
  15. package/dist/runtime/composables/useEvaluation.js +12 -0
  16. package/dist/runtime/csr/index.d.ts +3 -0
  17. package/dist/runtime/csr/index.js +2 -0
  18. package/dist/runtime/csr/useContent.d.ts +2 -0
  19. package/dist/runtime/csr/useContent.js +12 -0
  20. package/dist/runtime/plugin.client.d.ts +2 -0
  21. package/dist/runtime/plugin.client.js +36 -0
  22. package/dist/runtime/server/api/_croct/content.post.d.ts +2 -0
  23. package/dist/runtime/server/api/_croct/content.post.js +6 -0
  24. package/dist/runtime/server/api/_croct/evaluate.post.d.ts +2 -0
  25. package/dist/runtime/server/api/_croct/evaluate.post.js +6 -0
  26. package/dist/runtime/server/composables/anonymize.d.ts +1 -0
  27. package/dist/runtime/server/composables/anonymize.js +6 -0
  28. package/dist/runtime/server/composables/evaluate.d.ts +4 -0
  29. package/dist/runtime/server/composables/evaluate.js +37 -0
  30. package/dist/runtime/server/composables/fetchContent.d.ts +9 -0
  31. package/dist/runtime/server/composables/fetchContent.js +50 -0
  32. package/dist/runtime/server/composables/identify.d.ts +1 -0
  33. package/dist/runtime/server/composables/identify.js +6 -0
  34. package/dist/runtime/server/croct-resolvers.d.ts +4 -0
  35. package/dist/runtime/server/middleware/croct.d.ts +2 -0
  36. package/dist/runtime/server/middleware/croct.js +138 -0
  37. package/dist/runtime/server/utils/cookie.d.ts +8 -0
  38. package/dist/runtime/server/utils/cookie.js +19 -0
  39. package/dist/runtime/server/utils/security.d.ts +6 -0
  40. package/dist/runtime/server/utils/security.js +40 -0
  41. package/dist/runtime/types.d.ts +5 -0
  42. package/dist/runtime/types.js +0 -0
  43. package/dist/runtime/utils/locale.d.ts +9 -0
  44. package/dist/runtime/utils/locale.js +15 -0
  45. package/dist/types.d.mts +7 -0
  46. package/dist/types.d.ts +7 -0
  47. package/package.json +3 -2
@@ -0,0 +1,5 @@
1
+ module.exports = function(...args) {
2
+ return import('./module.mjs').then(m => m.default.call(this, ...args))
3
+ }
4
+ const _meta = module.exports.meta = require('./module.json')
5
+ module.exports.getMeta = () => Promise.resolve(_meta)
@@ -0,0 +1,3 @@
1
+ declare const _default: NuxtModule<TOptions, TOptions, false>;
2
+
3
+ export { _default as default };
@@ -0,0 +1,3 @@
1
+ declare const _default: NuxtModule<TOptions, TOptions, false>;
2
+
3
+ export { _default as default };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@croct/plug-nuxt",
3
+ "configKey": "croct",
4
+ "compatibility": {
5
+ "nuxt": ">=3.0.0"
6
+ },
7
+ "version": "0.1.0",
8
+ "builder": {
9
+ "@nuxt/module-builder": "0.8.4",
10
+ "unbuild": "2.0.0"
11
+ }
12
+ }
@@ -0,0 +1,128 @@
1
+ import { join } from 'path';
2
+ import { defineNuxtModule, createResolver, addTemplate, addPlugin, addServerHandler, addImportsDir, addServerImportsDir } from '@nuxt/kit';
3
+
4
+ const DEFAULT_CLIENT_ID_COOKIE_DURATION = 365 * 24 * 60 * 60;
5
+ const DEFAULT_USER_TOKEN_COOKIE_DURATION = 7 * 24 * 60 * 60;
6
+ const DEFAULT_TOKEN_DURATION = 24 * 60 * 60;
7
+ const module = defineNuxtModule({
8
+ meta: {
9
+ name: "@croct/plug-nuxt",
10
+ configKey: "croct",
11
+ compatibility: {
12
+ nuxt: ">=3.0.0"
13
+ }
14
+ },
15
+ defaults: {
16
+ debug: false,
17
+ test: false,
18
+ defaultPreferredLocale: "",
19
+ disableUserTokenAuthentication: false,
20
+ tokenDuration: DEFAULT_TOKEN_DURATION
21
+ },
22
+ setup: function(options, nuxt) {
23
+ const resolver = createResolver(import.meta.url);
24
+ const cookieConfig = {
25
+ clientId: {
26
+ name: options.cookie?.clientId?.name ?? "ct.client_id",
27
+ domain: options.cookie?.clientId?.domain ?? "",
28
+ duration: options.cookie?.clientId?.duration ?? DEFAULT_CLIENT_ID_COOKIE_DURATION
29
+ },
30
+ userToken: {
31
+ name: options.cookie?.userToken?.name ?? "ct.user_token",
32
+ domain: options.cookie?.userToken?.domain ?? "",
33
+ duration: options.cookie?.userToken?.duration ?? DEFAULT_USER_TOKEN_COOKIE_DURATION
34
+ },
35
+ previewToken: {
36
+ name: options.cookie?.previewToken?.name ?? "ct.preview_token",
37
+ domain: options.cookie?.previewToken?.domain ?? ""
38
+ }
39
+ };
40
+ nuxt.options.runtimeConfig.public.croct = {
41
+ appId: options.appId ?? "",
42
+ debug: options.debug ?? false,
43
+ test: options.test ?? false,
44
+ defaultPreferredLocale: options.defaultPreferredLocale ?? "",
45
+ defaultFetchTimeout: options.defaultFetchTimeout,
46
+ baseEndpointUrl: options.baseEndpointUrl,
47
+ cookie: cookieConfig
48
+ };
49
+ nuxt.options.runtimeConfig.croct = {
50
+ apiKey: "",
51
+ disableUserTokenAuthentication: options.disableUserTokenAuthentication ?? false,
52
+ tokenDuration: options.tokenDuration ?? DEFAULT_TOKEN_DURATION
53
+ };
54
+ const appResolver = createResolver(nuxt.options.rootDir);
55
+ const resolversCode = generateResolversModule(options, appResolver);
56
+ addTemplate({
57
+ filename: "croct/resolvers.ts",
58
+ write: true,
59
+ getContents: () => resolversCode
60
+ });
61
+ const nitroOptions = nuxt.options.nitro;
62
+ nitroOptions.alias = nitroOptions.alias ?? {};
63
+ nitroOptions.alias["#croct/resolvers"] = join(nuxt.options.buildDir, "croct/resolvers");
64
+ const clientOptionsCode = generateClientOptionsModule(options, appResolver);
65
+ addTemplate({
66
+ filename: "croct/client-options.ts",
67
+ write: true,
68
+ getContents: () => clientOptionsCode
69
+ });
70
+ nuxt.options.alias["#croct/client-options"] = join(nuxt.options.buildDir, "croct/client-options");
71
+ addPlugin(resolver.resolve("./runtime/plugin.client"));
72
+ addServerHandler({
73
+ handler: resolver.resolve("./runtime/server/middleware/croct"),
74
+ middleware: true
75
+ });
76
+ addImportsDir(resolver.resolve("./runtime/composables"));
77
+ addServerImportsDir(resolver.resolve("./runtime/server/composables"));
78
+ addServerHandler({
79
+ route: "/api/_croct/content",
80
+ method: "post",
81
+ handler: resolver.resolve("./runtime/server/api/_croct/content.post")
82
+ });
83
+ addServerHandler({
84
+ route: "/api/_croct/evaluate",
85
+ method: "post",
86
+ handler: resolver.resolve("./runtime/server/api/_croct/evaluate.post")
87
+ });
88
+ nuxt.hook("components:dirs", (dirs) => {
89
+ dirs.push({
90
+ path: resolver.resolve("./runtime/components"),
91
+ prefix: ""
92
+ });
93
+ });
94
+ nuxt.options.build.transpile.push("@croct/plug-vue", "@croct/sdk", "@croct/plug");
95
+ nuxt.options.vite = nuxt.options.vite ?? {};
96
+ nuxt.options.vite.ssr = nuxt.options.vite.ssr ?? {};
97
+ nuxt.options.vite.ssr.noExternal = nuxt.options.vite.ssr.noExternal ?? [];
98
+ if (Array.isArray(nuxt.options.vite.ssr.noExternal)) {
99
+ nuxt.options.vite.ssr.noExternal.push("@croct/plug-vue");
100
+ }
101
+ nuxt.options.experimental.asyncContext = true;
102
+ }
103
+ });
104
+ function generateResolversModule(options, appResolver) {
105
+ const lines = [];
106
+ if (options.localeResolver !== void 0) {
107
+ lines.push(`export { default as localeResolver } from '${appResolver.resolve(options.localeResolver)}';`);
108
+ } else {
109
+ lines.push("export const localeResolver = undefined;");
110
+ }
111
+ if (options.userIdResolver !== void 0) {
112
+ lines.push(`export { default as userIdResolver } from '${appResolver.resolve(options.userIdResolver)}';`);
113
+ } else {
114
+ lines.push("export const userIdResolver = undefined;");
115
+ }
116
+ return lines.join("\n");
117
+ }
118
+ function generateClientOptionsModule(options, appResolver) {
119
+ const lines = [];
120
+ if (options.urlSanitizer !== void 0) {
121
+ lines.push(`export { default as urlSanitizer } from '${appResolver.resolve(options.urlSanitizer)}';`);
122
+ } else {
123
+ lines.push("export const urlSanitizer = undefined;");
124
+ }
125
+ return lines.join("\n");
126
+ }
127
+
128
+ export { module as default };
@@ -0,0 +1,44 @@
1
+ import type { PropType } from 'vue';
2
+ import type { JsonObject } from '@croct/plug/sdk/json';
3
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ query: {
5
+ type: StringConstructor;
6
+ required: true;
7
+ };
8
+ fallback: {
9
+ type: PropType<unknown>;
10
+ default: undefined;
11
+ };
12
+ timeout: {
13
+ type: NumberConstructor;
14
+ default: undefined;
15
+ };
16
+ attributes: {
17
+ type: PropType<JsonObject>;
18
+ default: undefined;
19
+ };
20
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
21
+ [key: string]: any;
22
+ }>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
23
+ query: {
24
+ type: StringConstructor;
25
+ required: true;
26
+ };
27
+ fallback: {
28
+ type: PropType<unknown>;
29
+ default: undefined;
30
+ };
31
+ timeout: {
32
+ type: NumberConstructor;
33
+ default: undefined;
34
+ };
35
+ attributes: {
36
+ type: PropType<JsonObject>;
37
+ default: undefined;
38
+ };
39
+ }>> & Readonly<{}>, {
40
+ fallback: undefined;
41
+ timeout: number;
42
+ attributes: JsonObject;
43
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
44
+ export default _default;
@@ -0,0 +1,46 @@
1
+ import { defineComponent } from "vue";
2
+ import { useAsyncData } from "#app";
3
+ export default defineComponent({
4
+ name: "Personalization",
5
+ props: {
6
+ query: {
7
+ type: String,
8
+ required: true
9
+ },
10
+ fallback: {
11
+ type: [Object, String, Number, Boolean, null],
12
+ default: void 0
13
+ },
14
+ timeout: {
15
+ type: Number,
16
+ default: void 0
17
+ },
18
+ attributes: {
19
+ type: Object,
20
+ default: void 0
21
+ }
22
+ },
23
+ setup: async function(props, { slots }) {
24
+ const options = {
25
+ ...props.fallback !== void 0 ? { fallback: props.fallback } : {},
26
+ ...props.timeout !== void 0 ? { timeout: props.timeout } : {},
27
+ ...props.attributes !== void 0 ? { attributes: props.attributes } : {}
28
+ };
29
+ const { data, pending, error } = await useAsyncData(
30
+ `croct:pers:${props.query}:${JSON.stringify(options)}`,
31
+ () => $fetch("/api/_croct/evaluate", {
32
+ method: "POST",
33
+ body: { query: props.query, ...options }
34
+ })
35
+ );
36
+ return () => {
37
+ if (error.value !== null) {
38
+ return slots.error?.({ error: error.value });
39
+ }
40
+ if (pending.value) {
41
+ return slots.loading?.();
42
+ }
43
+ return slots.default?.({ result: data.value });
44
+ };
45
+ }
46
+ });
@@ -0,0 +1,45 @@
1
+ import type { PropType } from 'vue';
2
+ import type { VersionedSlotId } from '@croct/plug/slot';
3
+ import type { JsonObject } from '@croct/plug/sdk/json';
4
+ declare const _default: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
5
+ id: {
6
+ type: PropType<VersionedSlotId>;
7
+ required: true;
8
+ };
9
+ fallback: {
10
+ type: PropType<JsonObject>;
11
+ default: undefined;
12
+ };
13
+ preferredLocale: {
14
+ type: StringConstructor;
15
+ default: undefined;
16
+ };
17
+ attributes: {
18
+ type: PropType<JsonObject>;
19
+ default: undefined;
20
+ };
21
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
22
+ [key: string]: any;
23
+ }>[] | undefined, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
24
+ id: {
25
+ type: PropType<VersionedSlotId>;
26
+ required: true;
27
+ };
28
+ fallback: {
29
+ type: PropType<JsonObject>;
30
+ default: undefined;
31
+ };
32
+ preferredLocale: {
33
+ type: StringConstructor;
34
+ default: undefined;
35
+ };
36
+ attributes: {
37
+ type: PropType<JsonObject>;
38
+ default: undefined;
39
+ };
40
+ }>> & Readonly<{}>, {
41
+ fallback: JsonObject;
42
+ attributes: JsonObject;
43
+ preferredLocale: string;
44
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
45
+ export default _default;
@@ -0,0 +1,52 @@
1
+ import { defineComponent } from "vue";
2
+ import { useAsyncData } from "#app";
3
+ import { resolveLocale } from "../utils/locale.js";
4
+ export default defineComponent({
5
+ name: "Slot",
6
+ props: {
7
+ id: {
8
+ type: String,
9
+ required: true
10
+ },
11
+ fallback: {
12
+ type: [Object, String, Number, Boolean, null],
13
+ default: void 0
14
+ },
15
+ preferredLocale: {
16
+ type: String,
17
+ default: void 0
18
+ },
19
+ attributes: {
20
+ type: Object,
21
+ default: void 0
22
+ }
23
+ },
24
+ setup: async function(props, { slots }) {
25
+ const locale = resolveLocale(props.preferredLocale);
26
+ const options = {
27
+ ...props.fallback !== void 0 ? { fallback: props.fallback } : {},
28
+ ...locale !== void 0 ? { preferredLocale: locale } : {},
29
+ ...props.attributes !== void 0 ? { attributes: props.attributes } : {}
30
+ };
31
+ const { data, error } = await useAsyncData(
32
+ `croct:slot:${props.id}:${JSON.stringify(options)}`,
33
+ () => $fetch("/api/_croct/content", {
34
+ method: "POST",
35
+ body: { slotId: props.id, ...options }
36
+ })
37
+ );
38
+ return () => {
39
+ const response = data.value;
40
+ if (response !== null && typeof response === "object" && "content" in response) {
41
+ return slots.default?.({
42
+ content: response.content,
43
+ ...response.metadata !== void 0 ? { metadata: response.metadata } : {}
44
+ });
45
+ }
46
+ if (error.value !== null) {
47
+ return slots.error?.({ error: error.value });
48
+ }
49
+ return slots.loading?.();
50
+ };
51
+ }
52
+ });
@@ -0,0 +1,15 @@
1
+ import type { JsonObject } from '@croct/plug/sdk/json';
2
+ import type { VersionedSlotId, VersionedSlotMap } from '@croct/plug/slot';
3
+ import type { FetchResponseOptions } from '@croct/sdk/contentFetcher';
4
+ import type { FetchResponse } from '@croct/plug/api';
5
+ import type { AsyncData, NuxtError } from '#app';
6
+ import type { DynamicContentOptions } from '../server/composables/fetchContent.js';
7
+ export type UseContentOptions<T extends JsonObject = JsonObject> = DynamicContentOptions<T>;
8
+ type UseContentHook = {
9
+ <P extends JsonObject, O extends FetchResponseOptions = FetchResponseOptions>(id: keyof VersionedSlotMap extends never ? string : never, options?: O & UseContentOptions): AsyncData<FetchResponse<VersionedSlotId, P, never, O>, NuxtError | null>;
10
+ <P extends JsonObject, F extends JsonObject, O extends FetchResponseOptions = FetchResponseOptions>(id: keyof VersionedSlotMap extends never ? string : never, options: O & UseContentOptions<F>): AsyncData<FetchResponse<VersionedSlotId, P, F, O>, NuxtError | null>;
11
+ <S extends VersionedSlotId, O extends FetchResponseOptions = FetchResponseOptions>(id: S, options?: O & UseContentOptions): AsyncData<FetchResponse<S, JsonObject, never, O>, NuxtError | null>;
12
+ <F extends JsonObject, S extends VersionedSlotId, O extends FetchResponseOptions = FetchResponseOptions>(id: S, options: O & UseContentOptions<F>): AsyncData<FetchResponse<S, JsonObject, F, O>, NuxtError | null>;
13
+ };
14
+ export declare const useContent: UseContentHook;
15
+ export {};
@@ -0,0 +1,19 @@
1
+ import { useAsyncData } from "#app";
2
+ import { resolveLocale } from "../utils/locale.js";
3
+ function useContentNuxt(slotId, options = {}) {
4
+ const { preferredLocale, ...rest } = options;
5
+ const locale = resolveLocale(preferredLocale);
6
+ const resolvedOptions = {
7
+ ...locale !== void 0 ? { preferredLocale: locale } : {},
8
+ ...rest
9
+ };
10
+ const cacheKey = `croct:content:${slotId}:${JSON.stringify(resolvedOptions)}`;
11
+ return useAsyncData(
12
+ cacheKey,
13
+ () => $fetch("/api/_croct/content", {
14
+ method: "POST",
15
+ body: { slotId, ...resolvedOptions }
16
+ })
17
+ );
18
+ }
19
+ export const useContent = useContentNuxt;
@@ -0,0 +1 @@
1
+ export { useCroct } from '@croct/plug-vue';
@@ -0,0 +1 @@
1
+ export { useCroct } from "@croct/plug-vue";
@@ -0,0 +1,12 @@
1
+ import type { JsonValue } from '@croct/plug/sdk/json';
2
+ import type { AsyncData, NuxtError } from '#app';
3
+ import type { EvaluationOptions as BaseEvaluationOptions } from '../server/composables/evaluate.js';
4
+ export type EvaluationOptions<T extends JsonValue = JsonValue> = BaseEvaluationOptions<T>;
5
+ type UseEvaluationHook = {
6
+ <T extends JsonValue = JsonValue>(query: string, options?: EvaluationOptions<T>): AsyncData<T, NuxtError | null>;
7
+ <T extends JsonValue, F>(query: string, options: EvaluationOptions<T> & {
8
+ fallback: F;
9
+ }): AsyncData<T | F, NuxtError | null>;
10
+ };
11
+ export declare const useEvaluation: UseEvaluationHook;
12
+ export {};
@@ -0,0 +1,12 @@
1
+ import { useAsyncData } from "#app";
2
+ function useEvaluationNuxt(query, options = {}) {
3
+ const cacheKey = `croct:eval:${query}:${JSON.stringify(options)}`;
4
+ return useAsyncData(
5
+ cacheKey,
6
+ () => $fetch("/api/_croct/evaluate", {
7
+ method: "POST",
8
+ body: { query, ...options }
9
+ })
10
+ );
11
+ }
12
+ export const useEvaluation = useEvaluationNuxt;
@@ -0,0 +1,3 @@
1
+ export { useContent } from './useContent.js';
2
+ export { useCroct, useEvaluation, Slot, Personalization, createCroct } from '@croct/plug-vue';
3
+ export type { UseContentOptions, UseContentResult, ContentMetadata, UseEvaluationOptions } from '@croct/plug-vue';
@@ -0,0 +1,2 @@
1
+ export { useContent } from "./useContent.js";
2
+ export { useCroct, useEvaluation, Slot, Personalization, createCroct } from "@croct/plug-vue";
@@ -0,0 +1,2 @@
1
+ import { useContent as useContentVue } from '@croct/plug-vue';
2
+ export declare const useContent: typeof useContentVue;
@@ -0,0 +1,12 @@
1
+ import { toValue } from "vue";
2
+ import { useContent as useContentVue } from "@croct/plug-vue";
3
+ import { resolveLocale } from "../utils/locale.js";
4
+ function useContentNuxt(id, options = {}) {
5
+ const resolvedOptions = toValue(options);
6
+ const locale = resolveLocale(resolvedOptions.preferredLocale);
7
+ return useContentVue(id, {
8
+ ...resolvedOptions,
9
+ ...locale !== void 0 ? { preferredLocale: locale } : {}
10
+ });
11
+ }
12
+ export const useContent = useContentNuxt;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import { createCroct } from "@croct/plug-vue";
2
+ import { defineNuxtPlugin, useRuntimeConfig } from "#app";
3
+ import { urlSanitizer } from "#croct/client-options";
4
+ export default defineNuxtPlugin((nuxtApp) => {
5
+ const config = useRuntimeConfig().public.croct;
6
+ const plugin = createCroct({
7
+ appId: config.appId,
8
+ disableCidMirroring: true,
9
+ ...config.debug === true ? { debug: true } : {},
10
+ ...config.test === true ? { test: true } : {},
11
+ ...urlSanitizer !== void 0 ? { urlSanitizer } : {},
12
+ ...typeof config.baseEndpointUrl === "string" && config.baseEndpointUrl !== "" ? { baseEndpointUrl: config.baseEndpointUrl } : {},
13
+ ...config.defaultPreferredLocale !== "" ? { defaultPreferredLocale: config.defaultPreferredLocale } : {},
14
+ ...typeof config.defaultFetchTimeout === "number" ? { defaultFetchTimeout: config.defaultFetchTimeout } : {},
15
+ cookie: {
16
+ clientId: {
17
+ name: config.cookie.clientId.name,
18
+ maxAge: config.cookie.clientId.duration,
19
+ path: "/",
20
+ ...config.cookie.clientId.domain !== "" ? { domain: config.cookie.clientId.domain } : {}
21
+ },
22
+ userToken: {
23
+ name: config.cookie.userToken.name,
24
+ maxAge: config.cookie.userToken.duration,
25
+ path: "/",
26
+ ...config.cookie.userToken.domain !== "" ? { domain: config.cookie.userToken.domain } : {}
27
+ },
28
+ previewToken: {
29
+ name: config.cookie.previewToken.name,
30
+ path: "/",
31
+ ...config.cookie.previewToken.domain !== "" ? { domain: config.cookie.previewToken.domain } : {}
32
+ }
33
+ }
34
+ });
35
+ nuxtApp.vueApp.use(plugin);
36
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: globalThis.EventHandler<globalThis.EventHandlerRequest, Promise<globalThis.FetchResponse<any, import("@croct/json").JsonObject, never, import("@croct/sdk/contentFetcher").FetchResponseOptions>>>;
2
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import { readBody } from "h3";
2
+ import { fetchContent } from "../../composables/fetchContent.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const { slotId, ...options } = await readBody(event);
5
+ return fetchContent(slotId, options);
6
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: globalThis.EventHandler<globalThis.EventHandlerRequest, Promise<import("@croct/json").JsonValue>>;
2
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import { readBody } from "h3";
2
+ import { evaluate } from "../../composables/evaluate.js";
3
+ export default defineEventHandler(async (event) => {
4
+ const { query, ...options } = await readBody(event);
5
+ return evaluate(query, options);
6
+ });
@@ -0,0 +1 @@
1
+ export declare function anonymize(): Promise<void>;
@@ -0,0 +1,6 @@
1
+ import { useEvent } from "#imports";
2
+ import { issueToken } from "../utils/security.js";
3
+ import { setUserTokenCookie } from "../utils/cookie.js";
4
+ export async function anonymize() {
5
+ setUserTokenCookie(useEvent(), await issueToken());
6
+ }
@@ -0,0 +1,4 @@
1
+ import type { JsonValue } from '@croct/plug/sdk/json';
2
+ import type { EvaluationOptions as BaseEvaluationOptions } from '@croct/plug/api';
3
+ export type EvaluationOptions<T extends JsonValue = JsonValue> = Omit<BaseEvaluationOptions<T>, 'apiKey' | 'appId'>;
4
+ export declare function evaluate<T extends JsonValue>(query: string, options?: EvaluationOptions<T>): Promise<T>;
@@ -0,0 +1,37 @@
1
+ import { evaluate as executeQuery } from "@croct/plug/api";
2
+ import { FilteredLogger } from "@croct/sdk/logging/filteredLogger";
3
+ import { ConsoleLogger } from "@croct/sdk/logging/consoleLogger";
4
+ import { useEvent, useRuntimeConfig } from "#imports";
5
+ import { getApiKey } from "../utils/security.js";
6
+ export async function evaluate(query, options = {}) {
7
+ const event = useEvent();
8
+ const context = event.context.croct;
9
+ if (context === void 0) {
10
+ throw new Error(
11
+ "Croct's request context is missing. Make sure the @croct/plug-nuxt module is installed in your nuxt.config.ts. For help, see: https://croct.help/sdk/nuxt/missing-middleware"
12
+ );
13
+ }
14
+ const config = useRuntimeConfig();
15
+ const timeout = config.public.croct.defaultFetchTimeout;
16
+ return executeQuery(query, {
17
+ apiKey: getApiKey(),
18
+ clientIp: context.clientIp ?? "127.0.0.1",
19
+ ...context.previewToken !== void 0 ? { previewToken: context.previewToken } : {},
20
+ ...context.userToken !== void 0 ? { userToken: context.userToken } : {},
21
+ ...context.clientId !== void 0 ? { clientId: context.clientId } : {},
22
+ ...context.clientAgent !== void 0 ? { clientAgent: context.clientAgent } : {},
23
+ ...typeof config.public.croct.baseEndpointUrl === "string" && config.public.croct.baseEndpointUrl !== "" ? { baseEndpointUrl: config.public.croct.baseEndpointUrl } : {},
24
+ ...typeof timeout === "number" && timeout > 0 ? { timeout } : {},
25
+ extra: { cache: "no-store" },
26
+ logger: FilteredLogger.include(new ConsoleLogger(), ["warn", "error"]),
27
+ ...options,
28
+ ...context.uri !== void 0 ? {
29
+ context: {
30
+ page: {
31
+ url: context.uri,
32
+ ...context.referrer !== void 0 ? { referrer: context.referrer } : {}
33
+ }
34
+ }
35
+ } : {}
36
+ });
37
+ }
@@ -0,0 +1,9 @@
1
+ import type { SlotContent, VersionedSlotId } from '@croct/plug/slot';
2
+ import type { JsonObject } from '@croct/plug/sdk/json';
3
+ import type { FetchResponseOptions } from '@croct/sdk/contentFetcher';
4
+ import type { DynamicContentOptions as DynamicOptions, StaticContentOptions as StaticOptions, FetchResponse } from '@croct/plug/api';
5
+ export type DynamicContentOptions<T extends JsonObject = JsonObject> = Omit<DynamicOptions<T>, 'apiKey' | 'appId'>;
6
+ export type StaticContentOptions<T extends JsonObject = JsonObject> = Omit<StaticOptions<T>, 'apiKey' | 'appId'>;
7
+ export type FetchContentOptions<T extends JsonObject = JsonObject> = DynamicContentOptions<T> | StaticContentOptions<T>;
8
+ export type { FetchResponse };
9
+ export declare function fetchContent<I extends VersionedSlotId, C extends JsonObject, O extends FetchResponseOptions = FetchResponseOptions>(slotId: I, options?: Pick<O, keyof FetchResponseOptions> & FetchContentOptions<SlotContent<I, C>>): Promise<FetchResponse<I, C, never, O>>;
@@ -0,0 +1,50 @@
1
+ import { fetchContent as loadContent } from "@croct/plug/api";
2
+ import { FilteredLogger } from "@croct/sdk/logging/filteredLogger";
3
+ import { ConsoleLogger } from "@croct/sdk/logging/consoleLogger";
4
+ import { useEvent, useRuntimeConfig } from "#imports";
5
+ import { getApiKey } from "../utils/security.js";
6
+ export async function fetchContent(slotId, options = {}) {
7
+ const event = useEvent();
8
+ const context = event.context.croct;
9
+ if (context === void 0) {
10
+ throw new Error(
11
+ "Croct's request context is missing. Make sure the @croct/plug-nuxt module is installed in your nuxt.config.ts. For help, see: https://croct.help/sdk/nuxt/missing-middleware"
12
+ );
13
+ }
14
+ const config = useRuntimeConfig();
15
+ const timeout = config.public.croct.defaultFetchTimeout;
16
+ const { logger, ...rest } = options;
17
+ const commonOptions = {
18
+ apiKey: getApiKey(),
19
+ ...typeof config.public.croct.baseEndpointUrl === "string" && config.public.croct.baseEndpointUrl !== "" ? { baseEndpointUrl: config.public.croct.baseEndpointUrl } : {},
20
+ ...typeof timeout === "number" && timeout > 0 ? { timeout } : {},
21
+ logger: logger ?? FilteredLogger.include(new ConsoleLogger(), ["warn", "error"])
22
+ };
23
+ if ("static" in rest && rest.static === true) {
24
+ const preferredLocale = rest.preferredLocale ?? (config.public.croct.defaultPreferredLocale !== "" ? config.public.croct.defaultPreferredLocale : void 0);
25
+ return loadContent(slotId, {
26
+ ...commonOptions,
27
+ ...rest,
28
+ ...preferredLocale !== void 0 ? { preferredLocale } : {}
29
+ });
30
+ }
31
+ return loadContent(slotId, {
32
+ clientIp: context.clientIp,
33
+ ...context.previewToken !== void 0 ? { previewToken: context.previewToken } : {},
34
+ ...context.userToken !== void 0 ? { userToken: context.userToken } : {},
35
+ ...context.clientId !== void 0 ? { clientId: context.clientId } : {},
36
+ ...context.clientAgent !== void 0 ? { clientAgent: context.clientAgent } : {},
37
+ ...context.preferredLocale !== void 0 ? { preferredLocale: context.preferredLocale } : {},
38
+ ...context.uri !== void 0 ? {
39
+ context: {
40
+ page: {
41
+ url: context.uri,
42
+ ...context.referrer !== void 0 ? { referrer: context.referrer } : {}
43
+ }
44
+ }
45
+ } : {},
46
+ extra: { cache: "no-store" },
47
+ ...commonOptions,
48
+ ...rest
49
+ });
50
+ }
@@ -0,0 +1 @@
1
+ export declare function identify(userId: string): Promise<void>;
@@ -0,0 +1,6 @@
1
+ import { useEvent } from "#imports";
2
+ import { issueToken } from "../utils/security.js";
3
+ import { setUserTokenCookie } from "../utils/cookie.js";
4
+ export async function identify(userId) {
5
+ setUserTokenCookie(useEvent(), await issueToken(userId));
6
+ }
@@ -0,0 +1,4 @@
1
+ import type {H3Event} from 'h3';
2
+
3
+ export type UserIdResolver = (event: H3Event) => Promise<string | null> | string | null;
4
+ export type LocaleResolver = (event: H3Event) => Promise<string | null> | string | null;
@@ -0,0 +1,2 @@
1
+ declare const _default: globalThis.EventHandler<globalThis.EventHandlerRequest, Promise<void>>;
2
+ export default _default;
@@ -0,0 +1,138 @@
1
+ import {
2
+ defineEventHandler,
3
+ getRequestURL,
4
+ getHeader,
5
+ getCookie,
6
+ setCookie,
7
+ deleteCookie,
8
+ getQuery
9
+ } from "h3";
10
+ import { Token } from "@croct/sdk/token";
11
+ import { base64UrlDecode } from "@croct/sdk/base64Url";
12
+ import { useRuntimeConfig } from "#imports";
13
+ import { localeResolver, userIdResolver } from "#croct/resolvers";
14
+ import { setUserTokenCookie, getProductionDefaults } from "../utils/cookie.js";
15
+ import { getAuthenticationKey, isUserTokenAuthenticationEnabled, issueToken } from "../utils/security.js";
16
+ const CLIENT_ID_PATTERN = /^(?:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}|[a-f0-9]{32})$/;
17
+ const PREVIEW_QUERY_PARAM = "croct-preview";
18
+ const SKIP_EXTENSIONS = /\.(?:js|css|map|ico|png|jpg|jpeg|gif|svg|woff2?|ttf|eot|webp|avif|json|xml|txt)$/;
19
+ const SKIP_PREFIXES = ["/_nuxt/", "/__nuxt_error"];
20
+ function shouldSkip(pathname) {
21
+ if (SKIP_EXTENSIONS.test(pathname)) {
22
+ return true;
23
+ }
24
+ return SKIP_PREFIXES.some((prefix) => pathname.startsWith(prefix));
25
+ }
26
+ export default defineEventHandler(async (event) => {
27
+ const url = getRequestURL(event);
28
+ if (shouldSkip(url.pathname)) {
29
+ return;
30
+ }
31
+ const config = useRuntimeConfig();
32
+ const { cookie } = config.public.croct;
33
+ const clientId = resolveClientId(getCookie(event, cookie.clientId.name));
34
+ const userToken = await resolveUserToken(event, getCookie(event, cookie.userToken.name));
35
+ const preferredLocale = await resolvePreferredLocale(event, config.public.croct.defaultPreferredLocale);
36
+ const previewToken = resolvePreviewToken(
37
+ getQuery(event)[PREVIEW_QUERY_PARAM],
38
+ getCookie(event, cookie.previewToken.name)
39
+ );
40
+ const clientIp = getHeader(event, "x-forwarded-for")?.split(",")[0].trim() ?? getHeader(event, "x-real-ip") ?? void 0;
41
+ const context = {
42
+ clientId,
43
+ userToken: userToken.toString(),
44
+ uri: stripPreviewParam(url),
45
+ ...clientIp !== void 0 ? { clientIp } : {},
46
+ ...preferredLocale !== void 0 ? { preferredLocale } : {},
47
+ ...previewToken !== null && previewToken !== "exit" ? { previewToken } : {},
48
+ clientAgent: getHeader(event, "user-agent"),
49
+ referrer: getHeader(event, "referer")
50
+ };
51
+ event.context.croct = context;
52
+ const productionDefaults = getProductionDefaults();
53
+ if (previewToken === "exit") {
54
+ deleteCookie(event, cookie.previewToken.name);
55
+ } else if (previewToken !== null) {
56
+ setCookie(event, cookie.previewToken.name, previewToken, {
57
+ path: "/",
58
+ ...cookie.previewToken.domain !== "" ? { domain: cookie.previewToken.domain } : {},
59
+ ...productionDefaults,
60
+ httpOnly: true
61
+ });
62
+ }
63
+ setUserTokenCookie(event, userToken);
64
+ setCookie(event, cookie.clientId.name, clientId, {
65
+ maxAge: cookie.clientId.duration,
66
+ path: "/",
67
+ ...cookie.clientId.domain !== "" ? { domain: cookie.clientId.domain } : {},
68
+ ...productionDefaults,
69
+ httpOnly: true
70
+ });
71
+ });
72
+ function resolveClientId(cookieValue) {
73
+ if (cookieValue !== void 0 && CLIENT_ID_PATTERN.test(cookieValue)) {
74
+ return cookieValue;
75
+ }
76
+ return crypto.randomUUID();
77
+ }
78
+ async function resolveUserToken(event, cookieValue) {
79
+ const { appId } = useRuntimeConfig().public.croct;
80
+ let token = null;
81
+ if (cookieValue !== void 0) {
82
+ try {
83
+ token = Token.parse(cookieValue);
84
+ } catch {
85
+ }
86
+ }
87
+ const userId = userIdResolver !== void 0 ? await userIdResolver(event) : void 0;
88
+ if (token === null || isUserTokenAuthenticationEnabled() && !token.isSigned() || !token.isValidNow() || userId !== void 0 && (userId === null ? !token.isAnonymous() : !token.isSubject(userId))) {
89
+ return issueToken(userId ?? null);
90
+ }
91
+ const tokenAppId = token.getApplicationId();
92
+ if (tokenAppId !== null && tokenAppId !== appId) {
93
+ return issueToken(userId ?? null);
94
+ }
95
+ if (token.isSigned() && !await token.matchesKeyId(getAuthenticationKey())) {
96
+ return issueToken(token.getSubject(), token.getTokenId() ?? void 0);
97
+ }
98
+ return token;
99
+ }
100
+ async function resolvePreferredLocale(event, defaultLocale) {
101
+ if (localeResolver !== void 0) {
102
+ const locale = await localeResolver(event);
103
+ if (locale !== null && locale !== "") {
104
+ return locale;
105
+ }
106
+ }
107
+ if (defaultLocale !== "") {
108
+ return defaultLocale;
109
+ }
110
+ return void 0;
111
+ }
112
+ function resolvePreviewToken(queryParam, cookieValue) {
113
+ const value = queryParam ?? cookieValue;
114
+ if (value === void 0) {
115
+ return null;
116
+ }
117
+ if (isPreviewTokenValid(value)) {
118
+ return value;
119
+ }
120
+ return "exit";
121
+ }
122
+ function isPreviewTokenValid(token) {
123
+ if (typeof token !== "string" || token === "exit") {
124
+ return false;
125
+ }
126
+ const now = Math.floor(Date.now() / 1e3);
127
+ try {
128
+ const payload = JSON.parse(base64UrlDecode(token.split(".")[1]).toString());
129
+ return Number.isInteger(payload.exp) && payload.exp > now;
130
+ } catch {
131
+ return false;
132
+ }
133
+ }
134
+ function stripPreviewParam(url) {
135
+ const cleaned = new URL(url.toString());
136
+ cleaned.searchParams.delete(PREVIEW_QUERY_PARAM);
137
+ return cleaned.toString();
138
+ }
@@ -0,0 +1,8 @@
1
+ import type { H3Event } from 'h3';
2
+ import type { Token } from '@croct/sdk/token';
3
+ declare function getProductionDefaults(): {
4
+ secure?: boolean;
5
+ sameSite?: 'none';
6
+ };
7
+ export declare function setUserTokenCookie(event: H3Event, token: Token): void;
8
+ export { getProductionDefaults };
@@ -0,0 +1,19 @@
1
+ import { setCookie } from "h3";
2
+ import { useRuntimeConfig } from "#imports";
3
+ function getProductionDefaults() {
4
+ if (process.env.NODE_ENV !== "production") {
5
+ return {};
6
+ }
7
+ return { secure: true, sameSite: "none" };
8
+ }
9
+ export function setUserTokenCookie(event, token) {
10
+ const config = useRuntimeConfig().public.croct.cookie.userToken;
11
+ setCookie(event, config.name, token.toString(), {
12
+ maxAge: config.duration,
13
+ path: "/",
14
+ ...config.domain !== "" ? { domain: config.domain } : {},
15
+ ...getProductionDefaults(),
16
+ httpOnly: true
17
+ });
18
+ }
19
+ export { getProductionDefaults };
@@ -0,0 +1,6 @@
1
+ import { ApiKey } from '@croct/sdk/apiKey';
2
+ import { Token } from '@croct/sdk/token';
3
+ export declare function getApiKey(): ApiKey;
4
+ export declare function getAuthenticationKey(): ApiKey;
5
+ export declare function isUserTokenAuthenticationEnabled(): boolean;
6
+ export declare function issueToken(userId?: string | null, tokenId?: string): Promise<Token>;
@@ -0,0 +1,40 @@
1
+ import { ApiKey } from "@croct/sdk/apiKey";
2
+ import { Token } from "@croct/sdk/token";
3
+ import { useRuntimeConfig } from "#imports";
4
+ export function getApiKey() {
5
+ const { apiKey } = useRuntimeConfig().croct;
6
+ if (apiKey === "") {
7
+ throw new Error(
8
+ "Croct's API key is missing. Did you forget to set the NUXT_CROCT_API_KEY environment variable? For help, see: https://croct.help/sdk/nuxt/missing-environment-variable"
9
+ );
10
+ }
11
+ try {
12
+ return ApiKey.parse(apiKey);
13
+ } catch {
14
+ throw new Error(
15
+ "Croct's API key is invalid. Please check the NUXT_CROCT_API_KEY environment variable."
16
+ );
17
+ }
18
+ }
19
+ export function getAuthenticationKey() {
20
+ const apiKey = getApiKey();
21
+ if (!apiKey.hasPrivateKey()) {
22
+ throw new Error(
23
+ "Croct's API key does not have a private key. Please generate an API key with authenticate permissions and update the NUXT_CROCT_API_KEY environment variable."
24
+ );
25
+ }
26
+ return apiKey;
27
+ }
28
+ export function isUserTokenAuthenticationEnabled() {
29
+ const config = useRuntimeConfig().croct;
30
+ return config.apiKey !== "" && config.disableUserTokenAuthentication !== true;
31
+ }
32
+ export function issueToken(userId = null, tokenId) {
33
+ const config = useRuntimeConfig();
34
+ const { appId } = config.public.croct;
35
+ const token = Token.issue(appId, userId).withDuration(config.croct.tokenDuration);
36
+ if (isUserTokenAuthenticationEnabled()) {
37
+ return token.withTokenId(tokenId ?? crypto.randomUUID()).signedWith(getAuthenticationKey());
38
+ }
39
+ return Promise.resolve(token);
40
+ }
@@ -0,0 +1,5 @@
1
+ export type * from '@croct/plug/sdk/json';
2
+ export type * from '@croct/plug/slot';
3
+ export type * from '@croct/plug/component';
4
+ export type { UrlSanitizer } from '@croct/sdk/tab';
5
+ export type { UserIdResolver, LocaleResolver } from './server/croct-resolvers.js';
File without changes
@@ -0,0 +1,9 @@
1
+ import type { Ref } from 'vue';
2
+ declare module '#app' {
3
+ interface NuxtApp {
4
+ $i18n?: {
5
+ locale: Ref<string>;
6
+ };
7
+ }
8
+ }
9
+ export declare function resolveLocale(explicitLocale?: string): string | undefined;
@@ -0,0 +1,15 @@
1
+ import { useNuxtApp, useRuntimeConfig } from "#app";
2
+ export function resolveLocale(explicitLocale) {
3
+ if (explicitLocale !== void 0 && explicitLocale !== "") {
4
+ return explicitLocale;
5
+ }
6
+ const config = useRuntimeConfig().public.croct;
7
+ if (config.defaultPreferredLocale !== "") {
8
+ return config.defaultPreferredLocale;
9
+ }
10
+ const i18nLocale = useNuxtApp().$i18n?.locale.value;
11
+ if (i18nLocale !== void 0) {
12
+ return i18nLocale;
13
+ }
14
+ return void 0;
15
+ }
@@ -0,0 +1,7 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.js'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.js'
@@ -0,0 +1,7 @@
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croct/plug-nuxt",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Nuxt module to plug your Nuxt 3 applications into Croct.",
5
5
  "author": {
6
6
  "name": "Croct",
@@ -40,8 +40,9 @@
40
40
  "url": "git+https://github.com/croct-tech/plug-nuxt.git"
41
41
  },
42
42
  "scripts": {
43
+ "prepare": "nuxi prepare e2e/app",
44
+ "prepack": "nuxt-module-build build",
43
45
  "dev": "nuxi dev e2e/app",
44
- "postinstall": "nuxi prepare e2e/app",
45
46
  "dev:build": "nuxi build e2e/app",
46
47
  "build": "nuxt-module-build build",
47
48
  "lint": "eslint .",