@monkeyplus/flow 6.0.12 → 6.0.14

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 (54) hide show
  1. package/README.md +5 -0
  2. package/modules/content/module.mjs +12 -0
  3. package/modules/content/query.d.ts +1 -1
  4. package/modules/content/query.mjs +157 -57
  5. package/modules/netlify-cms/handler.d.ts +2 -0
  6. package/modules/netlify-cms/handler.mjs +5 -0
  7. package/modules/netlify-cms/module.d.ts +6 -0
  8. package/modules/netlify-cms/module.mjs +66 -0
  9. package/modules/netlify-cms/resources/admin.html +16 -0
  10. package/modules/netlify-cms/resources/adminV2.html +15 -0
  11. package/modules/netlify-cms/runtime/cms.d.ts +3 -0
  12. package/modules/netlify-cms/runtime/cms.mjs +3 -0
  13. package/modules/netlify-cms/server/api/admin.d.ts +2 -0
  14. package/modules/netlify-cms/server/api/admin.mjs +20 -0
  15. package/modules/netlify-cms/server/api/config.d.ts +2 -0
  16. package/modules/netlify-cms/server/api/config.mjs +72 -0
  17. package/modules/netlify-cms/server/api/local-fs.d.ts +2 -0
  18. package/modules/netlify-cms/server/api/local-fs.mjs +189 -0
  19. package/modules/netlify-cms/server/api/meta.d.ts +2 -0
  20. package/modules/netlify-cms/server/api/meta.mjs +8 -0
  21. package/modules/netlify-cms/server/lib/cms/handler.d.ts +2 -0
  22. package/modules/netlify-cms/server/lib/cms/handler.mjs +37 -0
  23. package/modules/netlify-cms/server/lib/cms/handlerV1.d.ts +2 -0
  24. package/modules/netlify-cms/server/lib/cms/handlerV1.mjs +40 -0
  25. package/modules/netlify-cms/server/lib/cms/helpers.d.ts +14 -0
  26. package/modules/netlify-cms/server/lib/cms/helpers.mjs +76 -0
  27. package/modules/netlify-cms/server/lib/cms/widgets.d.ts +113 -0
  28. package/modules/netlify-cms/server/lib/cms/widgets.mjs +168 -0
  29. package/modules/netlify-cms/server/lib/composables.d.ts +28 -0
  30. package/modules/netlify-cms/server/lib/composables.mjs +14 -0
  31. package/modules/netlify-cms/server/lib/entries.d.ts +25 -0
  32. package/modules/netlify-cms/server/lib/entries.mjs +39 -0
  33. package/modules/netlify-cms/server/lib/fs.d.ts +5 -0
  34. package/modules/netlify-cms/server/lib/fs.mjs +43 -0
  35. package/modules/netlify-cms/server/lib/types/collections.d.ts +16 -0
  36. package/modules/netlify-cms/server/lib/types/collections.mjs +0 -0
  37. package/modules/netlify-cms/server/lib/types/helpers.d.ts +23 -0
  38. package/modules/netlify-cms/server/lib/types/helpers.mjs +0 -0
  39. package/modules/netlify-cms/server/lib/types/index.d.ts +23 -0
  40. package/modules/netlify-cms/server/lib/types/index.mjs +11 -0
  41. package/modules/netlify-cms/server/lib/types/widgets.d.ts +39 -0
  42. package/modules/netlify-cms/server/lib/types/widgets.mjs +0 -0
  43. package/package.json +1 -1
  44. package/server/lib/handler.mjs +2 -2
  45. package/server/lib/pages.mjs +24 -3
  46. package/server/renderer.mjs +4 -0
  47. package/src/public/nitro.mjs +2 -0
  48. package/src/public/query-content.d.ts +8 -0
  49. package/src/public/query-content.mjs +103 -37
  50. package/src/runtime/config.d.ts +7 -0
  51. package/src/runtime/modules.mjs +2 -1
  52. package/src/runtime/nitro-plugin.d.ts +1 -0
  53. package/src/runtime/nitro-plugin.mjs +5 -0
  54. package/src/runtime/page-discovery.mjs +24 -3
