@nuraly/lumenjs 0.1.3 → 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/README.md +62 -282
- package/dist/auth/config.d.ts +23 -0
- package/dist/auth/config.js +115 -0
- package/dist/auth/guard.d.ts +12 -0
- package/dist/auth/guard.js +28 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/middleware.d.ts +23 -0
- package/dist/auth/middleware.js +89 -0
- package/dist/auth/native-auth.d.ts +82 -0
- package/dist/auth/native-auth.js +340 -0
- package/dist/auth/oidc-client.d.ts +17 -0
- package/dist/auth/oidc-client.js +123 -0
- package/dist/auth/providers/google.d.ts +23 -0
- package/dist/auth/providers/google.js +25 -0
- package/dist/auth/providers/index.d.ts +2 -0
- package/dist/auth/providers/index.js +1 -0
- package/dist/auth/routes/login.d.ts +8 -0
- package/dist/auth/routes/login.js +121 -0
- package/dist/auth/routes/logout.d.ts +4 -0
- package/dist/auth/routes/logout.js +79 -0
- package/dist/auth/routes/oidc-callback.d.ts +3 -0
- package/dist/auth/routes/oidc-callback.js +70 -0
- package/dist/auth/routes/password.d.ts +5 -0
- package/dist/auth/routes/password.js +149 -0
- package/dist/auth/routes/signup.d.ts +3 -0
- package/dist/auth/routes/signup.js +81 -0
- package/dist/auth/routes/token.d.ts +4 -0
- package/dist/auth/routes/token.js +70 -0
- package/dist/auth/routes/totp.d.ts +22 -0
- package/dist/auth/routes/totp.js +232 -0
- package/dist/auth/routes/utils.d.ts +7 -0
- package/dist/auth/routes/utils.js +35 -0
- package/dist/auth/routes/verify.d.ts +3 -0
- package/dist/auth/routes/verify.js +26 -0
- package/dist/auth/routes.d.ts +8 -0
- package/dist/auth/routes.js +124 -0
- package/dist/auth/session.d.ts +8 -0
- package/dist/auth/session.js +54 -0
- package/dist/auth/token.d.ts +33 -0
- package/dist/auth/token.js +90 -0
- package/dist/auth/types.d.ts +156 -0
- package/dist/auth/types.js +2 -0
- package/dist/build/build-client.d.ts +15 -0
- package/dist/build/build-client.js +45 -0
- package/dist/build/build-prerender.d.ts +11 -0
- package/dist/build/build-prerender.js +159 -0
- package/dist/build/build-server.d.ts +18 -0
- package/dist/build/build-server.js +107 -0
- package/dist/build/build.js +60 -123
- package/dist/build/scan.d.ts +18 -0
- package/dist/build/scan.js +77 -6
- package/dist/build/serve-api.js +8 -2
- package/dist/build/serve-loaders.d.ts +4 -4
- package/dist/build/serve-loaders.js +26 -18
- package/dist/build/serve-ssr.js +38 -11
- package/dist/build/serve-static.js +3 -3
- package/dist/build/serve.js +341 -18
- package/dist/cli.js +37 -6
- package/dist/communication/encryption.d.ts +35 -0
- package/dist/communication/encryption.js +90 -0
- package/dist/communication/handlers/context.d.ts +27 -0
- package/dist/communication/handlers/context.js +1 -0
- package/dist/communication/handlers/conversation.d.ts +24 -0
- package/dist/communication/handlers/conversation.js +113 -0
- package/dist/communication/handlers/file-upload.d.ts +17 -0
- package/dist/communication/handlers/file-upload.js +62 -0
- package/dist/communication/handlers/messaging.d.ts +30 -0
- package/dist/communication/handlers/messaging.js +237 -0
- package/dist/communication/handlers/presence.d.ts +15 -0
- package/dist/communication/handlers/presence.js +76 -0
- package/dist/communication/handlers.d.ts +5 -0
- package/dist/communication/handlers.js +5 -0
- package/dist/communication/index.d.ts +9 -0
- package/dist/communication/index.js +7 -0
- package/dist/communication/link-preview.d.ts +18 -0
- package/dist/communication/link-preview.js +115 -0
- package/dist/communication/schema.d.ts +10 -0
- package/dist/communication/schema.js +101 -0
- package/dist/communication/server.d.ts +86 -0
- package/dist/communication/server.js +212 -0
- package/dist/communication/signaling.d.ts +43 -0
- package/dist/communication/signaling.js +271 -0
- package/dist/communication/store.d.ts +71 -0
- package/dist/communication/store.js +289 -0
- package/dist/communication/types.d.ts +454 -0
- package/dist/communication/types.js +1 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +55 -0
- package/dist/db/auto-migrate.d.ts +3 -0
- package/dist/db/auto-migrate.js +100 -0
- package/dist/db/client.d.ts +3 -0
- package/dist/db/client.js +18 -0
- package/dist/db/index.d.ts +17 -13
- package/dist/db/index.js +205 -26
- package/dist/db/seed.d.ts +12 -0
- package/dist/db/seed.js +88 -0
- package/dist/db/table.d.ts +10 -0
- package/dist/db/table.js +12 -0
- package/dist/dev-server/config.d.ts +11 -0
- package/dist/dev-server/config.js +40 -20
- package/dist/dev-server/index-html.d.ts +4 -0
- package/dist/dev-server/index-html.js +21 -6
- package/dist/dev-server/nuralyui-aliases.d.ts +0 -4
- package/dist/dev-server/nuralyui-aliases.js +115 -94
- package/dist/dev-server/plugins/vite-plugin-api-routes.js +29 -5
- package/dist/dev-server/plugins/vite-plugin-auth.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-auth.js +223 -0
- package/dist/dev-server/plugins/vite-plugin-auto-define.d.ts +16 -0
- package/dist/dev-server/plugins/vite-plugin-auto-define.js +111 -0
- package/dist/dev-server/plugins/vite-plugin-communication.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-communication.js +205 -0
- package/dist/dev-server/plugins/vite-plugin-editor-api.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-editor-api.js +318 -0
- package/dist/dev-server/plugins/vite-plugin-i18n.js +69 -2
- package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +78 -34
- package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +44 -2
- package/dist/dev-server/plugins/vite-plugin-llms.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-llms.js +92 -0
- package/dist/dev-server/plugins/vite-plugin-loaders.js +146 -13
- package/dist/dev-server/plugins/vite-plugin-routes.js +16 -5
- package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
- package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
- package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
- package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
- package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +140 -3
- package/dist/dev-server/server.js +242 -70
- package/dist/dev-server/ssr-render.d.ts +2 -1
- package/dist/dev-server/ssr-render.js +117 -50
- package/dist/editor/ai/backend.d.ts +20 -0
- package/dist/editor/ai/backend.js +113 -0
- package/dist/editor/ai/claude-code-client.d.ts +20 -0
- package/dist/editor/ai/claude-code-client.js +145 -0
- package/dist/editor/ai/deepseek-client.d.ts +7 -0
- package/dist/editor/ai/deepseek-client.js +113 -0
- package/dist/editor/ai/opencode-client.d.ts +14 -0
- package/dist/editor/ai/opencode-client.js +99 -0
- package/dist/editor/ai/snapshot-store.d.ts +22 -0
- package/dist/editor/ai/snapshot-store.js +35 -0
- package/dist/editor/ai/types.d.ts +30 -0
- package/dist/editor/ai/types.js +136 -0
- package/dist/editor/ai-chat-panel.d.ts +13 -0
- package/dist/editor/ai-chat-panel.js +613 -0
- package/dist/editor/ai-markdown.d.ts +10 -0
- package/dist/editor/ai-markdown.js +70 -0
- package/dist/editor/ai-project-panel.d.ts +11 -0
- package/dist/editor/ai-project-panel.js +332 -0
- package/dist/editor/ast-modification.d.ts +11 -0
- package/dist/editor/ast-modification.js +1 -0
- package/dist/editor/ast-service.d.ts +30 -0
- package/dist/editor/ast-service.js +180 -0
- package/dist/editor/css-rules.d.ts +54 -0
- package/dist/editor/css-rules.js +423 -0
- package/dist/editor/editor-api-client.d.ts +51 -0
- package/dist/editor/editor-api-client.js +162 -0
- package/dist/editor/editor-bridge.d.ts +1 -0
- package/dist/editor/editor-bridge.js +18 -8
- package/dist/editor/editor-toolbar.d.ts +14 -0
- package/dist/editor/editor-toolbar.js +115 -0
- package/dist/editor/file-editor.d.ts +9 -0
- package/dist/editor/file-editor.js +236 -0
- package/dist/editor/file-service.d.ts +16 -0
- package/dist/editor/file-service.js +52 -0
- package/dist/editor/i18n-key-gen.d.ts +1 -0
- package/dist/editor/i18n-key-gen.js +7 -0
- package/dist/editor/inline-text-edit.d.ts +5 -0
- package/dist/editor/inline-text-edit.js +173 -92
- package/dist/editor/overlay-events.d.ts +5 -0
- package/dist/editor/overlay-events.js +364 -0
- package/dist/editor/overlay-hmr.d.ts +2 -0
- package/dist/editor/overlay-hmr.js +76 -0
- package/dist/editor/overlay-selection.d.ts +29 -0
- package/dist/editor/overlay-selection.js +148 -0
- package/dist/editor/overlay-utils.d.ts +12 -0
- package/dist/editor/overlay-utils.js +59 -0
- package/dist/editor/properties-panel-persist.d.ts +14 -0
- package/dist/editor/properties-panel-persist.js +70 -0
- package/dist/editor/properties-panel-rows.d.ts +10 -0
- package/dist/editor/properties-panel-rows.js +349 -0
- package/dist/editor/properties-panel-styles.d.ts +4 -0
- package/dist/editor/properties-panel-styles.js +174 -0
- package/dist/editor/properties-panel.d.ts +4 -0
- package/dist/editor/properties-panel.js +148 -0
- package/dist/editor/property-registry.d.ts +16 -0
- package/dist/editor/property-registry.js +303 -0
- package/dist/editor/standalone-file-panel.d.ts +0 -0
- package/dist/editor/standalone-file-panel.js +1 -0
- package/dist/editor/standalone-overlay-dom.d.ts +0 -0
- package/dist/editor/standalone-overlay-dom.js +1 -0
- package/dist/editor/standalone-overlay-styles.d.ts +0 -0
- package/dist/editor/standalone-overlay-styles.js +1 -0
- package/dist/editor/standalone-overlay.d.ts +1 -0
- package/dist/editor/standalone-overlay.js +76 -0
- package/dist/editor/syntax-highlighter.d.ts +4 -0
- package/dist/editor/syntax-highlighter.js +81 -0
- package/dist/editor/text-toolbar.d.ts +11 -0
- package/dist/editor/text-toolbar.js +327 -0
- package/dist/editor/toolbar-styles.d.ts +4 -0
- package/dist/editor/toolbar-styles.js +198 -0
- package/dist/email/index.d.ts +32 -0
- package/dist/email/index.js +154 -0
- package/dist/email/providers/resend.d.ts +2 -0
- package/dist/email/providers/resend.js +24 -0
- package/dist/email/providers/sendgrid.d.ts +2 -0
- package/dist/email/providers/sendgrid.js +31 -0
- package/dist/email/providers/smtp.d.ts +13 -0
- package/dist/email/providers/smtp.js +125 -0
- package/dist/email/template-engine.d.ts +18 -0
- package/dist/email/template-engine.js +116 -0
- package/dist/email/templates/base.d.ts +9 -0
- package/dist/email/templates/base.js +65 -0
- package/dist/email/templates/password-reset.d.ts +5 -0
- package/dist/email/templates/password-reset.js +15 -0
- package/dist/email/templates/verify-email.d.ts +5 -0
- package/dist/email/templates/verify-email.js +15 -0
- package/dist/email/templates/welcome.d.ts +5 -0
- package/dist/email/templates/welcome.js +13 -0
- package/dist/email/types.d.ts +49 -0
- package/dist/email/types.js +1 -0
- package/dist/llms/generate.d.ts +46 -0
- package/dist/llms/generate.js +185 -0
- package/dist/permissions/guard.d.ts +28 -0
- package/dist/permissions/guard.js +30 -0
- package/dist/permissions/index.d.ts +6 -0
- package/dist/permissions/index.js +3 -0
- package/dist/permissions/service.d.ts +80 -0
- package/dist/permissions/service.js +210 -0
- package/dist/permissions/tables.d.ts +5 -0
- package/dist/permissions/tables.js +68 -0
- package/dist/permissions/types.d.ts +33 -0
- package/dist/permissions/types.js +1 -0
- package/dist/runtime/app-shell.d.ts +1 -1
- package/dist/runtime/app-shell.js +164 -0
- package/dist/runtime/auth.d.ts +10 -0
- package/dist/runtime/auth.js +30 -0
- package/dist/runtime/communication.d.ts +137 -0
- package/dist/runtime/communication.js +228 -0
- package/dist/runtime/error-boundary.d.ts +23 -0
- package/dist/runtime/error-boundary.js +120 -0
- package/dist/runtime/i18n.d.ts +6 -1
- package/dist/runtime/i18n.js +42 -21
- package/dist/runtime/island.d.ts +16 -0
- package/dist/runtime/island.js +80 -0
- package/dist/runtime/router-data.d.ts +3 -0
- package/dist/runtime/router-data.js +102 -17
- package/dist/runtime/router-hydration.js +34 -2
- package/dist/runtime/router.d.ts +19 -2
- package/dist/runtime/router.js +237 -43
- package/dist/runtime/socket-client.d.ts +2 -0
- package/dist/runtime/socket-client.js +30 -0
- package/dist/runtime/webrtc.d.ts +91 -0
- package/dist/runtime/webrtc.js +428 -0
- package/dist/shared/dom-shims.js +4 -2
- package/dist/shared/graceful-shutdown.d.ts +8 -0
- package/dist/shared/graceful-shutdown.js +36 -0
- package/dist/shared/health.d.ts +8 -0
- package/dist/shared/health.js +25 -0
- package/dist/shared/llms-txt.d.ts +31 -0
- package/dist/shared/llms-txt.js +85 -0
- package/dist/shared/logger.d.ts +32 -0
- package/dist/shared/logger.js +93 -0
- package/dist/shared/meta.d.ts +27 -0
- package/dist/shared/meta.js +71 -0
- package/dist/shared/middleware-runner.d.ts +9 -0
- package/dist/shared/middleware-runner.js +29 -0
- package/dist/shared/rate-limit.d.ts +18 -0
- package/dist/shared/rate-limit.js +71 -0
- package/dist/shared/request-id.d.ts +5 -0
- package/dist/shared/request-id.js +18 -0
- package/dist/shared/route-matching.js +16 -1
- package/dist/shared/security-headers.d.ts +18 -0
- package/dist/shared/security-headers.js +38 -0
- package/dist/shared/socket-io-setup.d.ts +11 -0
- package/dist/shared/socket-io-setup.js +51 -0
- package/dist/shared/types.d.ts +15 -0
- package/dist/shared/utils.d.ts +33 -7
- package/dist/shared/utils.js +164 -27
- package/dist/storage/adapters/local.d.ts +44 -0
- package/dist/storage/adapters/local.js +85 -0
- package/dist/storage/adapters/s3.d.ts +32 -0
- package/dist/storage/adapters/s3.js +119 -0
- package/dist/storage/adapters/types.d.ts +53 -0
- package/dist/storage/adapters/types.js +1 -0
- package/dist/storage/index.d.ts +76 -0
- package/dist/storage/index.js +83 -0
- package/package.json +45 -7
- package/templates/blog/api/posts.ts +4 -18
- package/templates/blog/data/migrations/001_init.sql +6 -5
- package/templates/blog/lumenjs.config.ts +3 -0
- package/templates/blog/package.json +14 -0
- package/templates/blog/pages/_layout.ts +25 -0
- package/templates/blog/pages/index.ts +48 -22
- package/templates/blog/pages/posts/[slug].ts +45 -20
- package/templates/blog/pages/tag/[tag].ts +44 -0
- package/templates/dashboard/api/stats.ts +8 -5
- package/templates/dashboard/lumenjs.config.ts +3 -0
- package/templates/dashboard/package.json +14 -0
- package/templates/dashboard/pages/_layout.ts +25 -0
- package/templates/dashboard/pages/index.ts +54 -23
- package/templates/dashboard/pages/settings/index.ts +29 -0
- package/templates/default/lumenjs.config.ts +3 -0
- package/templates/default/package.json +14 -0
- package/templates/default/pages/index.ts +24 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-create permission tables in the LumenJS SQLite database.
|
|
3
|
+
* Follows the same pattern as ensureUsersTable() in auth/native-auth.ts.
|
|
4
|
+
*/
|
|
5
|
+
export async function ensurePermissionTables(db) {
|
|
6
|
+
if (db.isPg)
|
|
7
|
+
return;
|
|
8
|
+
await db.exec(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS _nk_resource_permissions (
|
|
10
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
|
+
resource_type TEXT NOT NULL,
|
|
12
|
+
resource_id TEXT NOT NULL,
|
|
13
|
+
grantee_type TEXT NOT NULL CHECK (grantee_type IN ('user', 'role', 'public', 'anonymous')),
|
|
14
|
+
grantee_id TEXT NOT NULL DEFAULT '',
|
|
15
|
+
permission TEXT NOT NULL,
|
|
16
|
+
granted_by TEXT,
|
|
17
|
+
expires_at TEXT,
|
|
18
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
19
|
+
UNIQUE(resource_type, resource_id, grantee_type, grantee_id, permission)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_nk_rp_resource
|
|
23
|
+
ON _nk_resource_permissions(resource_type, resource_id);
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_nk_rp_grantee
|
|
25
|
+
ON _nk_resource_permissions(grantee_type, grantee_id);
|
|
26
|
+
|
|
27
|
+
CREATE TABLE IF NOT EXISTS _nk_roles (
|
|
28
|
+
id TEXT PRIMARY KEY,
|
|
29
|
+
name TEXT NOT NULL UNIQUE,
|
|
30
|
+
permissions TEXT NOT NULL DEFAULT '[]',
|
|
31
|
+
hierarchy INTEGER NOT NULL DEFAULT 0,
|
|
32
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS _nk_user_roles (
|
|
36
|
+
user_id TEXT NOT NULL,
|
|
37
|
+
role_id TEXT NOT NULL,
|
|
38
|
+
resource_type TEXT NOT NULL DEFAULT '',
|
|
39
|
+
resource_id TEXT NOT NULL DEFAULT '',
|
|
40
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
41
|
+
PRIMARY KEY (user_id, role_id, resource_type, resource_id)
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_nk_ur_user
|
|
45
|
+
ON _nk_user_roles(user_id);
|
|
46
|
+
|
|
47
|
+
CREATE TABLE IF NOT EXISTS _nk_permission_audit_log (
|
|
48
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
action TEXT NOT NULL,
|
|
50
|
+
resource_type TEXT,
|
|
51
|
+
resource_id TEXT,
|
|
52
|
+
grantee_type TEXT,
|
|
53
|
+
grantee_id TEXT,
|
|
54
|
+
permission TEXT,
|
|
55
|
+
role_id TEXT,
|
|
56
|
+
actor_id TEXT NOT NULL,
|
|
57
|
+
details TEXT,
|
|
58
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_nk_pal_resource
|
|
62
|
+
ON _nk_permission_audit_log(resource_type, resource_id);
|
|
63
|
+
CREATE INDEX IF NOT EXISTS idx_nk_pal_actor
|
|
64
|
+
ON _nk_permission_audit_log(actor_id);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_nk_pal_created
|
|
66
|
+
ON _nk_permission_audit_log(created_at);
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface ResourcePermission {
|
|
2
|
+
id: number;
|
|
3
|
+
resource_type: string;
|
|
4
|
+
resource_id: string;
|
|
5
|
+
grantee_type: 'user' | 'role' | 'public' | 'anonymous';
|
|
6
|
+
grantee_id: string | null;
|
|
7
|
+
permission: string;
|
|
8
|
+
granted_by: string | null;
|
|
9
|
+
expires_at: string | null;
|
|
10
|
+
created_at: string;
|
|
11
|
+
}
|
|
12
|
+
export interface Role {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
permissions: string[];
|
|
16
|
+
hierarchy: number;
|
|
17
|
+
created_at: string;
|
|
18
|
+
}
|
|
19
|
+
export interface UserRole {
|
|
20
|
+
user_id: string;
|
|
21
|
+
role_id: string;
|
|
22
|
+
resource_type: string | null;
|
|
23
|
+
resource_id: string | null;
|
|
24
|
+
created_at: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PermissionsConfig {
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
defaultOwnerGrants?: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface ResolvedPermissionsConfig {
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
defaultOwnerGrants: string[];
|
|
33
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import './island.js';
|
|
@@ -1,5 +1,147 @@
|
|
|
1
1
|
import { routes } from 'virtual:lumenjs-routes';
|
|
2
2
|
import { NkRouter } from './router.js';
|
|
3
|
+
import './island.js';
|
|
4
|
+
function getDefaultStrategy() {
|
|
5
|
+
const el = document.getElementById('__nk_prefetch__');
|
|
6
|
+
if (el) {
|
|
7
|
+
const val = el.textContent?.trim();
|
|
8
|
+
if (val === 'hover' || val === 'viewport' || val === 'none')
|
|
9
|
+
return val;
|
|
10
|
+
}
|
|
11
|
+
return 'hover';
|
|
12
|
+
}
|
|
13
|
+
function getAnchorHref(anchor) {
|
|
14
|
+
const href = anchor.getAttribute('href');
|
|
15
|
+
if (!href || href.startsWith('http') || href.startsWith('#') || anchor.hasAttribute('target'))
|
|
16
|
+
return null;
|
|
17
|
+
return href;
|
|
18
|
+
}
|
|
19
|
+
function getLinkStrategy(anchor, defaultStrategy) {
|
|
20
|
+
const override = anchor.dataset.prefetch;
|
|
21
|
+
if (override === 'hover' || override === 'viewport' || override === 'none')
|
|
22
|
+
return override;
|
|
23
|
+
return defaultStrategy;
|
|
24
|
+
}
|
|
25
|
+
function setupPrefetchObserver(defaultStrategy) {
|
|
26
|
+
const prefetched = new Set();
|
|
27
|
+
const doPrefetch = (href) => {
|
|
28
|
+
if (prefetched.has(href))
|
|
29
|
+
return;
|
|
30
|
+
prefetched.add(href);
|
|
31
|
+
const pf = window.__nk_prefetch;
|
|
32
|
+
if (pf)
|
|
33
|
+
pf(href);
|
|
34
|
+
};
|
|
35
|
+
// Hover strategy: event delegation on document
|
|
36
|
+
if (defaultStrategy === 'hover' || defaultStrategy === 'viewport') {
|
|
37
|
+
const onPointerEnter = (e) => {
|
|
38
|
+
const path = e.composedPath();
|
|
39
|
+
const anchor = path.find(el => el instanceof HTMLElement && el.tagName === 'A');
|
|
40
|
+
if (!anchor)
|
|
41
|
+
return;
|
|
42
|
+
const href = getAnchorHref(anchor);
|
|
43
|
+
if (!href)
|
|
44
|
+
return;
|
|
45
|
+
const strategy = getLinkStrategy(anchor, defaultStrategy);
|
|
46
|
+
if (strategy === 'none')
|
|
47
|
+
return;
|
|
48
|
+
if (strategy === 'hover')
|
|
49
|
+
doPrefetch(href);
|
|
50
|
+
};
|
|
51
|
+
document.addEventListener('pointerenter', onPointerEnter, true);
|
|
52
|
+
document.addEventListener('focusin', (e) => {
|
|
53
|
+
const target = e.target;
|
|
54
|
+
if (target instanceof HTMLAnchorElement) {
|
|
55
|
+
const href = getAnchorHref(target);
|
|
56
|
+
if (!href)
|
|
57
|
+
return;
|
|
58
|
+
const strategy = getLinkStrategy(target, defaultStrategy);
|
|
59
|
+
if (strategy === 'none')
|
|
60
|
+
return;
|
|
61
|
+
if (strategy === 'hover')
|
|
62
|
+
doPrefetch(href);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// Viewport strategy: IntersectionObserver for links
|
|
67
|
+
if (defaultStrategy === 'viewport') {
|
|
68
|
+
const observedLinks = new WeakSet();
|
|
69
|
+
const io = new IntersectionObserver((entries) => {
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
if (!entry.isIntersecting)
|
|
72
|
+
continue;
|
|
73
|
+
const anchor = entry.target;
|
|
74
|
+
const href = getAnchorHref(anchor);
|
|
75
|
+
if (!href)
|
|
76
|
+
continue;
|
|
77
|
+
const strategy = getLinkStrategy(anchor, defaultStrategy);
|
|
78
|
+
if (strategy === 'none' || strategy === 'hover')
|
|
79
|
+
continue;
|
|
80
|
+
doPrefetch(href);
|
|
81
|
+
io.unobserve(anchor);
|
|
82
|
+
}
|
|
83
|
+
}, { rootMargin: '200px' });
|
|
84
|
+
const observeLinks = (root) => {
|
|
85
|
+
const anchors = root.querySelectorAll('a[href]');
|
|
86
|
+
for (const a of anchors) {
|
|
87
|
+
const anchor = a;
|
|
88
|
+
if (observedLinks.has(anchor))
|
|
89
|
+
continue;
|
|
90
|
+
const href = getAnchorHref(anchor);
|
|
91
|
+
if (!href)
|
|
92
|
+
continue;
|
|
93
|
+
const strategy = getLinkStrategy(anchor, defaultStrategy);
|
|
94
|
+
if (strategy !== 'viewport')
|
|
95
|
+
continue;
|
|
96
|
+
observedLinks.add(anchor);
|
|
97
|
+
io.observe(anchor);
|
|
98
|
+
}
|
|
99
|
+
// Walk shadow roots to find links inside web components
|
|
100
|
+
const els = root.querySelectorAll('*');
|
|
101
|
+
for (const el of els) {
|
|
102
|
+
if (el.shadowRoot)
|
|
103
|
+
observeLinks(el.shadowRoot);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const observeShadowRoot = (sr) => {
|
|
107
|
+
observeLinks(sr);
|
|
108
|
+
const smo = new MutationObserver(() => observeLinks(sr));
|
|
109
|
+
smo.observe(sr, { childList: true, subtree: true });
|
|
110
|
+
};
|
|
111
|
+
// Observe existing links after initial render (including shadow roots)
|
|
112
|
+
requestAnimationFrame(() => {
|
|
113
|
+
observeLinks(document);
|
|
114
|
+
// Also observe shadow roots of existing elements
|
|
115
|
+
document.querySelectorAll('*').forEach(el => {
|
|
116
|
+
if (el.shadowRoot)
|
|
117
|
+
observeShadowRoot(el.shadowRoot);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
// Watch for dynamically added links and new shadow roots
|
|
121
|
+
const mo = new MutationObserver((mutations) => {
|
|
122
|
+
for (const mutation of mutations) {
|
|
123
|
+
for (const node of mutation.addedNodes) {
|
|
124
|
+
if (node instanceof HTMLElement) {
|
|
125
|
+
if (node.tagName === 'A') {
|
|
126
|
+
observeLinks(node.parentElement || document);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
observeLinks(node);
|
|
130
|
+
if (node.shadowRoot)
|
|
131
|
+
observeShadowRoot(node.shadowRoot);
|
|
132
|
+
}
|
|
133
|
+
// Check children for shadow roots
|
|
134
|
+
node.querySelectorAll?.('*').forEach(child => {
|
|
135
|
+
if (child.shadowRoot)
|
|
136
|
+
observeShadowRoot(child.shadowRoot);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
mo.observe(document.body, { childList: true, subtree: true });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
3
145
|
/**
|
|
4
146
|
* <nk-app> — The application shell. Sets up the router and renders pages.
|
|
5
147
|
*/
|
|
@@ -14,7 +156,29 @@ class NkApp extends HTMLElement {
|
|
|
14
156
|
this.innerHTML = '<div id="nk-router-outlet"></div>';
|
|
15
157
|
}
|
|
16
158
|
const outlet = this.querySelector('#nk-router-outlet');
|
|
159
|
+
// Create route announcer for screen readers (WCAG 2.4.2)
|
|
160
|
+
const announcer = document.createElement('div');
|
|
161
|
+
announcer.id = 'nk-route-announcer';
|
|
162
|
+
announcer.setAttribute('aria-live', 'assertive');
|
|
163
|
+
announcer.setAttribute('aria-atomic', 'true');
|
|
164
|
+
announcer.setAttribute('role', 'status');
|
|
165
|
+
Object.assign(announcer.style, {
|
|
166
|
+
position: 'absolute',
|
|
167
|
+
width: '1px',
|
|
168
|
+
height: '1px',
|
|
169
|
+
padding: '0',
|
|
170
|
+
margin: '-1px',
|
|
171
|
+
overflow: 'hidden',
|
|
172
|
+
clip: 'rect(0,0,0,0)',
|
|
173
|
+
whiteSpace: 'nowrap',
|
|
174
|
+
border: '0',
|
|
175
|
+
});
|
|
176
|
+
this.appendChild(announcer);
|
|
17
177
|
this.router = new NkRouter(routes, outlet, isSSR);
|
|
178
|
+
const strategy = getDefaultStrategy();
|
|
179
|
+
if (strategy !== 'none') {
|
|
180
|
+
setupPrefetchObserver(strategy);
|
|
181
|
+
}
|
|
18
182
|
}
|
|
19
183
|
}
|
|
20
184
|
if (!customElements.get('nk-app')) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side auth store — singleton following the i18n.ts pattern.
|
|
3
|
+
* Hydrated from __nk_auth__ script tag on page load.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getUser(): any;
|
|
6
|
+
export declare function isAuthenticated(): boolean;
|
|
7
|
+
export declare function hasRole(role: string): boolean;
|
|
8
|
+
export declare function initAuth(user: any): void;
|
|
9
|
+
export declare function login(returnTo?: string): void;
|
|
10
|
+
export declare function logout(): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side auth store — singleton following the i18n.ts pattern.
|
|
3
|
+
* Hydrated from __nk_auth__ script tag on page load.
|
|
4
|
+
*/
|
|
5
|
+
let currentUser = null;
|
|
6
|
+
let initialized = false;
|
|
7
|
+
export function getUser() {
|
|
8
|
+
return currentUser;
|
|
9
|
+
}
|
|
10
|
+
export function isAuthenticated() {
|
|
11
|
+
return currentUser !== null;
|
|
12
|
+
}
|
|
13
|
+
export function hasRole(role) {
|
|
14
|
+
return currentUser?.roles?.includes(role) ?? false;
|
|
15
|
+
}
|
|
16
|
+
export function initAuth(user) {
|
|
17
|
+
currentUser = user;
|
|
18
|
+
initialized = true;
|
|
19
|
+
}
|
|
20
|
+
export function login(returnTo) {
|
|
21
|
+
const url = new URL('/__nk_auth/login', location.origin);
|
|
22
|
+
if (returnTo)
|
|
23
|
+
url.searchParams.set('returnTo', returnTo);
|
|
24
|
+
location.href = url.toString();
|
|
25
|
+
}
|
|
26
|
+
export function logout() {
|
|
27
|
+
currentUser = null;
|
|
28
|
+
initialized = false;
|
|
29
|
+
location.href = '/__nk_auth/logout';
|
|
30
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side communication SDK.
|
|
3
|
+
* Connects to the server via Socket.io and provides a clean API for chat, typing, and presence.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Connect to the communication socket.
|
|
7
|
+
* Must be called before using any other functions.
|
|
8
|
+
*/
|
|
9
|
+
export declare function connectChat(params?: Record<string, string>): Promise<void>;
|
|
10
|
+
/** Join a conversation room to receive messages */
|
|
11
|
+
export declare function joinConversation(conversationId: string): void;
|
|
12
|
+
/** Leave a conversation room */
|
|
13
|
+
export declare function leaveConversation(conversationId: string): void;
|
|
14
|
+
/** Send a message */
|
|
15
|
+
export declare function sendMessage(conversationId: string, content: string, type?: string): void;
|
|
16
|
+
/** Mark a message as read */
|
|
17
|
+
export declare function markRead(conversationId: string, messageId: string): void;
|
|
18
|
+
/** React to a message with an emoji (toggle) */
|
|
19
|
+
export declare function reactToMessage(messageId: string, conversationId: string, emoji: string): void;
|
|
20
|
+
/** Edit a message */
|
|
21
|
+
export declare function editMessage(messageId: string, conversationId: string, content: string): void;
|
|
22
|
+
/** Delete a message */
|
|
23
|
+
export declare function deleteMessage(messageId: string, conversationId: string): void;
|
|
24
|
+
/** Listen for reaction updates */
|
|
25
|
+
export declare function onReactionUpdate(handler: (data: {
|
|
26
|
+
messageId: string;
|
|
27
|
+
reactions: any[];
|
|
28
|
+
}) => void): () => void;
|
|
29
|
+
/** Listen for message edits */
|
|
30
|
+
export declare function onMessageUpdated(handler: (data: {
|
|
31
|
+
messageId: string;
|
|
32
|
+
content: string;
|
|
33
|
+
updatedAt: string;
|
|
34
|
+
}) => void): () => void;
|
|
35
|
+
/** Listen for message deletions */
|
|
36
|
+
export declare function onMessageDeleted(handler: (data: {
|
|
37
|
+
messageId: string;
|
|
38
|
+
conversationId: string;
|
|
39
|
+
}) => void): () => void;
|
|
40
|
+
/** Upload a file (returns attachment metadata) */
|
|
41
|
+
export declare function uploadFile(file: Blob, filename: string, encrypted?: boolean): Promise<{
|
|
42
|
+
id: string;
|
|
43
|
+
url: string;
|
|
44
|
+
size: number;
|
|
45
|
+
}>;
|
|
46
|
+
/** Fetch link previews for a text */
|
|
47
|
+
export declare function fetchLinkPreviews(text: string): Promise<any[]>;
|
|
48
|
+
/** Start typing indicator */
|
|
49
|
+
export declare function startTyping(conversationId: string): void;
|
|
50
|
+
/** Stop typing indicator */
|
|
51
|
+
export declare function stopTyping(conversationId: string): void;
|
|
52
|
+
/** Update presence status */
|
|
53
|
+
export declare function updatePresence(status: 'online' | 'offline' | 'away' | 'busy'): void;
|
|
54
|
+
/** Listen for new messages */
|
|
55
|
+
export declare function onMessage(handler: (message: any) => void): () => void;
|
|
56
|
+
/** Listen for typing updates */
|
|
57
|
+
export declare function onTyping(handler: (data: {
|
|
58
|
+
conversationId: string;
|
|
59
|
+
userId: string;
|
|
60
|
+
isTyping: boolean;
|
|
61
|
+
}) => void): () => void;
|
|
62
|
+
/** Listen for presence changes */
|
|
63
|
+
export declare function onPresence(handler: (data: {
|
|
64
|
+
userId: string;
|
|
65
|
+
status: string;
|
|
66
|
+
lastSeen: string;
|
|
67
|
+
}) => void): () => void;
|
|
68
|
+
/** Listen for read receipts */
|
|
69
|
+
export declare function onReadReceipt(handler: (data: any) => void): () => void;
|
|
70
|
+
/** Listen for incoming calls */
|
|
71
|
+
export declare function onIncomingCall(handler: (call: any) => void): () => void;
|
|
72
|
+
/** Listen for call state changes (initiating, ringing, connecting, connected, ended) */
|
|
73
|
+
export declare function onCallStateChanged(handler: (data: {
|
|
74
|
+
callId: string;
|
|
75
|
+
state: string;
|
|
76
|
+
endReason?: string;
|
|
77
|
+
}) => void): () => void;
|
|
78
|
+
/** Listen for participants joining a call */
|
|
79
|
+
export declare function onParticipantJoined(handler: (data: {
|
|
80
|
+
callId: string;
|
|
81
|
+
participant: any;
|
|
82
|
+
}) => void): () => void;
|
|
83
|
+
/** Listen for participants leaving a call */
|
|
84
|
+
export declare function onParticipantLeft(handler: (data: {
|
|
85
|
+
callId: string;
|
|
86
|
+
userId: string;
|
|
87
|
+
}) => void): () => void;
|
|
88
|
+
/** Listen for media changes (mute/unmute/screenshare) */
|
|
89
|
+
export declare function onMediaChanged(handler: (data: {
|
|
90
|
+
callId: string;
|
|
91
|
+
userId: string;
|
|
92
|
+
audio?: boolean;
|
|
93
|
+
video?: boolean;
|
|
94
|
+
screenShare?: boolean;
|
|
95
|
+
}) => void): () => void;
|
|
96
|
+
/** Initiate an audio or video call */
|
|
97
|
+
export declare function initiateCall(conversationId: string, type: 'audio' | 'video', calleeIds: string[]): void;
|
|
98
|
+
/** Respond to an incoming call */
|
|
99
|
+
export declare function respondToCall(callId: string, action: 'accept' | 'reject'): void;
|
|
100
|
+
/** Hang up an active call */
|
|
101
|
+
export declare function hangup(callId: string, reason?: string): void;
|
|
102
|
+
/** Toggle audio/video/screenshare during a call */
|
|
103
|
+
export declare function toggleMedia(callId: string, opts: {
|
|
104
|
+
audio?: boolean;
|
|
105
|
+
video?: boolean;
|
|
106
|
+
screenShare?: boolean;
|
|
107
|
+
}): void;
|
|
108
|
+
/** Send SDP offer to a peer */
|
|
109
|
+
export declare function sendOffer(callId: string, toUserId: string, sdp: string): void;
|
|
110
|
+
/** Send SDP answer to a peer */
|
|
111
|
+
export declare function sendAnswer(callId: string, toUserId: string, sdp: string): void;
|
|
112
|
+
/** Send ICE candidate to a peer */
|
|
113
|
+
export declare function sendIceCandidate(callId: string, toUserId: string, candidate: string, sdpMLineIndex: number | null, sdpMid: string | null): void;
|
|
114
|
+
/** Listen for SDP offers from peers */
|
|
115
|
+
export declare function onSignalOffer(handler: (data: {
|
|
116
|
+
callId: string;
|
|
117
|
+
fromUserId: string;
|
|
118
|
+
sdp: string;
|
|
119
|
+
}) => void): () => void;
|
|
120
|
+
/** Listen for SDP answers from peers */
|
|
121
|
+
export declare function onSignalAnswer(handler: (data: {
|
|
122
|
+
callId: string;
|
|
123
|
+
fromUserId: string;
|
|
124
|
+
sdp: string;
|
|
125
|
+
}) => void): () => void;
|
|
126
|
+
/** Listen for ICE candidates from peers */
|
|
127
|
+
export declare function onIceCandidate(handler: (data: {
|
|
128
|
+
callId: string;
|
|
129
|
+
fromUserId: string;
|
|
130
|
+
candidate: string;
|
|
131
|
+
sdpMLineIndex: number | null;
|
|
132
|
+
sdpMid: string | null;
|
|
133
|
+
}) => void): () => void;
|
|
134
|
+
/** Disconnect from communication socket */
|
|
135
|
+
export declare function disconnect(): void;
|
|
136
|
+
/** Check if connected */
|
|
137
|
+
export declare function isConnected(): boolean;
|