@fiyuu/core 0.1.0 → 0.2.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/package.json +5 -5
- package/src/artifacts.ts +328 -0
- package/src/config.ts +260 -0
- package/src/contracts.ts +247 -0
- package/src/{generator.js → generator.ts} +68 -41
- package/src/media.ts +143 -0
- package/src/reactive.ts +229 -0
- package/src/{responsive-wrapper.js → responsive-wrapper.ts} +118 -74
- package/src/responsive.ts +54 -0
- package/src/scanner.ts +289 -0
- package/src/template.ts +110 -0
- package/src/{virtual.js → virtual.tsx} +16 -7
- package/LICENSE +0 -674
- package/README.md +0 -194
- package/src/artifacts.d.ts +0 -20
- package/src/artifacts.js +0 -274
- package/src/client.js +0 -8
- package/src/config.d.ts +0 -179
- package/src/config.js +0 -58
- package/src/contracts.d.ts +0 -176
- package/src/contracts.js +0 -45
- package/src/generator.d.ts +0 -2
- package/src/index.js +0 -11
- package/src/media.d.ts +0 -44
- package/src/media.js +0 -87
- package/src/reactive.d.ts +0 -53
- package/src/reactive.js +0 -160
- package/src/responsive-wrapper.d.ts +0 -18
- package/src/responsive.d.ts +0 -15
- package/src/responsive.js +0 -48
- package/src/scanner.d.ts +0 -65
- package/src/scanner.js +0 -200
- package/src/state.js +0 -1
- package/src/template.d.ts +0 -48
- package/src/template.js +0 -98
- package/src/virtual.d.ts +0 -14
- /package/src/{client.d.ts → client.ts} +0 -0
- /package/src/{index.d.ts → index.ts} +0 -0
- /package/src/{state.d.ts → state.ts} +0 -0
package/src/config.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { pathToFileURL } from "node:url";
|
|
5
|
-
export async function loadFiyuuConfig(rootDirectory, mode = "dev") {
|
|
6
|
-
const env = await loadFiyuuEnvironment(rootDirectory, mode);
|
|
7
|
-
const configPath = path.join(rootDirectory, "fiyuu.config.ts");
|
|
8
|
-
if (!existsSync(configPath)) {
|
|
9
|
-
applyEnv(env);
|
|
10
|
-
return { config: {}, env };
|
|
11
|
-
}
|
|
12
|
-
const moduleUrl = pathToFileURL(configPath).href;
|
|
13
|
-
const loaded = await import(`${moduleUrl}?t=${Date.now()}`);
|
|
14
|
-
const config = (loaded.default ?? loaded.config ?? {}) ?? {};
|
|
15
|
-
applyEnv(env);
|
|
16
|
-
return { config, env };
|
|
17
|
-
}
|
|
18
|
-
async function loadFiyuuEnvironment(rootDirectory, mode) {
|
|
19
|
-
const directory = path.join(rootDirectory, ".fiyuu");
|
|
20
|
-
const files = [
|
|
21
|
-
path.join(directory, "env"),
|
|
22
|
-
path.join(directory, `${mode}.env`),
|
|
23
|
-
path.join(directory, "SECRET"),
|
|
24
|
-
];
|
|
25
|
-
const env = {};
|
|
26
|
-
for (const filePath of files) {
|
|
27
|
-
if (!existsSync(filePath)) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const name = path.basename(filePath);
|
|
31
|
-
if (name === "SECRET") {
|
|
32
|
-
env.FIYUU_SECRET = (await fs.readFile(filePath, "utf8")).trim();
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
const content = await fs.readFile(filePath, "utf8");
|
|
36
|
-
for (const line of content.split(/\r?\n/)) {
|
|
37
|
-
const trimmed = line.trim();
|
|
38
|
-
if (!trimmed || trimmed.startsWith("#")) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
const separator = trimmed.indexOf("=");
|
|
42
|
-
if (separator <= 0) {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
const key = trimmed.slice(0, separator).trim();
|
|
46
|
-
const value = trimmed.slice(separator + 1).trim();
|
|
47
|
-
env[key] = value;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return env;
|
|
51
|
-
}
|
|
52
|
-
function applyEnv(env) {
|
|
53
|
-
for (const [key, value] of Object.entries(env)) {
|
|
54
|
-
if (process.env[key] === undefined) {
|
|
55
|
-
process.env[key] = value;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
package/src/contracts.d.ts
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export type AnyZodSchema = z.ZodTypeAny;
|
|
3
|
-
export interface SchemaContract<TInput extends AnyZodSchema = AnyZodSchema, TOutput extends AnyZodSchema = AnyZodSchema> {
|
|
4
|
-
input: TInput;
|
|
5
|
-
output: TOutput;
|
|
6
|
-
description: string;
|
|
7
|
-
}
|
|
8
|
-
export interface ActionDefinition<TInput extends AnyZodSchema = AnyZodSchema, TOutput extends AnyZodSchema = AnyZodSchema> extends SchemaContract<TInput, TOutput> {
|
|
9
|
-
kind: "action";
|
|
10
|
-
}
|
|
11
|
-
export interface QueryDefinition<TInput extends AnyZodSchema = AnyZodSchema, TOutput extends AnyZodSchema = AnyZodSchema> extends SchemaContract<TInput, TOutput> {
|
|
12
|
-
kind: "query";
|
|
13
|
-
}
|
|
14
|
-
export interface QueryCacheConfig {
|
|
15
|
-
/** Cache TTL in seconds. Set to 0 to disable. */
|
|
16
|
-
ttl: number;
|
|
17
|
-
/** Cache key varies by these URL query string parameters. */
|
|
18
|
-
vary?: string[];
|
|
19
|
-
}
|
|
20
|
-
export interface MetaDefinition {
|
|
21
|
-
intent: string;
|
|
22
|
-
title?: string;
|
|
23
|
-
render?: RenderMode;
|
|
24
|
-
/**
|
|
25
|
-
* Mark this page as zero-JS.
|
|
26
|
-
* `fiyuu doctor` will warn if <script> tags are detected in page.tsx.
|
|
27
|
-
*/
|
|
28
|
-
noJs?: boolean;
|
|
29
|
-
/**
|
|
30
|
-
* Revalidate interval in seconds for `render: "ssg"` pages.
|
|
31
|
-
* Works like ISR: stale HTML is served immediately and refreshed in background.
|
|
32
|
-
*/
|
|
33
|
-
revalidate?: number;
|
|
34
|
-
seo?: {
|
|
35
|
-
title?: string;
|
|
36
|
-
description?: string;
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
export interface PageDefinition {
|
|
40
|
-
kind: "page";
|
|
41
|
-
intent: string;
|
|
42
|
-
}
|
|
43
|
-
export interface LayoutDefinition {
|
|
44
|
-
kind: "layout";
|
|
45
|
-
name?: string;
|
|
46
|
-
}
|
|
47
|
-
export type RenderMode = "ssr" | "csr" | "ssg";
|
|
48
|
-
export interface PageProps<TData = unknown> {
|
|
49
|
-
data: TData | null;
|
|
50
|
-
route: string;
|
|
51
|
-
intent: string;
|
|
52
|
-
render: RenderMode;
|
|
53
|
-
/** Dynamic route parameters extracted from the URL, e.g. { id: "42" } for /blog/[id] */
|
|
54
|
-
params: Record<string, string>;
|
|
55
|
-
}
|
|
56
|
-
export interface LayoutProps {
|
|
57
|
-
children: string;
|
|
58
|
-
route: string;
|
|
59
|
-
}
|
|
60
|
-
export declare function defineAction<TInput extends AnyZodSchema, TOutput extends AnyZodSchema>(config: SchemaContract<TInput, TOutput>): ActionDefinition<TInput, TOutput>;
|
|
61
|
-
export declare function defineQuery<TInput extends AnyZodSchema, TOutput extends AnyZodSchema>(config: SchemaContract<TInput, TOutput>): QueryDefinition<TInput, TOutput>;
|
|
62
|
-
export declare function definePage(config: {
|
|
63
|
-
intent: string;
|
|
64
|
-
}): PageDefinition;
|
|
65
|
-
export declare function defineLayout(config?: {
|
|
66
|
-
name?: string;
|
|
67
|
-
}): LayoutDefinition;
|
|
68
|
-
export declare function defineMeta(config: MetaDefinition): MetaDefinition;
|
|
69
|
-
/** TypeScript output type inferred from a QueryDefinition's output Zod schema. */
|
|
70
|
-
export type InferQueryOutput<T extends QueryDefinition> = z.infer<T["output"]>;
|
|
71
|
-
/** TypeScript input type inferred from a QueryDefinition's input Zod schema. */
|
|
72
|
-
export type InferQueryInput<T extends QueryDefinition> = z.infer<T["input"]>;
|
|
73
|
-
/** TypeScript input type inferred from an ActionDefinition's input Zod schema. */
|
|
74
|
-
export type InferActionInput<T extends ActionDefinition> = z.infer<T["input"]>;
|
|
75
|
-
/** TypeScript output type inferred from an ActionDefinition's output Zod schema. */
|
|
76
|
-
export type InferActionOutput<T extends ActionDefinition> = z.infer<T["output"]>;
|
|
77
|
-
/**
|
|
78
|
-
* Context passed to query `execute()` functions.
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* export async function execute({ request, params }: QueryContext) {
|
|
82
|
-
* const sessionId = getSessionIdFromRequest(request as any);
|
|
83
|
-
* ...
|
|
84
|
-
* }
|
|
85
|
-
*/
|
|
86
|
-
export interface QueryContext {
|
|
87
|
-
/** Incoming HTTP request (Node.js IncomingMessage). Cast to `any` if you need raw access. */
|
|
88
|
-
request: unknown;
|
|
89
|
-
/** Dynamic URL parameters, e.g. { id: "42" } for route /blog/[id] */
|
|
90
|
-
params: Record<string, string>;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Context passed to action `execute()` functions.
|
|
94
|
-
* Pass `typeof action` as the generic to get fully typed `input`.
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* export const action = defineAction({ input: z.object({ title: z.string() }), ... });
|
|
98
|
-
*
|
|
99
|
-
* export async function execute({ input, request }: ActionContext<typeof action>) {
|
|
100
|
-
* // input.title is typed as string — no manual type annotation needed
|
|
101
|
-
* }
|
|
102
|
-
*/
|
|
103
|
-
export interface ActionContext<T extends ActionDefinition = ActionDefinition> {
|
|
104
|
-
/** Action input, typed from the action's Zod input schema. */
|
|
105
|
-
input: z.infer<T["input"]>;
|
|
106
|
-
/** Incoming HTTP request (Node.js IncomingMessage). Cast to `any` if you need raw access. */
|
|
107
|
-
request: unknown;
|
|
108
|
-
/** Dynamic URL parameters, e.g. { id: "42" } for route /blog/[id] */
|
|
109
|
-
params: Record<string, string>;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Context object passed to every middleware handler.
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* export const middleware = defineMiddleware(async ({ url, request }, next) => {
|
|
116
|
-
* if (url.pathname === "/secret") { ... }
|
|
117
|
-
* await next();
|
|
118
|
-
* });
|
|
119
|
-
*/
|
|
120
|
-
export interface FiyuuMiddlewareContext {
|
|
121
|
-
/** Incoming HTTP request — Node.js `IncomingMessage`. */
|
|
122
|
-
request: unknown;
|
|
123
|
-
/** Parsed request URL. */
|
|
124
|
-
url: URL;
|
|
125
|
-
/** Mutable response headers that will be sent with the final response. */
|
|
126
|
-
responseHeaders: Record<string, string>;
|
|
127
|
-
/** Unique ID for this request (useful for logging). */
|
|
128
|
-
requestId: string;
|
|
129
|
-
/** Non-fatal warnings accumulated during request handling. */
|
|
130
|
-
warnings: string[];
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Return this from a middleware to short-circuit the request.
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* // Redirect to login
|
|
137
|
-
* return {
|
|
138
|
-
* headers: { Location: "/auth" },
|
|
139
|
-
* response: { status: 302, body: "" },
|
|
140
|
-
* };
|
|
141
|
-
*/
|
|
142
|
-
export interface FiyuuMiddlewareResult {
|
|
143
|
-
/** Extra response headers to send (e.g. `Location` for redirects). */
|
|
144
|
-
headers?: Record<string, string>;
|
|
145
|
-
response?: {
|
|
146
|
-
/** HTTP status code. */
|
|
147
|
-
status?: number;
|
|
148
|
-
/** JSON body — serialised automatically. */
|
|
149
|
-
json?: unknown;
|
|
150
|
-
/** Raw string body. */
|
|
151
|
-
body?: string;
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
/** Call this to continue to the next middleware or the route handler. */
|
|
155
|
-
export type FiyuuMiddlewareNext = () => Promise<void>;
|
|
156
|
-
/**
|
|
157
|
-
* A single middleware function.
|
|
158
|
-
* Return `FiyuuMiddlewareResult` to short-circuit, or call `next()` to continue.
|
|
159
|
-
*/
|
|
160
|
-
export type FiyuuMiddlewareHandler = (context: FiyuuMiddlewareContext, next: FiyuuMiddlewareNext) => Promise<FiyuuMiddlewareResult | void> | FiyuuMiddlewareResult | void;
|
|
161
|
-
/**
|
|
162
|
-
* Wraps your middleware function and gives it full type inference.
|
|
163
|
-
* Import from `@fiyuu/core` — no need to touch `@fiyuu/runtime`.
|
|
164
|
-
*
|
|
165
|
-
* @example app/middleware.ts
|
|
166
|
-
* import { defineMiddleware } from "@fiyuu/core";
|
|
167
|
-
*
|
|
168
|
-
* export const middleware = defineMiddleware(async ({ url, request }, next) => {
|
|
169
|
-
* if (url.pathname.startsWith("/dashboard")) {
|
|
170
|
-
* const user = await getSessionUser(request);
|
|
171
|
-
* if (!user) return { headers: { Location: "/auth" }, response: { status: 302, body: "" } };
|
|
172
|
-
* }
|
|
173
|
-
* await next();
|
|
174
|
-
* });
|
|
175
|
-
*/
|
|
176
|
-
export declare function defineMiddleware(handler: FiyuuMiddlewareHandler): FiyuuMiddlewareHandler;
|
package/src/contracts.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
export function defineAction(config) {
|
|
2
|
-
return {
|
|
3
|
-
kind: "action",
|
|
4
|
-
...config,
|
|
5
|
-
};
|
|
6
|
-
}
|
|
7
|
-
export function defineQuery(config) {
|
|
8
|
-
return {
|
|
9
|
-
kind: "query",
|
|
10
|
-
...config,
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
export function definePage(config) {
|
|
14
|
-
return {
|
|
15
|
-
kind: "page",
|
|
16
|
-
...config,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
export function defineLayout(config = {}) {
|
|
20
|
-
return {
|
|
21
|
-
kind: "layout",
|
|
22
|
-
...config,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
export function defineMeta(config) {
|
|
26
|
-
return config;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Wraps your middleware function and gives it full type inference.
|
|
30
|
-
* Import from `@fiyuu/core` — no need to touch `@fiyuu/runtime`.
|
|
31
|
-
*
|
|
32
|
-
* @example app/middleware.ts
|
|
33
|
-
* import { defineMiddleware } from "@fiyuu/core";
|
|
34
|
-
*
|
|
35
|
-
* export const middleware = defineMiddleware(async ({ url, request }, next) => {
|
|
36
|
-
* if (url.pathname.startsWith("/dashboard")) {
|
|
37
|
-
* const user = await getSessionUser(request);
|
|
38
|
-
* if (!user) return { headers: { Location: "/auth" }, response: { status: 302, body: "" } };
|
|
39
|
-
* }
|
|
40
|
-
* await next();
|
|
41
|
-
* });
|
|
42
|
-
*/
|
|
43
|
-
export function defineMiddleware(handler) {
|
|
44
|
-
return handler;
|
|
45
|
-
}
|
package/src/generator.d.ts
DELETED
package/src/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export * from "./contracts.js";
|
|
2
|
-
export * from "./artifacts.js";
|
|
3
|
-
export * from "./config.js";
|
|
4
|
-
export * from "./generator.js";
|
|
5
|
-
export * from "./media.js";
|
|
6
|
-
export * from "./responsive.js";
|
|
7
|
-
export * from "./responsive-wrapper.js";
|
|
8
|
-
export * from "./scanner.js";
|
|
9
|
-
export * from "./state.js";
|
|
10
|
-
export * from "./virtual.js";
|
|
11
|
-
export * from "./reactive.js";
|
package/src/media.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export interface OptimizedImageSource {
|
|
2
|
-
srcSet: string;
|
|
3
|
-
media?: string;
|
|
4
|
-
type?: string;
|
|
5
|
-
sizes?: string;
|
|
6
|
-
}
|
|
7
|
-
export interface OptimizedImageProps {
|
|
8
|
-
src: string;
|
|
9
|
-
alt: string;
|
|
10
|
-
width?: number;
|
|
11
|
-
height?: number;
|
|
12
|
-
sizes?: string;
|
|
13
|
-
srcSet?: string;
|
|
14
|
-
sources?: OptimizedImageSource[];
|
|
15
|
-
loading?: "lazy" | "eager";
|
|
16
|
-
decoding?: "async" | "sync" | "auto";
|
|
17
|
-
fetchPriority?: "high" | "low" | "auto";
|
|
18
|
-
class?: string;
|
|
19
|
-
style?: string;
|
|
20
|
-
id?: string;
|
|
21
|
-
}
|
|
22
|
-
export interface OptimizedVideoSource {
|
|
23
|
-
src: string;
|
|
24
|
-
type?: string;
|
|
25
|
-
media?: string;
|
|
26
|
-
}
|
|
27
|
-
export interface OptimizedVideoProps {
|
|
28
|
-
src?: string;
|
|
29
|
-
sources?: OptimizedVideoSource[];
|
|
30
|
-
poster?: string;
|
|
31
|
-
preload?: "none" | "metadata" | "auto";
|
|
32
|
-
controls?: boolean;
|
|
33
|
-
muted?: boolean;
|
|
34
|
-
loop?: boolean;
|
|
35
|
-
autoPlay?: boolean;
|
|
36
|
-
playsInline?: boolean;
|
|
37
|
-
width?: number;
|
|
38
|
-
height?: number;
|
|
39
|
-
class?: string;
|
|
40
|
-
style?: string;
|
|
41
|
-
id?: string;
|
|
42
|
-
}
|
|
43
|
-
export declare function optimizedImage(props: OptimizedImageProps): string;
|
|
44
|
-
export declare function optimizedVideo(props: OptimizedVideoProps): string;
|
package/src/media.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { escapeHtml } from "./template.js";
|
|
2
|
-
export function optimizedImage(props) {
|
|
3
|
-
const loading = props.loading ?? "lazy";
|
|
4
|
-
const decoding = props.decoding ?? "async";
|
|
5
|
-
const fetchPriority = props.fetchPriority ?? "auto";
|
|
6
|
-
const imageAttributes = [
|
|
7
|
-
createAttribute("src", props.src),
|
|
8
|
-
createAttribute("alt", props.alt),
|
|
9
|
-
createAttribute("loading", loading),
|
|
10
|
-
createAttribute("decoding", decoding),
|
|
11
|
-
createAttribute("fetchpriority", fetchPriority),
|
|
12
|
-
createAttribute("sizes", props.sizes),
|
|
13
|
-
createAttribute("srcset", props.srcSet),
|
|
14
|
-
createAttribute("width", props.width),
|
|
15
|
-
createAttribute("height", props.height),
|
|
16
|
-
createAttribute("class", props.class),
|
|
17
|
-
createAttribute("style", props.style),
|
|
18
|
-
createAttribute("id", props.id),
|
|
19
|
-
]
|
|
20
|
-
.filter(Boolean)
|
|
21
|
-
.join(" ");
|
|
22
|
-
const sourceTags = (props.sources ?? [])
|
|
23
|
-
.map((source) => {
|
|
24
|
-
const attributes = [
|
|
25
|
-
createAttribute("srcset", source.srcSet),
|
|
26
|
-
createAttribute("media", source.media),
|
|
27
|
-
createAttribute("type", source.type),
|
|
28
|
-
createAttribute("sizes", source.sizes),
|
|
29
|
-
]
|
|
30
|
-
.filter(Boolean)
|
|
31
|
-
.join(" ");
|
|
32
|
-
return `<source ${attributes}>`;
|
|
33
|
-
})
|
|
34
|
-
.join("");
|
|
35
|
-
if (sourceTags.length > 0) {
|
|
36
|
-
return `<picture>${sourceTags}<img ${imageAttributes}></picture>`;
|
|
37
|
-
}
|
|
38
|
-
return `<img ${imageAttributes}>`;
|
|
39
|
-
}
|
|
40
|
-
export function optimizedVideo(props) {
|
|
41
|
-
const preload = props.preload ?? "metadata";
|
|
42
|
-
const controls = props.controls ?? true;
|
|
43
|
-
const playsInline = props.playsInline ?? true;
|
|
44
|
-
const sources = props.sources && props.sources.length > 0
|
|
45
|
-
? props.sources
|
|
46
|
-
: props.src
|
|
47
|
-
? [{ src: props.src }]
|
|
48
|
-
: [];
|
|
49
|
-
if (sources.length === 0) {
|
|
50
|
-
throw new Error("optimizedVideo requires either `src` or `sources`.");
|
|
51
|
-
}
|
|
52
|
-
const videoAttributes = [
|
|
53
|
-
createAttribute("poster", props.poster),
|
|
54
|
-
createAttribute("preload", preload),
|
|
55
|
-
createAttribute("width", props.width),
|
|
56
|
-
createAttribute("height", props.height),
|
|
57
|
-
createAttribute("class", props.class),
|
|
58
|
-
createAttribute("style", props.style),
|
|
59
|
-
createAttribute("id", props.id),
|
|
60
|
-
controls ? "controls" : "",
|
|
61
|
-
props.muted ? "muted" : "",
|
|
62
|
-
props.loop ? "loop" : "",
|
|
63
|
-
props.autoPlay ? "autoplay" : "",
|
|
64
|
-
playsInline ? "playsinline" : "",
|
|
65
|
-
]
|
|
66
|
-
.filter(Boolean)
|
|
67
|
-
.join(" ");
|
|
68
|
-
const sourceTags = sources
|
|
69
|
-
.map((source) => {
|
|
70
|
-
const attributes = [
|
|
71
|
-
createAttribute("src", source.src),
|
|
72
|
-
createAttribute("type", source.type),
|
|
73
|
-
createAttribute("media", source.media),
|
|
74
|
-
]
|
|
75
|
-
.filter(Boolean)
|
|
76
|
-
.join(" ");
|
|
77
|
-
return `<source ${attributes}>`;
|
|
78
|
-
})
|
|
79
|
-
.join("");
|
|
80
|
-
return `<video ${videoAttributes}>${sourceTags}</video>`;
|
|
81
|
-
}
|
|
82
|
-
function createAttribute(name, value) {
|
|
83
|
-
if (value === undefined || value === null || value === "") {
|
|
84
|
-
return "";
|
|
85
|
-
}
|
|
86
|
-
return `${name}="${escapeHtml(value)}"`;
|
|
87
|
-
}
|
package/src/reactive.d.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { type RawHtml } from "./template.js";
|
|
2
|
-
export type Signal<T> = {
|
|
3
|
-
get(): T;
|
|
4
|
-
set(next: T): void;
|
|
5
|
-
subscribe(fn: () => void): () => void;
|
|
6
|
-
};
|
|
7
|
-
type RenderBlock = string | (() => string);
|
|
8
|
-
export declare function createSignal<T>(initial: T): Signal<T>;
|
|
9
|
-
export declare function when(condition: unknown, content: string | (() => string)): string;
|
|
10
|
-
export type ElseBranch = {
|
|
11
|
-
kind: "Else";
|
|
12
|
-
render: () => string;
|
|
13
|
-
};
|
|
14
|
-
export declare function Else(content: RenderBlock): ElseBranch;
|
|
15
|
-
export declare function If(input: {
|
|
16
|
-
condition: unknown;
|
|
17
|
-
then: RenderBlock;
|
|
18
|
-
else?: RenderBlock | ElseBranch;
|
|
19
|
-
}): string;
|
|
20
|
-
export declare function each<T>(items: T[] | readonly T[], renderItem: (item: T, index: number) => string): string;
|
|
21
|
-
export declare function For<T>(input: {
|
|
22
|
-
each: readonly T[] | T[];
|
|
23
|
-
render: (item: T, index: number) => string;
|
|
24
|
-
empty?: RenderBlock;
|
|
25
|
-
}): string;
|
|
26
|
-
export declare function onEvent(handler: (event: Event) => void): string;
|
|
27
|
-
export declare function getEventHandler(id: string): ((event: Event) => void) | undefined;
|
|
28
|
-
export declare function clearEventRegistry(): void;
|
|
29
|
-
export declare function mount(root: HTMLElement, render: () => string, signals?: Signal<unknown>[]): () => void;
|
|
30
|
-
export declare function createFiyuuStore<T>(initialValue: T): {
|
|
31
|
-
get: () => T;
|
|
32
|
-
set: (next: T) => void;
|
|
33
|
-
subscribe: (fn: () => void) => () => void;
|
|
34
|
-
};
|
|
35
|
-
export declare function createFlatStore<T>(initialValue: T): {
|
|
36
|
-
get: () => T;
|
|
37
|
-
set: (next: T) => void;
|
|
38
|
-
subscribe: (fn: () => void) => () => void;
|
|
39
|
-
};
|
|
40
|
-
export declare function scopedStyles(name: string, css: string): {
|
|
41
|
-
scopeClass: string;
|
|
42
|
-
style: RawHtml;
|
|
43
|
-
};
|
|
44
|
-
export declare function island(options: {
|
|
45
|
-
id: string;
|
|
46
|
-
placeholder: string;
|
|
47
|
-
bootCode: string;
|
|
48
|
-
trigger?: "click" | "hover" | "visible";
|
|
49
|
-
}): string;
|
|
50
|
-
export declare function debugTag(name: string, content: string): string;
|
|
51
|
-
export declare function humanDebugOverlay(): RawHtml;
|
|
52
|
-
export type FiyuuStore<T> = ReturnType<typeof createFiyuuStore<T>>;
|
|
53
|
-
export {};
|
package/src/reactive.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { html, raw } from "./template.js";
|
|
2
|
-
export function createSignal(initial) {
|
|
3
|
-
let value = initial;
|
|
4
|
-
const listeners = new Set();
|
|
5
|
-
return {
|
|
6
|
-
get() {
|
|
7
|
-
return value;
|
|
8
|
-
},
|
|
9
|
-
set(next) {
|
|
10
|
-
if (value === next)
|
|
11
|
-
return;
|
|
12
|
-
value = next;
|
|
13
|
-
for (const fn of listeners)
|
|
14
|
-
fn();
|
|
15
|
-
},
|
|
16
|
-
subscribe(fn) {
|
|
17
|
-
listeners.add(fn);
|
|
18
|
-
return () => listeners.delete(fn);
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
export function when(condition, content) {
|
|
23
|
-
if (!condition)
|
|
24
|
-
return "";
|
|
25
|
-
return typeof content === "function" ? content() : content;
|
|
26
|
-
}
|
|
27
|
-
export function Else(content) {
|
|
28
|
-
return {
|
|
29
|
-
kind: "Else",
|
|
30
|
-
render: () => renderBlock(content),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export function If(input) {
|
|
34
|
-
if (input.condition) {
|
|
35
|
-
return renderBlock(input.then);
|
|
36
|
-
}
|
|
37
|
-
if (!input.else) {
|
|
38
|
-
return "";
|
|
39
|
-
}
|
|
40
|
-
if (isElseBranch(input.else)) {
|
|
41
|
-
return input.else.render();
|
|
42
|
-
}
|
|
43
|
-
return renderBlock(input.else);
|
|
44
|
-
}
|
|
45
|
-
function isElseBranch(value) {
|
|
46
|
-
return Boolean(value &&
|
|
47
|
-
typeof value === "object" &&
|
|
48
|
-
"kind" in value &&
|
|
49
|
-
"render" in value &&
|
|
50
|
-
value.kind === "Else" &&
|
|
51
|
-
typeof value.render === "function");
|
|
52
|
-
}
|
|
53
|
-
function renderBlock(block) {
|
|
54
|
-
if (block == null) {
|
|
55
|
-
return "";
|
|
56
|
-
}
|
|
57
|
-
if (typeof block === "function") {
|
|
58
|
-
return block();
|
|
59
|
-
}
|
|
60
|
-
return block;
|
|
61
|
-
}
|
|
62
|
-
export function each(items, renderItem) {
|
|
63
|
-
if (!items || items.length === 0)
|
|
64
|
-
return "";
|
|
65
|
-
return items.map(renderItem).join("");
|
|
66
|
-
}
|
|
67
|
-
export function For(input) {
|
|
68
|
-
if (!input.each || input.each.length === 0) {
|
|
69
|
-
return renderBlock(input.empty);
|
|
70
|
-
}
|
|
71
|
-
return input.each.map((item, index) => input.render(item, index)).join("");
|
|
72
|
-
}
|
|
73
|
-
let _eventId = 0;
|
|
74
|
-
const _eventRegistry = new Map();
|
|
75
|
-
export function onEvent(handler) {
|
|
76
|
-
const id = `fiyuu_evt_${_eventId++}`;
|
|
77
|
-
_eventRegistry.set(id, handler);
|
|
78
|
-
return id;
|
|
79
|
-
}
|
|
80
|
-
export function getEventHandler(id) {
|
|
81
|
-
return _eventRegistry.get(id);
|
|
82
|
-
}
|
|
83
|
-
export function clearEventRegistry() {
|
|
84
|
-
_eventRegistry.clear();
|
|
85
|
-
_eventId = 0;
|
|
86
|
-
}
|
|
87
|
-
export function mount(root, render, signals = []) {
|
|
88
|
-
function update() {
|
|
89
|
-
root.innerHTML = render();
|
|
90
|
-
attachDeclarativeEvents(root);
|
|
91
|
-
}
|
|
92
|
-
update();
|
|
93
|
-
const unsubscribers = signals.map((s) => s.subscribe(update));
|
|
94
|
-
return () => {
|
|
95
|
-
unsubscribers.forEach((u) => u());
|
|
96
|
-
root.innerHTML = "";
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
function attachDeclarativeEvents(root) {
|
|
100
|
-
const elements = root.querySelectorAll("[data-fiyuu-on]");
|
|
101
|
-
for (const el of elements) {
|
|
102
|
-
const parts = el.getAttribute("data-fiyuu-on").split(":");
|
|
103
|
-
if (parts.length !== 2)
|
|
104
|
-
continue;
|
|
105
|
-
const [eventName, handlerId] = parts;
|
|
106
|
-
const handler = _eventRegistry.get(handlerId);
|
|
107
|
-
if (handler) {
|
|
108
|
-
el.addEventListener(eventName, handler);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
export function createFiyuuStore(initialValue) {
|
|
113
|
-
const signal = createSignal(initialValue);
|
|
114
|
-
return {
|
|
115
|
-
get: signal.get,
|
|
116
|
-
set: signal.set,
|
|
117
|
-
subscribe: signal.subscribe,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
export function createFlatStore(initialValue) {
|
|
121
|
-
return createFiyuuStore(initialValue);
|
|
122
|
-
}
|
|
123
|
-
let scopedStyleCounter = 0;
|
|
124
|
-
function toScopeSlug(value) {
|
|
125
|
-
return value
|
|
126
|
-
.toLowerCase()
|
|
127
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
128
|
-
.replace(/^-+|-+$/g, "")
|
|
129
|
-
.slice(0, 24) || "scope";
|
|
130
|
-
}
|
|
131
|
-
export function scopedStyles(name, css) {
|
|
132
|
-
scopedStyleCounter += 1;
|
|
133
|
-
const scopeClass = `fx-${toScopeSlug(name)}-${scopedStyleCounter}`;
|
|
134
|
-
const scopedCss = css.replaceAll(":scope", `.${scopeClass}`);
|
|
135
|
-
return {
|
|
136
|
-
scopeClass,
|
|
137
|
-
style: raw(`<style>${scopedCss}</style>`),
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
function escapeSingleQuotes(value) {
|
|
141
|
-
return value.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
|
|
142
|
-
}
|
|
143
|
-
export function island(options) {
|
|
144
|
-
const trigger = options.trigger ?? "click";
|
|
145
|
-
const safeId = escapeSingleQuotes(options.id);
|
|
146
|
-
const script = `(function(){\n var root = document.querySelector('[data-fiyuu-island="${safeId}"]');\n if (!root) return;\n\n var started = false;\n\n function start() {\n if (started) return;\n started = true;\n ${options.bootCode}\n }\n\n if ("${trigger}" === "click") {\n root.addEventListener("click", start, { once: true });\n return;\n }\n\n if ("${trigger}" === "hover") {\n root.addEventListener("mouseenter", start, { once: true });\n root.addEventListener("focusin", start, { once: true });\n return;\n }\n\n if (typeof window.IntersectionObserver !== "function") {\n start();\n return;\n }\n\n var observer = new IntersectionObserver(function(entries) {\n for (var i = 0; i < entries.length; i += 1) {\n if (!entries[i].isIntersecting) continue;\n observer.disconnect();\n start();\n break;\n }\n }, { rootMargin: "220px" });\n\n observer.observe(root);\n})();`;
|
|
147
|
-
return html `
|
|
148
|
-
<section data-fiyuu-island="${options.id}">
|
|
149
|
-
${raw(options.placeholder)}
|
|
150
|
-
</section>
|
|
151
|
-
<script>${raw(script)}</script>
|
|
152
|
-
`;
|
|
153
|
-
}
|
|
154
|
-
export function debugTag(name, content) {
|
|
155
|
-
return html `<section data-fiyuu-debug-tag="${name}">${raw(content)}</section>`;
|
|
156
|
-
}
|
|
157
|
-
export function humanDebugOverlay() {
|
|
158
|
-
const script = `(function() {\n var lastTag = "unknown";\n\n function findTag(target) {\n if (!target || !target.closest) return null;\n var tagged = target.closest("[data-fiyuu-debug-tag]");\n if (!tagged) return null;\n return tagged.getAttribute("data-fiyuu-debug-tag");\n }\n\n document.addEventListener("pointerdown", function(event) {\n var tag = findTag(event.target);\n if (tag) lastTag = tag;\n }, true);\n\n document.addEventListener("focusin", function(event) {\n var tag = findTag(event.target);\n if (tag) lastTag = tag;\n }, true);\n\n function inferHint(message) {\n var text = String(message || "").toLowerCase();\n if (text.includes("undefined") || text.includes("null")) {\n return "Eksik null/undefined kontrolu var. Verinin geldigi yeri kontrol et.";\n }\n if (text.includes("is not a function")) {\n return "Fonksiyon yerine farkli bir tip geciyor. Cagrilan methodu dogrula.";\n }\n if (text.includes("json")) {\n return "Beklenen veri formati ile gelen cevap uyusmuyor. Query/action ciktisini kontrol et.";\n }\n return "Kosul bloklarinda beklenmeyen bir durum olabilir. Son degisen bolumu kontrol et.";\n }\n\n function getPanel() {\n var panel = document.getElementById("fiyuu-human-debug");\n if (panel) return panel;\n\n panel = document.createElement("aside");\n panel.id = "fiyuu-human-debug";\n panel.style.position = "fixed";\n panel.style.right = "16px";\n panel.style.bottom = "16px";\n panel.style.maxWidth = "min(92vw, 480px)";\n panel.style.padding = "12px 14px";\n panel.style.borderRadius = "10px";\n panel.style.border = "1px solid #fecaca";\n panel.style.background = "#fff1f2";\n panel.style.color = "#7f1d1d";\n panel.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, monospace";\n panel.style.fontSize = "12px";\n panel.style.lineHeight = "1.45";\n panel.style.whiteSpace = "pre-line";\n panel.style.zIndex = "99999";\n panel.style.boxShadow = "0 10px 28px rgba(0,0,0,0.12)";\n document.body.appendChild(panel);\n return panel;\n }\n\n if (!window.fiyuu || typeof window.fiyuu.onError !== "function") {\n return;\n }\n\n window.fiyuu.onError(function(detail) {\n var source = detail && detail.source ? detail.source : "unknown file";\n var line = detail && detail.line ? ":" + detail.line : "";\n var message = detail && detail.message ? detail.message : "Unknown runtime error";\n var hint = inferHint(message);\n var tag = lastTag;\n var activeElement = document.activeElement;\n\n if (tag === "unknown" && activeElement && activeElement.tagName) {\n tag = activeElement.tagName.toLowerCase();\n }\n\n var panel = getPanel();\n panel.textContent = [\n "Insancil Hata Analizi",\n "Mesaj: " + message,\n "Dosya: " + source + line,\n "Etiket/Bolum: " + tag,\n "Ihtimal: " + hint,\n ].join("\\n");\n });\n})();`;
|
|
159
|
-
return raw(`<script>${script}</script>`);
|
|
160
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export type BreakpointPreset = "mobile-first" | "desktop-first" | "full-width" | "article" | "card" | "dashboard" | "narrow";
|
|
2
|
-
export type PreviewDevice = "phone" | "tablet" | "watch" | "desktop";
|
|
3
|
-
export type PreviewMode = "mobile" | "desktop" | "both";
|
|
4
|
-
export interface ResponsiveWrapperOptions {
|
|
5
|
-
content: string;
|
|
6
|
-
class?: string;
|
|
7
|
-
style?: string;
|
|
8
|
-
maxWidth?: number;
|
|
9
|
-
padding?: "none" | "sm" | "md" | "lg" | "xl" | string;
|
|
10
|
-
previewEnabled?: boolean;
|
|
11
|
-
previewLabel?: string;
|
|
12
|
-
id?: string;
|
|
13
|
-
preset?: BreakpointPreset;
|
|
14
|
-
/** Default preview mode */
|
|
15
|
-
defaultPreviewMode?: PreviewMode;
|
|
16
|
-
}
|
|
17
|
-
export declare function responsiveWrapper(options: ResponsiveWrapperOptions): string;
|
|
18
|
-
export declare function responsiveWrapperScript(): string;
|
package/src/responsive.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export declare const BREAKPOINTS: {
|
|
2
|
-
readonly xs: 480;
|
|
3
|
-
readonly sm: 640;
|
|
4
|
-
readonly md: 768;
|
|
5
|
-
readonly lg: 1024;
|
|
6
|
-
readonly xl: 1280;
|
|
7
|
-
readonly "2xl": 1536;
|
|
8
|
-
};
|
|
9
|
-
export type BreakpointName = keyof typeof BREAKPOINTS;
|
|
10
|
-
export declare function mediaUp(name: BreakpointName, css: string): string;
|
|
11
|
-
export declare function mediaDown(name: BreakpointName, css: string): string;
|
|
12
|
-
export declare function mediaBetween(min: BreakpointName, max: BreakpointName, css: string): string;
|
|
13
|
-
export declare function fluid(minSizePx: number, maxSizePx: number, minViewportPx?: number, maxViewportPx?: number): string;
|
|
14
|
-
export declare function responsiveSizes(config: Partial<Record<BreakpointName, string>>, fallback?: string): string;
|
|
15
|
-
export declare function responsiveStyle(selector: string, baseCss: string, overrides: Partial<Record<BreakpointName, string>>): string;
|