@@ -0,0 +1,14 @@
1
+ import { join } from "node:path";
2
+ export function defineCmsCollection(options) {
3
+ return (ctx) => {
4
+ const setupFn = options.setup(ctx);
5
+ const collectionFn = setupFn(join(options.group, options.dir));
6
+ return (baseDir, ctx2) => {
7
+ const collection = collectionFn(baseDir, ctx2);
8
+ return {
9
+ ...collection,
10
+ group: options.group
11
+ };
12
+ };
13
+ };
14
+ }
@@ -0,0 +1,25 @@
1
+ export declare function entriesFromFiles(repoPath: string, files: {
2
+ path: string;
3
+ label?: string;
4
+ }[]): Promise<({
5
+ data: string;
6
+ file: {
7
+ path: string;
8
+ label: string | undefined;
9
+ id: any;
10
+ };
11
+ } | {
12
+ data: null;
13
+ file: {
14
+ path: string;
15
+ label: string | undefined;
16
+ id: null;
17
+ };
18
+ })[]>;
19
+ export declare function readMediaFile(repoPath: string, file: string): Promise<{
20
+ id: any;
21
+ content: string;
22
+ encoding: string;
23
+ path: string;
24
+ name: any;
25
+ }>;
@@ -0,0 +1,39 @@
1
+ import crypto from "node:crypto";
2
+ import { promises as fs } from "node:fs";
3
+ import path from "node:path";
4
+ function sha256(buffer) {
5
+ return crypto.createHash("sha256").update(buffer).digest("hex");
6
+ }
7
+ function normalizePath(path2) {
8
+ return path2.replace(/\\/g, "/");
9
+ }
10
+ export async function entriesFromFiles(repoPath, files) {
11
+ return Promise.all(
12
+ files.map(async (file) => {
13
+ try {
14
+ const content = await fs.readFile(path.join(repoPath, file.path));
15
+ return {
16
+ data: content.toString(),
17
+ file: { path: normalizePath(file.path), label: file.label, id: sha256(content) }
18
+ };
19
+ } catch (e) {
20
+ return {
21
+ data: null,
22
+ file: { path: normalizePath(file.path), label: file.label, id: null }
23
+ };
24
+ }
25
+ })
26
+ );
27
+ }
28
+ export async function readMediaFile(repoPath, file) {
29
+ const encoding = "base64";
30
+ const buffer = await fs.readFile(path.join(repoPath, file));
31
+ const id = sha256(buffer);
32
+ return {
33
+ id,
34
+ content: buffer.toString(encoding),
35
+ encoding,
36
+ path: normalizePath(file),
37
+ name: path.basename(file)
38
+ };
39
+ }
@@ -0,0 +1,5 @@
1
+ export declare function listRepoFiles(repoPath: string, folder: string, extension: string, depth: number): Promise<string[]>;
2
+ export declare function writeFile(filePath: string, content: Buffer | string): Promise<void>;
3
+ export declare function deleteFile(repoPath: string, filePath: string): Promise<void>;
4
+ export declare function move(from: string, to: string): Promise<void>;
5
+ export declare function getUpdateDate(repoPath: string, filePath: string): Promise<Date>;
@@ -0,0 +1,43 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ async function listFiles(dir, extension, depth) {
4
+ if (depth <= 0)
5
+ return [];
6
+ try {
7
+ const dirents = await fs.readdir(dir, { withFileTypes: true });
8
+ const files = await Promise.all(
9
+ dirents.map((dirent) => {
10
+ const res = path.join(dir, dirent.name);
11
+ return dirent.isDirectory() ? listFiles(res, extension, depth - 1) : [res].filter((f) => f.endsWith(extension));
12
+ })
13
+ );
14
+ return [].concat(...files);
15
+ } catch (e) {
16
+ return [];
17
+ }
18
+ }
19
+ export async function listRepoFiles(repoPath, folder, extension, depth) {
20
+ const files = await listFiles(path.join(repoPath, folder), extension, depth);
21
+ return files.map((f) => f.substr(repoPath.length + 1));
22
+ }
23
+ export async function writeFile(filePath, content) {
24
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
25
+ await fs.writeFile(filePath, content);
26
+ }
27
+ export async function deleteFile(repoPath, filePath) {
28
+ await fs.unlink(path.join(repoPath, filePath)).catch(() => void 0);
29
+ }
30
+ async function moveFile(from, to) {
31
+ await fs.mkdir(path.dirname(to), { recursive: true });
32
+ await fs.rename(from, to);
33
+ }
34
+ export async function move(from, to) {
35
+ await moveFile(from, to);
36
+ const sourceDir = path.dirname(from);
37
+ const destDir = path.dirname(to);
38
+ const allFiles = await listFiles(sourceDir, "", 100);
39
+ await Promise.all(allFiles.map((file) => moveFile(file, file.replace(sourceDir, destDir))));
40
+ }
41
+ export async function getUpdateDate(repoPath, filePath) {
42
+ return fs.stat(path.join(repoPath, filePath)).then((stat) => stat.mtime).catch(() => /* @__PURE__ */ new Date());
43
+ }
@@ -0,0 +1,16 @@
1
+ import type { CmsCollection, CmsCollectionFile } from 'netlify-cms-core';
2
+ import type { Cms } from './index';
3
+ export declare namespace Collections {
4
+ type _Base = Omit<CmsCollection, 'files' | 'folder' | 'fields'>;
5
+ type Folder = _Base & {
6
+ folder: string;
7
+ fields: Cms.Fields[];
8
+ };
9
+ type Files = _Base & {
10
+ files: File[];
11
+ };
12
+ type File = Omit<CmsCollectionFile, 'fields'> & {
13
+ fields: Cms.Fields[];
14
+ htmlId?: string;
15
+ };
16
+ }
@@ -0,0 +1,23 @@
1
+ import type { Collections } from './collections';
2
+ import type { Cms } from './index';
3
+ export type DefineMethod<R> = (parentName: string, ctx: ContextWidget) => R;
4
+ export type DefineMethodExtend<R> = (id: string) => (parentName: string, ctx: ContextWidget) => R;
5
+ export interface ContextWidget {
6
+ rootDir: string;
7
+ }
8
+ export type DefineFile = Record<string, DefineMethodExtend<Collections.File>>;
9
+ export interface CollectionFileId extends Omit<Collections._Base, 'name'> {
10
+ files: DefineFile;
11
+ }
12
+ export interface CollectionFolderId extends Omit<Collections.Folder, 'name' | 'folder' | 'fields'> {
13
+ fields: Model.Fields;
14
+ }
15
+ export declare namespace Model {
16
+ type Fields = Record<string, (parentName: string, ctx: ContextWidget) => Cms.Fields>;
17
+ }
18
+ export declare namespace Helpers {
19
+ interface CollectionFiles {
20
+ (base: CollectionFileId): (baseDir: string) => DefineMethod<Collections.Files>;
21
+ }
22
+ }
23
+ export type DefineCms = (options: Omit<Cms.Config, 'collections'>, collections: DefineMethod<Collections.Files | Collections.Folder>[]) => (dir: string, ctx: ContextWidget) => Cms.Config;
@@ -0,0 +1,23 @@
1
+ import type { Collections } from './collections';
2
+ import type { DefineMethod } from './helpers';
3
+ import type { Widgets } from './widgets';
4
+ export { Collections } from './collections';
5
+ export { ContextWidget, DefineCms } from './helpers';
6
+ export { CollectionFileId, CollectionFolderId, DefineMethod, DefineMethodExtend, Helpers, Model, } from './helpers';
7
+ export { Widgets } from './widgets';
8
+ export declare namespace Cms {
9
+ type Config = Record<string, any> & {
10
+ collections: (Collections.Folder | Collections.Files)[];
11
+ };
12
+ type Fields = Widgets.Boolean | Widgets.Markdown | Widgets.Code | Widgets.Color | Widgets.DateTime | Widgets.Hidden | Widgets.Map | Widgets.Number | Widgets.Select | Widgets.Text | Widgets.String | Widgets.Relation | Widgets.File | Widgets.Image | Widgets.Object | Widgets.List | Widgets.Date | Widgets.Meta;
13
+ }
14
+ export interface FlowNetlifyCms {
15
+ config?: Omit<Partial<Cms.Config>, 'collections'>;
16
+ functions?: {
17
+ dir: string;
18
+ proxy?: Record<string, string>;
19
+ };
20
+ omitRoutes?: boolean;
21
+ omitProxy?: boolean;
22
+ collections?: DefineMethod<Collections.Files | Collections.Folder>[];
23
+ }
@@ -0,0 +1,11 @@
1
+ export { Collections } from "./collections.mjs";
2
+ export { ContextWidget, DefineCms } from "./helpers.mjs";
3
+ export {
4
+ CollectionFileId,
5
+ CollectionFolderId,
6
+ DefineMethod,
7
+ DefineMethodExtend,
8
+ Helpers,
9
+ Model
10
+ } from "./helpers.mjs";
11
+ export { Widgets } from "./widgets.mjs";
@@ -0,0 +1,39 @@
1
+ import type { CmsFieldBase, CmsFieldBoolean, CmsFieldCode, CmsFieldColor, CmsFieldDateTime, CmsFieldFileOrImage, CmsFieldHidden, CmsFieldList, CmsFieldMap, CmsFieldMarkdown, CmsFieldMeta, CmsFieldNumber, CmsFieldObject, CmsFieldRelation, CmsFieldSelect, CmsFieldStringOrText } from 'netlify-cms-core';
2
+ import type { Cms } from './index';
3
+ interface CmsFieldExtra {
4
+ extraOptions?: Record<string, any>;
5
+ htmlId?: string;
6
+ }
7
+ export declare namespace Widgets {
8
+ type Boolean = CmsFieldExtra & CmsFieldBase & CmsFieldBoolean;
9
+ type Markdown = CmsFieldExtra & CmsFieldBase & CmsFieldMarkdown;
10
+ type Code = CmsFieldExtra & CmsFieldBase & CmsFieldCode;
11
+ type Color = CmsFieldExtra & CmsFieldBase & CmsFieldColor;
12
+ type DateTime = CmsFieldExtra & CmsFieldBase & CmsFieldDateTime;
13
+ type Hidden = CmsFieldExtra & CmsFieldBase & CmsFieldHidden;
14
+ type Map = CmsFieldExtra & CmsFieldBase & CmsFieldMap;
15
+ type Number = CmsFieldExtra & CmsFieldBase & CmsFieldNumber;
16
+ type Select = CmsFieldExtra & CmsFieldBase & CmsFieldSelect;
17
+ type Text = CmsFieldExtra & CmsFieldBase & CmsFieldStringOrText;
18
+ type String = CmsFieldExtra & CmsFieldBase & CmsFieldStringOrText;
19
+ type Relation = CmsFieldExtra & CmsFieldBase & CmsFieldRelation;
20
+ type File = CmsFieldExtra & CmsFieldBase & CmsFieldFileOrImage;
21
+ type Image = CmsFieldExtra & CmsFieldBase & CmsFieldFileOrImage;
22
+ type Meta = CmsFieldExtra & CmsFieldBase & CmsFieldMeta & {
23
+ options?: Record<string, unknown>;
24
+ };
25
+ type Object = CmsFieldExtra & CmsFieldBase & Omit<CmsFieldObject, 'fields'> & {
26
+ fields?: Cms.Fields[];
27
+ };
28
+ type List = CmsFieldExtra & CmsFieldBase & Omit<CmsFieldList, 'fields' | 'field'> & {
29
+ field?: Cms.Fields;
30
+ fields?: Cms.Fields[];
31
+ };
32
+ type Date = CmsFieldExtra & CmsFieldBase & {
33
+ widget: 'date';
34
+ format?: string;
35
+ date_format?: boolean | string;
36
+ time_format?: boolean | string;
37
+ };
38
+ }
39
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monkeyplus/flow",
3
- "version": "6.0.12",
3
+ "version": "6.0.14",
4
4
  "description": "@monkeyplus/flow package-first runtime with Vite, Nitro, Vue and a workspace playground.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -1,7 +1,7 @@
1
- import clientAssets from "../../src/main.mjs?assets=client";
2
1
  import bundleAssets from "virtual:flow/client-page-assets";
3
- import { renderDocument } from "./render.mjs";
2
+ import clientAssets from "../../src/main.mjs?assets=client";
4
3
  import { resolvePage, resolvePageByName } from "./pages.mjs";
4
+ import { renderDocument } from "./render.mjs";
5
5
  function mergeClientAssets(baseAssets, pageAssets) {
6
6
  const css = [...baseAssets.css || [], ...pageAssets?.css || []];
7
7
  const js = [...baseAssets.js || [], ...pageAssets?.js || []];
@@ -130,6 +130,26 @@ function replacePath(pattern, url) {
130
130
  function combineName(name, dynamicName) {
131
131
  return `${name.replace(/\/*$/, "")}/${dynamicName.replace(/^\/*/, "")}`;
132
132
  }
133
+ function resolveUrlTarget(namePage, explicitDynamicName) {
134
+ if (explicitDynamicName || !namePage.includes("/")) {
135
+ return {
136
+ namePage,
137
+ dynamicName: explicitDynamicName
138
+ };
139
+ }
140
+ const match = pageDefinitions.filter((candidate) => candidate.name && namePage.startsWith(`${candidate.name}/`)).sort((left, right) => right.name.length - left.name.length)[0];
141
+ if (!match) {
142
+ return {
143
+ namePage,
144
+ dynamicName: explicitDynamicName
145
+ };
146
+ }
147
+ const dynamicName = namePage.slice(match.name.length + 1);
148
+ return {
149
+ namePage: match.name,
150
+ dynamicName: dynamicName || explicitDynamicName
151
+ };
152
+ }
133
153
  function getEnabledLocaleCodes() {
134
154
  const runtimeConfig = getFlowRuntimeConfig();
135
155
  return runtimeConfig.flow?.locale?.locales || [];
@@ -163,8 +183,9 @@ export async function getUrl(namePage, localeCode, options = {}) {
163
183
  const enabledLocales = getEnabledLocaleCodes();
164
184
  const targetLocale = localeCode || enabledLocales[0];
165
185
  const runtimeConfig = getFlowRuntimeConfig();
186
+ const target = resolveUrlTarget(namePage, options.dynamicName);
166
187
  for (const definition of pageDefinitions) {
167
- if (definition.name !== namePage) {
188
+ if (definition.name !== target.namePage) {
168
189
  continue;
169
190
  }
170
191
  const localeEntries = targetLocale ? [targetLocale] : Object.keys(definition.locales);
@@ -180,11 +201,11 @@ export async function getUrl(namePage, localeCode, options = {}) {
180
201
  if (options.params) {
181
202
  return replacePath(localizedRoute, options.params);
182
203
  }
183
- if (localePage.dynamic && options.dynamicName) {
204
+ if (localePage.dynamic && target.dynamicName) {
184
205
  const locale = createLocale(code);
185
206
  const ctx = createContext(definition, locale, localePage, toPublicRoute(runtimeConfig.flow, code, localePage.url), {});
186
207
  const entries = await getDynamicEntries(definition, code, ctx);
187
- const entry = entries.find((candidate) => candidate.name === options.dynamicName);
208
+ const entry = entries.find((candidate) => candidate.name === target.dynamicName);
188
209
  if (!entry) {
189
210
  return void 0;
190
211
  }
@@ -36,6 +36,10 @@ async function resolvePrerenderFetchHandler() {
36
36
  return await prerenderFetchHandlerPromise;
37
37
  }
38
38
  export default async function renderPage(input) {
39
+ const nitroApp = globalThis.__nitro__;
40
+ if (nitroApp && nitroApp.localFetch) {
41
+ globalThis.$fetch = nitroApp.localFetch;
42
+ }
39
43
  const viteEnvs = globalThis.__nitro_vite_envs__;
40
44
  const request = resolveRequest(input);
41
45
  const viteEnv = viteEnvs?.ssr;
@@ -64,10 +64,12 @@ export function createFlowNitroConfig(options = {}) {
64
64
  plugins: flowModules.nitro.plugins,
65
65
  noExternals: [...configuredNoExternals, flowPackagePattern],
66
66
  handlers: flowModules.nitro.handlers,
67
+ storage: flowModules.nitro.storage,
67
68
  routeRules: {
68
69
  "/api/**": { cors: true },
69
70
  ...flowModules.nitro.routeRules
70
71
  },
72
+ imports: flowModules.nitro.imports,
71
73
  runtimeConfig: {
72
74
  app: {
73
75
  name: "Flow Next",
@@ -1,7 +1,15 @@
1
1
  import type { ContentEntry, ContentTreeNode } from '../../modules/content/query.ts';
2
2
  export interface QueryContentBuilder {
3
+ where: (field: string, operator: string, value?: unknown) => QueryContentBuilder;
4
+ order: (field: string, direction: 'ASC' | 'DESC' | 'asc' | 'desc') => QueryContentBuilder;
5
+ skip: (amount: number) => QueryContentBuilder;
6
+ limit: (amount: number) => QueryContentBuilder;
7
+ select: (...fields: string[]) => QueryContentBuilder;
3
8
  find: () => Promise<ContentEntry[]>;
4
9
  findOne: () => Promise<ContentEntry | null>;
10
+ all: () => Promise<ContentEntry[]>;
11
+ first: () => Promise<ContentEntry | null>;
12
+ surround: (targetPath: string) => Promise<[ContentEntry | null, ContentEntry | null]>;
5
13
  tree: () => Promise<ContentTreeNode[]>;
6
14
  }
7
15
  export declare function queryContent(path?: string): QueryContentBuilder;
@@ -27,6 +27,7 @@ function withQuery(url, params) {
27
27
  if (!params || Object.keys(params).length === 0) {
28
28
  return url;
29
29
  }
30
+ const isAbsolute = url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//");
30
31
  const origin = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
31
32
  const target = new URL(url, origin);
32
33
  Object.entries(params).forEach(([key, value]) => {
@@ -35,7 +36,7 @@ function withQuery(url, params) {
35
36
  }
36
37
  target.searchParams.set(key, String(value));
37
38
  });
38
- return target.toString();
39
+ return isAbsolute ? target.toString() : target.pathname + target.search;
39
40
  }
40
41
  async function getRuntimeConfig() {
41
42
  if (typeof window !== "undefined") {
@@ -65,44 +66,46 @@ async function resolveContentApiBase() {
65
66
  const runtimeConfig = await getRuntimeConfig();
66
67
  return runtimeConfig?.flow?.content?.apiBase || runtimeConfig?.public?.flow?.content?.apiBase || defaultContentConfig.apiBase;
67
68
  }
68
- async function resolveAbsoluteContentApiBase() {
69
- const runtimeConfig = await getRuntimeConfig();
69
+ async function fetchContent(route, queryParams) {
70
70
  const apiBase = await resolveContentApiBase();
71
- if (typeof window !== "undefined") {
72
- return apiBase;
71
+ const query = { ...queryParams };
72
+ if (query.path === "/") {
73
+ delete query.path;
73
74
  }
74
- if (runtimeConfig?.flow?.siteUrl) {
75
- return joinUrl(runtimeConfig.flow.siteUrl, apiBase);
75
+ for (const key of Object.keys(query)) {
76
+ if (query[key] === void 0 || query[key] === null) {
77
+ delete query[key];
78
+ }
76
79
  }
77
- return joinUrl("http://localhost:3000", apiBase);
78
- }
79
- async function fetchContent(route, path) {
80
- const apiBase = await resolveContentApiBase();
81
- const query = path && path !== "/" ? { path } : void 0;
82
80
  const localFetch = getGlobalFetch();
83
- const nitroFetch = await getNitroFetch();
84
- if (typeof window === "undefined" && localFetch) {
85
- console.debug(`[Flow Content] Fetching content from ${joinUrl(apiBase, route)} using global fetch`);
86
- return await localFetch(joinUrl(apiBase, route), {
87
- headers: {
88
- accept: "application/json"
89
- },
90
- query
91
- });
92
- }
93
- if (typeof window === "undefined" && nitroFetch) {
94
- console.debug(`[Flow Content] Fetching content from ${joinUrl(apiBase, route)} using Nitro fetch`);
95
- return await nitroFetch(joinUrl(apiBase, route), {
96
- headers: {
97
- accept: "application/json"
98
- },
99
- query
100
- });
81
+ const relativeTarget = withQuery(joinUrl(apiBase, route), query);
82
+ if (typeof window === "undefined") {
83
+ try {
84
+ if (import.meta.env?.SSR || process.server) {
85
+ const { findContentEntries, buildContentTree, findContentTree, readContentEntries } = await import("../../modules/content/query.js");
86
+ let entries = await readContentEntries();
87
+ const isTreeRequest = route.endsWith("/tree") || query.tree === true || query.tree === "true" || query.tree === "1";
88
+ if (isTreeRequest) {
89
+ const tree = buildContentTree(entries);
90
+ return findContentTree(tree, query.path);
91
+ }
92
+ if (query.path) {
93
+ entries = findContentEntries(entries, query.path);
94
+ }
95
+ if (query.where) {
96
+ try {
97
+ const whereConditions = JSON.parse(query.where);
98
+ } catch {
99
+ }
100
+ }
101
+ return entries;
102
+ }
103
+ } catch (e) {
104
+ }
101
105
  }
102
- const absoluteBase = await resolveAbsoluteContentApiBase();
103
- const target = withQuery(joinUrl(absoluteBase, route), query);
104
- console.debug(`[Flow Content] Fetching content from ${target}`);
105
- const response = await fetch(target, {
106
+ const fetchFn = typeof window === "undefined" ? localFetch || fetch : fetch;
107
+ const targetToFetch = typeof window === "undefined" && localFetch ? relativeTarget : relativeTarget;
108
+ const response = await fetchFn(targetToFetch, {
106
109
  headers: {
107
110
  accept: "application/json"
108
111
  }
@@ -115,16 +118,79 @@ async function fetchContent(route, path) {
115
118
  }
116
119
  export function queryContent(path = "") {
117
120
  const normalizedPath = normalizeQueryPath(path);
118
- return {
121
+ const queryObj = {
122
+ path: normalizedPath,
123
+ where: [],
124
+ order: [],
125
+ skip: 0,
126
+ limit: 0,
127
+ select: []
128
+ };
129
+ const builder = {
130
+ where(field, operator, value) {
131
+ if (value === void 0) {
132
+ value = operator;
133
+ operator = "=";
134
+ }
135
+ queryObj.where.push({ field, operator, value });
136
+ return this;
137
+ },
138
+ order(field, direction = "ASC") {
139
+ queryObj.order.push({ field, direction });
140
+ return this;
141
+ },
142
+ skip(amount) {
143
+ queryObj.skip = amount;
144
+ return this;
145
+ },
146
+ limit(amount) {
147
+ queryObj.limit = amount;
148
+ return this;
149
+ },
150
+ select(...fields) {
151
+ queryObj.select.push(...fields);
152
+ return this;
153
+ },
119
154
  async find() {
120
- return await fetchContent("query", normalizedPath);
155
+ const q = { path: queryObj.path };
156
+ if (queryObj.where.length)
157
+ q.where = JSON.stringify(queryObj.where);
158
+ if (queryObj.order.length)
159
+ q.order = JSON.stringify(queryObj.order);
160
+ if (queryObj.skip > 0)
161
+ q.skip = String(queryObj.skip);
162
+ if (queryObj.limit > 0)
163
+ q.limit = String(queryObj.limit);
164
+ if (queryObj.select.length)
165
+ q.select = queryObj.select.join(",");
166
+ return await fetchContent("query", q);
121
167
  },
122
168
  async findOne() {
123
169
  const entries = await this.find();
124
170
  return entries[0] || null;
125
171
  },
172
+ async all() {
173
+ return await this.find();
174
+ },
175
+ async first() {
176
+ this.limit(1);
177
+ return await this.findOne();
178
+ },
179
+ async surround(targetPath) {
180
+ const entries = await this.find();
181
+ const index = entries.findIndex((e) => e.path === targetPath);
182
+ if (index === -1) {
183
+ return [null, null];
184
+ }
185
+ return [
186
+ index > 0 ? entries[index - 1] : null,
187
+ index < entries.length - 1 ? entries[index + 1] : null
188
+ ];
189
+ },
126
190
  async tree() {
127
- return await fetchContent("tree", normalizedPath);
191
+ const q = { path: queryObj.path };
192
+ return await fetchContent("tree", q);
128
193
  }
129
194
  };
195
+ return builder;
130
196
  }
@@ -71,6 +71,13 @@ export interface FlowModuleNitroConfig extends Record<string, unknown> {
71
71
  routeRules: Record<string, Record<string, unknown>>;
72
72
  runtimeConfig: Record<string, unknown>;
73
73
  virtual: Record<string, string | (() => string | Promise<string>)>;
74
+ imports?: {
75
+ imports: Array<{
76
+ name: string;
77
+ from: string;
78
+ as?: string;
79
+ }>;
80
+ };
74
81
  }
75
82
  export interface FlowModuleViteConfig extends Record<string, unknown> {
76
83
  plugins: unknown[];
@@ -7,7 +7,8 @@ function createNitroConfig() {
7
7
  handlers: [],
8
8
  routeRules: {},
9
9
  runtimeConfig: {},
10
- virtual: {}
10
+ virtual: {},
11
+ imports: { imports: [] }
11
12
  };
12
13
  }
13
14
  function createViteConfig() {
@@ -0,0 +1 @@
1
+ export default function (nitroApp: any): void;
@@ -0,0 +1,5 @@
1
+ export default function(nitroApp) {
2
+ if (!globalThis.$fetch && nitroApp) {
3
+ globalThis.$fetch = nitroApp.fetch;
4
+ }
5
+ }
@@ -53,6 +53,26 @@ function replacePath(pattern, url) {
53
53
  function combineName(name, dynamicName) {
54
54
  return `${name.replace(/\/*$/, "")}/${dynamicName.replace(/^\/*/, "")}`;
55
55
  }
56
+ function resolveUrlTarget(definitions, namePage, explicitDynamicName) {
57
+ if (explicitDynamicName || !namePage.includes("/")) {
58
+ return {
59
+ namePage,
60
+ dynamicName: explicitDynamicName
61
+ };
62
+ }
63
+ const match = definitions.filter((candidate) => candidate.name && namePage.startsWith(`${candidate.name}/`)).sort((left, right) => right.name.length - left.name.length)[0];
64
+ if (!match) {
65
+ return {
66
+ namePage,
67
+ dynamicName: explicitDynamicName
68
+ };
69
+ }
70
+ const dynamicName = namePage.slice(match.name.length + 1);
71
+ return {
72
+ namePage: match.name,
73
+ dynamicName: dynamicName || explicitDynamicName
74
+ };
75
+ }
56
76
  function createContext(definitions, flowConfig, definition, locale, localePage, pathname, params, dynamic) {
57
77
  return {
58
78
  dynamic,
@@ -111,7 +131,8 @@ export async function loadPageDefinitions(projectRoot, flowConfig) {
111
131
  return pages;
112
132
  }
113
133
  export async function getUrlFromDefinitions(definitions, flowConfig, namePage, localeCode = flowConfig.locale.locales[0], options = {}) {
114
- const definition = definitions.find((candidate) => candidate.name === namePage);
134
+ const target = resolveUrlTarget(definitions, namePage, options.dynamicName);
135
+ const definition = definitions.find((candidate) => candidate.name === target.namePage);
115
136
  if (!definition) {
116
137
  return void 0;
117
138
  }
@@ -126,9 +147,9 @@ export async function getUrlFromDefinitions(definitions, flowConfig, namePage, l
126
147
  if (options.params) {
127
148
  return replacePath(localizedRoute, options.params);
128
149
  }
129
- if (localePage.dynamic && options.dynamicName) {
150
+ if (localePage.dynamic && target.dynamicName) {
130
151
  const entries = await getDynamicEntries(definitions, flowConfig, definition, localeCode, localePage);
131
- const entry = entries.find((candidate) => candidate.name === options.dynamicName);
152
+ const entry = entries.find((candidate) => candidate.name === target.dynamicName);
132
153
  if (!entry) {
133
154
  return void 0;
134
155
  }