@supersoniks/concorde 4.6.0 → 4.7.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/.gitlab-ci.yml +23 -0
- package/README.md +106 -55
- package/ai/AGENTS.md +52 -0
- package/ai/README.md +30 -0
- package/ai/cursor/rules/concorde-menu.mdc +15 -0
- package/ai/cursor/rules/concorde-scope.mdc +14 -0
- package/ai/cursor/rules/concorde-theme.mdc +13 -0
- package/ai/cursor/rules/concorde.mdc +49 -0
- package/ai/jetbrains/rules/concorde.md +39 -0
- package/ai/skills/concorde/SKILL.md +220 -0
- package/ai/skills/concorde-get-set-dp/SKILL.md +194 -0
- package/ai/skills/concorde-imports/SKILL.md +78 -0
- package/ai/skills/concorde-menu/SKILL.md +74 -0
- package/ai/skills/concorde-scope/SKILL.md +70 -0
- package/ai/skills/concorde-theme/SKILL.md +46 -0
- package/build-infos.json +1 -1
- package/concorde-core.bundle.js +127 -127
- package/concorde-core.es.js +1435 -1364
- package/dist/altcha-widget.js +2662 -0
- package/dist/concorde-core.bundle.js +127 -127
- package/dist/concorde-core.es.js +1435 -1364
- package/dist/docs-mock-api-sw.js +589 -0
- package/dist/docs-mock-api-sw.js.map +7 -0
- package/docs/altcha-widget.js +2662 -0
- package/docs/assets/index-D9pxaQYK.js +7508 -0
- package/docs/assets/index-t0-i22oI.css +1 -0
- package/docs/docs-mock-api-sw.js +589 -0
- package/docs/docs-mock-api-sw.js.map +7 -0
- package/docs/index.html +2 -2
- package/docs/src/core/components/functional/fetch/fetch.md +13 -11
- package/docs/src/core/components/functional/if/if.md +4 -11
- package/docs/src/core/components/functional/list/list.md +60 -194
- package/docs/src/core/components/functional/queue/queue.md +70 -85
- package/docs/src/core/components/functional/router/router.md +62 -97
- package/docs/src/core/components/functional/states/states.md +2 -2
- package/docs/src/core/components/functional/submit/submit.md +86 -55
- package/docs/src/core/components/ui/captcha/captcha.md +2 -2
- package/docs/src/core/components/ui/card/card.md +1 -1
- package/docs/src/core/components/ui/form/checkbox/checkbox.md +5 -32
- package/docs/src/core/components/ui/form/input/input.md +5 -30
- package/docs/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +6 -4
- package/docs/src/core/components/ui/form/radio/radio.md +5 -32
- package/docs/src/core/components/ui/form/select/select.md +5 -31
- package/docs/src/core/components/ui/form/switch/switch.md +5 -32
- package/docs/src/core/components/ui/loader/loader.md +1 -13
- package/docs/src/core/components/ui/table/table.md +3 -3
- package/docs/src/docs/_core-concept/dataFlow.md +73 -0
- package/docs/src/docs/_core-concept/subscriber.md +9 -10
- package/docs/src/docs/_decorators/ancestor-attribute.md +4 -3
- package/docs/src/docs/_decorators/auto-subscribe.md +19 -16
- package/docs/src/docs/_decorators/bind.md +20 -17
- package/docs/src/docs/_decorators/get.md +7 -4
- package/docs/src/docs/_decorators/handle.md +171 -0
- package/docs/src/docs/_decorators/on-assign.md +99 -73
- package/docs/src/docs/_decorators/publish.md +2 -1
- package/docs/src/docs/_decorators/subscribe.md +70 -9
- package/docs/src/docs/_decorators/wait-for-ancestors.md +13 -10
- package/docs/src/docs/_directives/sub.md +91 -0
- package/docs/src/docs/_getting-started/ai-agents.md +56 -0
- package/docs/src/docs/_getting-started/concorde-manual-install.md +133 -0
- package/docs/src/docs/_getting-started/concorde-outside.md +13 -123
- package/docs/src/docs/_getting-started/create-a-component.md +2 -0
- package/docs/src/docs/_getting-started/my-first-component.md +236 -0
- package/docs/src/docs/_getting-started/my-first-subscriber.md +29 -83
- package/docs/src/docs/_getting-started/pubsub.md +21 -134
- package/docs/src/docs/_getting-started/start.md +26 -18
- package/docs/src/docs/_misc/api-configuration.md +79 -0
- package/docs/src/docs/_misc/dataProviderKey.md +38 -5
- package/docs/src/docs/_misc/docs-mock-api.md +60 -0
- package/docs/src/docs/_misc/endpoint.md +2 -1
- package/docs/src/docs/_misc/html-integration.md +13 -0
- package/docs/src/docs/search/docs-search.json +4163 -873
- package/docs/src/tsconfig.json +380 -317
- package/gitlab/job_tests.sh +55 -0
- package/package.json +34 -3
- package/public/altcha-widget.js +2662 -0
- package/public/docs-mock-api-sw.js +589 -0
- package/public/docs-mock-api-sw.js.map +7 -0
- package/scripts/ai-init.mjs +167 -0
- package/scripts/docs-mock-api-vite-plugin.ts +116 -0
- package/scripts/docs-open-in-editor-plugin.ts +130 -0
- package/scripts/pre-publish.mjs +2 -1
- package/src/core/components/functional/example/example.ts +1 -1
- package/src/core/components/functional/fetch/fetch.md +13 -11
- package/src/core/components/functional/if/if.md +4 -11
- package/src/core/components/functional/list/list.demo.ts +4 -4
- package/src/core/components/functional/list/list.md +60 -194
- package/src/core/components/functional/list/list.ts +8 -7
- package/src/core/components/functional/queue/queue.demo.ts +1 -1
- package/src/core/components/functional/queue/queue.md +70 -85
- package/src/core/components/functional/queue/queue.ts +4 -4
- package/src/core/components/functional/router/router.md +62 -97
- package/src/core/components/functional/router/router.ts +1 -1
- package/src/core/components/functional/states/states.md +2 -2
- package/src/core/components/functional/submit/submit.md +86 -55
- package/src/core/components/functional/submit/submit.ts +10 -3
- package/src/core/components/ui/captcha/captcha.md +2 -2
- package/src/core/components/ui/card/card.md +1 -1
- package/src/core/components/ui/form/checkbox/checkbox.md +5 -32
- package/src/core/components/ui/form/input/input.md +5 -30
- package/src/core/components/ui/form/input-autocomplete/input-autocomplete.md +6 -4
- package/src/core/components/ui/form/radio/radio.md +5 -32
- package/src/core/components/ui/form/select/select.md +5 -31
- package/src/core/components/ui/form/switch/switch.md +5 -32
- package/src/core/components/ui/loader/loader.md +1 -13
- package/src/core/components/ui/table/table.md +3 -3
- package/src/core/directives/DataProvider.sub.spec.ts +96 -0
- package/src/core/directives/DataProvider.ts +109 -40
- package/src/core/utils/PublisherProxy.ts +33 -18
- package/src/core/utils/dataProviderKey.ts +23 -0
- package/src/core/utils/publisherPathKey.spec.ts +58 -0
- package/src/docs/_core-concept/dataFlow.md +73 -0
- package/src/docs/_core-concept/subscriber.md +9 -10
- package/src/docs/_decorators/ancestor-attribute.md +4 -3
- package/src/docs/_decorators/auto-subscribe.md +19 -16
- package/src/docs/_decorators/bind.md +19 -16
- package/src/docs/_decorators/get.md +7 -4
- package/src/docs/_decorators/handle.md +15 -13
- package/src/docs/_decorators/on-assign.md +53 -53
- package/src/docs/_decorators/publish.md +2 -1
- package/src/docs/_decorators/subscribe.md +70 -9
- package/src/docs/_decorators/wait-for-ancestors.md +13 -10
- package/src/docs/_directives/sub.md +91 -0
- package/src/docs/_getting-started/ai-agents.md +56 -0
- package/src/docs/_getting-started/concorde-manual-install.md +133 -0
- package/src/docs/_getting-started/concorde-outside.md +13 -123
- package/src/docs/_getting-started/create-a-component.md +2 -0
- package/src/docs/_getting-started/my-first-component.md +236 -0
- package/src/docs/_getting-started/my-first-subscriber.md +29 -83
- package/src/docs/_getting-started/pubsub.md +21 -134
- package/src/docs/_getting-started/start.md +26 -18
- package/src/docs/_misc/api-configuration.md +79 -0
- package/src/docs/_misc/dataProviderKey.md +34 -1
- package/src/docs/_misc/docs-mock-api.md +60 -0
- package/src/docs/_misc/endpoint.md +2 -1
- package/src/docs/_misc/html-integration.md +13 -0
- package/src/docs/code.ts +58 -12
- package/src/docs/components/docs-demo-sources.ts +397 -0
- package/src/docs/components/docs-lit-demo-raw.ts +28 -0
- package/src/docs/components/docs-lit-demo.ts +166 -0
- package/src/docs/components/docs-source-link.ts +72 -0
- package/src/docs/docs-location.ts +54 -0
- package/src/docs/docs.ts +12 -0
- package/src/docs/example/decorators-demo-bind-demos.ts +41 -46
- package/src/docs/example/decorators-demo-geo.ts +16 -11
- package/src/docs/example/decorators-demo-init.ts +2 -228
- package/src/docs/example/decorators-demo-subscribe-publish-get-demos.ts +54 -14
- package/src/docs/example/decorators-demo.ts +71 -70
- package/src/docs/example/docs-api-config-demos.ts +234 -0
- package/src/docs/example/docs-joke-demos.ts +297 -0
- package/src/docs/example/docs-list-demos.ts +179 -0
- package/src/docs/example/docs-provider-keys.ts +315 -0
- package/src/docs/example/docs-queue-demos.ts +114 -0
- package/src/docs/example/docs-router-demos.ts +89 -0
- package/src/docs/example/docs-submit-demos.ts +455 -0
- package/src/docs/example/docs-toggle-demos.ts +73 -0
- package/src/docs/example/docs-user-two-scopes.ts +37 -0
- package/src/docs/example/docs-users-list.ts +71 -0
- package/src/docs/example/users.ts +41 -24
- package/src/docs/mock-api/api-config-mock.ts +152 -0
- package/src/docs/mock-api/fixtures.ts +377 -0
- package/src/docs/mock-api/register.ts +25 -0
- package/src/docs/mock-api/router.ts +234 -0
- package/src/docs/mock-api/service-worker.ts +23 -0
- package/src/docs/mock-api/urls.ts +11 -0
- package/src/docs/navigation/navigation.ts +39 -7
- package/src/docs/search/docs-search.json +4021 -936
- package/src/docs/search/markdown-renderer.ts +7 -3
- package/src/docs/search/page.ts +11 -14
- package/src/docs/search/sonic-code-markdown.spec.ts +29 -0
- package/src/docs/search/sonic-code-markdown.ts +28 -0
- package/src/docs.ts +4 -0
- package/src/tsconfig.json +87 -0
- package/src/tsconfig.tsbuildinfo +1 -1
- package/vite.config.mts +8 -0
- package/docs/assets/index-CaysOMFz.js +0 -5046
- package/docs/assets/index-D8mGoXzF.css +0 -1
- package/docs/src/docs/_misc/templates-demo.md +0 -19
- package/src/docs/_misc/templates-demo.md +0 -19
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Installe les fichiers agent IA Concorde dans un projet.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node node_modules/@supersoniks/concorde/scripts/ai-init.mjs [options]
|
|
7
|
+
* yarn concorde ai-init
|
|
8
|
+
*
|
|
9
|
+
* Options:
|
|
10
|
+
* --project-root <dir> Racine du projet (défaut: cwd)
|
|
11
|
+
* --concorde-ai <dir> Dossier ai/ Concorde (défaut: auto-detect)
|
|
12
|
+
* --overlay <dir> Couche additionnelle (ex. starter kit)
|
|
13
|
+
* --cursor-only N'installe que Cursor (.cursor/)
|
|
14
|
+
* --jetbrains-only N'installe que JetBrains (.aiassistant/rules/)
|
|
15
|
+
* --merge-agents Fusionne AGENTS.md au lieu d'écraser si existant
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import {fileURLToPath} from "node:url";
|
|
21
|
+
|
|
22
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
|
|
26
|
+
function readArg(name) {
|
|
27
|
+
const i = args.indexOf(name);
|
|
28
|
+
if (i === -1) return undefined;
|
|
29
|
+
return args[i + 1];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const flags = new Set(args.filter((a) => a.startsWith("--") && !a.includes("=")));
|
|
33
|
+
|
|
34
|
+
const projectRoot = path.resolve(readArg("--project-root") ?? process.cwd());
|
|
35
|
+
const overlayDir = readArg("--overlay")
|
|
36
|
+
? path.resolve(readArg("--overlay"))
|
|
37
|
+
: undefined;
|
|
38
|
+
|
|
39
|
+
const cursorOnly = flags.has("--cursor-only");
|
|
40
|
+
const jetbrainsOnly = flags.has("--jetbrains-only");
|
|
41
|
+
const mergeAgents = flags.has("--merge-agents");
|
|
42
|
+
const installCursor = !jetbrainsOnly;
|
|
43
|
+
const installJetbrains = !cursorOnly;
|
|
44
|
+
|
|
45
|
+
function detectConcordeAi() {
|
|
46
|
+
const explicit = readArg("--concorde-ai");
|
|
47
|
+
if (explicit) return path.resolve(explicit);
|
|
48
|
+
const fromScript = path.resolve(__dirname, "../ai");
|
|
49
|
+
if (fs.existsSync(fromScript)) return fromScript;
|
|
50
|
+
const fromNodeModules = path.resolve(
|
|
51
|
+
projectRoot,
|
|
52
|
+
"node_modules/@supersoniks/concorde/ai",
|
|
53
|
+
);
|
|
54
|
+
if (fs.existsSync(fromNodeModules)) return fromNodeModules;
|
|
55
|
+
throw new Error(
|
|
56
|
+
"Dossier ai/ Concorde introuvable. Passez --concorde-ai <chemin>.",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function copyDir(src, dest) {
|
|
61
|
+
if (!fs.existsSync(src)) return;
|
|
62
|
+
fs.mkdirSync(dest, {recursive: true});
|
|
63
|
+
for (const entry of fs.readdirSync(src, {withFileTypes: true})) {
|
|
64
|
+
const from = path.join(src, entry.name);
|
|
65
|
+
const to = path.join(dest, entry.name);
|
|
66
|
+
if (entry.isDirectory()) {
|
|
67
|
+
copyDir(from, to);
|
|
68
|
+
} else {
|
|
69
|
+
fs.mkdirSync(path.dirname(to), {recursive: true});
|
|
70
|
+
fs.copyFileSync(from, to);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function mergeAgentsFile(targetPath, baseContent, overlayContent) {
|
|
76
|
+
const marker = "<!-- concorde-ai -->";
|
|
77
|
+
let existing = "";
|
|
78
|
+
if (fs.existsSync(targetPath)) {
|
|
79
|
+
existing = fs.readFileSync(targetPath, "utf8");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const parts = [baseContent.trim()];
|
|
83
|
+
if (overlayContent?.trim()) {
|
|
84
|
+
parts.push(overlayContent.trim());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const combined = parts.join("\n\n---\n\n");
|
|
88
|
+
|
|
89
|
+
if (!existing) {
|
|
90
|
+
fs.mkdirSync(path.dirname(targetPath), {recursive: true});
|
|
91
|
+
fs.writeFileSync(targetPath, combined + "\n");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (existing.includes(marker)) {
|
|
96
|
+
const before = existing.split(marker)[0].trimEnd();
|
|
97
|
+
fs.writeFileSync(targetPath, `${before}\n\n${marker}\n\n${combined}\n`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (mergeAgents) {
|
|
102
|
+
fs.writeFileSync(
|
|
103
|
+
targetPath,
|
|
104
|
+
`${existing.trimEnd()}\n\n---\n\n${marker}\n\n${combined}\n`,
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fs.writeFileSync(targetPath, combined + "\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const concordeAi = detectConcordeAi();
|
|
113
|
+
|
|
114
|
+
console.log(`Concorde ai-init → ${projectRoot}`);
|
|
115
|
+
console.log(` source: ${concordeAi}`);
|
|
116
|
+
if (overlayDir) console.log(` overlay: ${overlayDir}`);
|
|
117
|
+
|
|
118
|
+
if (installCursor) {
|
|
119
|
+
copyDir(
|
|
120
|
+
path.join(concordeAi, "cursor", "rules"),
|
|
121
|
+
path.join(projectRoot, ".cursor", "rules"),
|
|
122
|
+
);
|
|
123
|
+
copyDir(
|
|
124
|
+
path.join(concordeAi, "skills"),
|
|
125
|
+
path.join(projectRoot, ".cursor", "skills"),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (overlayDir) {
|
|
129
|
+
copyDir(
|
|
130
|
+
path.join(overlayDir, "cursor", "rules"),
|
|
131
|
+
path.join(projectRoot, ".cursor", "rules"),
|
|
132
|
+
);
|
|
133
|
+
copyDir(
|
|
134
|
+
path.join(overlayDir, "skills"),
|
|
135
|
+
path.join(projectRoot, ".cursor", "skills"),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (installJetbrains) {
|
|
141
|
+
copyDir(
|
|
142
|
+
path.join(concordeAi, "jetbrains", "rules"),
|
|
143
|
+
path.join(projectRoot, ".aiassistant", "rules"),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
if (overlayDir) {
|
|
147
|
+
copyDir(
|
|
148
|
+
path.join(overlayDir, "jetbrains", "rules"),
|
|
149
|
+
path.join(projectRoot, ".aiassistant", "rules"),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const baseAgents = fs.readFileSync(path.join(concordeAi, "AGENTS.md"), "utf8");
|
|
155
|
+
let overlayAgents = "";
|
|
156
|
+
if (overlayDir) {
|
|
157
|
+
const overlayPath = path.join(overlayDir, "AGENTS.starter.md");
|
|
158
|
+
if (fs.existsSync(overlayPath)) {
|
|
159
|
+
overlayAgents = fs.readFileSync(overlayPath, "utf8");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
mergeAgentsFile(path.join(projectRoot, "AGENTS.md"), baseAgents, overlayAgents);
|
|
163
|
+
|
|
164
|
+
console.log(" ✓ AGENTS.md");
|
|
165
|
+
if (installCursor) console.log(" ✓ .cursor/skills + .cursor/rules");
|
|
166
|
+
if (installJetbrains) console.log(" ✓ .aiassistant/rules");
|
|
167
|
+
console.log("Done.");
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as esbuild from "esbuild";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import {fileURLToPath} from "url";
|
|
5
|
+
import type {IncomingMessage} from "node:http";
|
|
6
|
+
import type {Connect, Plugin} from "vite";
|
|
7
|
+
import {handleDocsMockApiRequest} from "../src/docs/mock-api/router";
|
|
8
|
+
|
|
9
|
+
function readNodeRequestBody(req: IncomingMessage): Promise<Buffer | undefined> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const method = req.method?.toUpperCase() ?? "GET";
|
|
12
|
+
if (method === "GET" || method === "HEAD") {
|
|
13
|
+
resolve(undefined);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const chunks: Buffer[] = [];
|
|
17
|
+
req.on("data", (chunk) => chunks.push(chunk as Buffer));
|
|
18
|
+
req.on("end", () => resolve(chunks.length ? Buffer.concat(chunks) : undefined));
|
|
19
|
+
req.on("error", reject);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** HTTP/2 pseudo-headers (`:method`, …) and other invalid names must not reach `Headers`. */
|
|
24
|
+
function isValidFetchHeaderName(name: string): boolean {
|
|
25
|
+
if (!name || name.startsWith(":")) return false;
|
|
26
|
+
return /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/.test(name);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function nodeHeadersToFetchHeaders(req: IncomingMessage): Headers {
|
|
30
|
+
const headers = new Headers();
|
|
31
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
32
|
+
if (value == null || !isValidFetchHeaderName(key)) continue;
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
for (const v of value) headers.append(key, v);
|
|
35
|
+
} else {
|
|
36
|
+
headers.set(key, value);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return headers;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
43
|
+
const root = path.resolve(__dirname, "..");
|
|
44
|
+
const swEntry = path.join(root, "src/docs/mock-api/service-worker.ts");
|
|
45
|
+
async function buildDocsMockApiServiceWorker(): Promise<void> {
|
|
46
|
+
await esbuild.build({
|
|
47
|
+
entryPoints: [swEntry],
|
|
48
|
+
outfile: path.join(root, "public/docs-mock-api-sw.js"),
|
|
49
|
+
bundle: true,
|
|
50
|
+
format: "iife",
|
|
51
|
+
platform: "browser",
|
|
52
|
+
target: "es2020",
|
|
53
|
+
minify: false,
|
|
54
|
+
sourcemap: true,
|
|
55
|
+
logLevel: "silent",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function docsMockApiVitePlugin(): Plugin {
|
|
60
|
+
const middleware: Connect.NextHandleFunction = async (req, res, next) => {
|
|
61
|
+
const rawUrl = req.url?.split("?")[0] ?? "";
|
|
62
|
+
if (!rawUrl.startsWith("/docs-mock-api")) {
|
|
63
|
+
return next();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const proto =
|
|
67
|
+
(req.headers["x-forwarded-proto"] as string | undefined) ?? "http";
|
|
68
|
+
const host = req.headers.host ?? "localhost";
|
|
69
|
+
const body = await readNodeRequestBody(req);
|
|
70
|
+
const init: RequestInit = {
|
|
71
|
+
method: req.method ?? "GET",
|
|
72
|
+
headers: nodeHeadersToFetchHeaders(req),
|
|
73
|
+
};
|
|
74
|
+
if (body?.length) {
|
|
75
|
+
init.body = new Uint8Array(body);
|
|
76
|
+
}
|
|
77
|
+
const request = new Request(
|
|
78
|
+
new URL(req.url!, `${proto}://${host}`),
|
|
79
|
+
init,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const response = await handleDocsMockApiRequest(request);
|
|
84
|
+
if (!response) {
|
|
85
|
+
res.statusCode = 404;
|
|
86
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
87
|
+
res.end(JSON.stringify({error: "Not handled", path: rawUrl}));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
res.statusCode = response.status;
|
|
92
|
+
response.headers.forEach((value, key) => {
|
|
93
|
+
if (key.toLowerCase() === "content-length") return;
|
|
94
|
+
res.setHeader(key, value);
|
|
95
|
+
});
|
|
96
|
+
res.end(Buffer.from(await response.arrayBuffer()));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.error("[docs-mock-api] middleware error", err);
|
|
99
|
+
next();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
name: "concorde-docs-mock-api",
|
|
105
|
+
async buildStart() {
|
|
106
|
+
await buildDocsMockApiServiceWorker();
|
|
107
|
+
},
|
|
108
|
+
configureServer(server) {
|
|
109
|
+
// Avant le fallback SPA Vite (configureServer synchrone, pas le callback)
|
|
110
|
+
server.middlewares.use(middleware);
|
|
111
|
+
},
|
|
112
|
+
configurePreviewServer(server) {
|
|
113
|
+
server.middlewares.use(middleware);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import type { Connect, Plugin } from "vite";
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const projectRoot = path.resolve(__dirname, "..");
|
|
9
|
+
|
|
10
|
+
const OPEN_PREFIX = "/__docs/open";
|
|
11
|
+
|
|
12
|
+
/** Paths allowed for ↗ source links (demo / mock API sources only). */
|
|
13
|
+
export function isDocsSourcePathAllowed(relPath: string): boolean {
|
|
14
|
+
const normalized = path.normalize(relPath).replace(/\\/g, "/");
|
|
15
|
+
if (normalized.startsWith("..") || path.isAbsolute(normalized)) return false;
|
|
16
|
+
|
|
17
|
+
if (normalized.startsWith("src/docs/example/")) return true;
|
|
18
|
+
if (normalized.startsWith("src/docs/mock-api/")) return true;
|
|
19
|
+
if (/^src\/core\/components\/functional\/[^/]+\/[^/]+\.demo\.ts$/.test(normalized)) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
const functionalImpl = new Set([
|
|
23
|
+
"src/core/components/functional/queue/queue.ts",
|
|
24
|
+
"src/core/components/functional/list/list.ts",
|
|
25
|
+
]);
|
|
26
|
+
if (functionalImpl.has(normalized)) return true;
|
|
27
|
+
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function isEditorAvailable(): boolean {
|
|
32
|
+
const editor = process.env.DOCS_EDITOR ?? process.env.STARTER_EDITOR ?? "cursor";
|
|
33
|
+
try {
|
|
34
|
+
execSync(`${editor} --version`, { stdio: "pipe", timeout: 5000 });
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function openInEditor(absPath: string, line: number, column: number): void {
|
|
42
|
+
const editor = process.env.DOCS_EDITOR ?? process.env.STARTER_EDITOR ?? "cursor";
|
|
43
|
+
const goto = `${absPath}:${line}:${column}`;
|
|
44
|
+
const args = process.env.DOCS_EDITOR_ARGS
|
|
45
|
+
? [...process.env.DOCS_EDITOR_ARGS.split(/\s+/), goto, projectRoot]
|
|
46
|
+
: ["-r", "-g", goto, projectRoot];
|
|
47
|
+
|
|
48
|
+
const child = spawn(editor, args, {
|
|
49
|
+
detached: true,
|
|
50
|
+
stdio: "ignore",
|
|
51
|
+
});
|
|
52
|
+
child.unref();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Dev middleware: open doc demo sources in Cursor (`cursor -r -g`).
|
|
57
|
+
* Only paths under src/docs/example/, src/docs/mock-api/, or *.demo.ts in functional/.
|
|
58
|
+
*/
|
|
59
|
+
export function docsOpenInEditorPlugin(): Plugin {
|
|
60
|
+
let editorAvailable = false;
|
|
61
|
+
|
|
62
|
+
const middleware: Connect.NextHandleFunction = (req, res, next) => {
|
|
63
|
+
if (!req.url || req.method !== "GET" || !req.url.startsWith(OPEN_PREFIX)) {
|
|
64
|
+
return next();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!editorAvailable) {
|
|
68
|
+
res.statusCode = 503;
|
|
69
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
70
|
+
res.end("Editor not available");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const url = new URL(req.url, "http://localhost");
|
|
76
|
+
const relPath = url.searchParams.get("path") ?? "";
|
|
77
|
+
const line = Math.max(1, Number(url.searchParams.get("line") ?? "1") || 1);
|
|
78
|
+
const column = Math.max(
|
|
79
|
+
1,
|
|
80
|
+
Number(url.searchParams.get("column") ?? "1") || 1,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (!relPath || !isDocsSourcePathAllowed(relPath)) {
|
|
84
|
+
res.statusCode = 403;
|
|
85
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
86
|
+
res.end("Forbidden");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const absPath = path.resolve(projectRoot, relPath);
|
|
91
|
+
if (!fs.existsSync(absPath)) {
|
|
92
|
+
res.statusCode = 404;
|
|
93
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
94
|
+
res.end("Not found");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
openInEditor(absPath, line, column);
|
|
99
|
+
res.statusCode = 204;
|
|
100
|
+
res.end();
|
|
101
|
+
} catch {
|
|
102
|
+
res.statusCode = 500;
|
|
103
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
104
|
+
res.end("Error");
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
name: "concorde-docs-open-in-editor",
|
|
110
|
+
config() {
|
|
111
|
+
editorAvailable = isEditorAvailable();
|
|
112
|
+
if (!editorAvailable) {
|
|
113
|
+
console.log(
|
|
114
|
+
"[docs] ↗ source links hidden — Cursor CLI not found (Command Palette → Install 'cursor' command)",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
define: {
|
|
119
|
+
__DOCS_EDITOR_AVAILABLE__: JSON.stringify(editorAvailable),
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
configureServer(server) {
|
|
124
|
+
server.middlewares.use(middleware);
|
|
125
|
+
},
|
|
126
|
+
configurePreviewServer(server) {
|
|
127
|
+
server.middlewares.use(middleware);
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
package/scripts/pre-publish.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
ApiGetResult,
|
|
10
10
|
} from "@supersoniks/concorde/decorators";
|
|
11
11
|
|
|
12
|
-
/** Same API as the queue demo:
|
|
12
|
+
/** Same API as the queue demo: /docs-mock-api/geo/ */
|
|
13
13
|
type City = { nom: string; code: string };
|
|
14
14
|
|
|
15
15
|
const endpoint = new Endpoint<City[], { limit: number }>(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Fetch
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
> **New apps:** prefer [@get](#docs/_decorators/get.md/get) for a typed GET on a component, or [List](#core/components/functional/list/list.md/list) / [Queue](#core/components/functional/queue/queue.md/queue) with `fetch` for collections. Use [Local API demos](#docs/_misc/docs-mock-api.md/docs-mock-api) (`serviceURL="/docs-mock-api"`) to try examples offline.
|
|
4
|
+
|
|
5
|
+
The **sonic-fetch** component requests and stores API data. It extends the Fetcher and [Subscriber](#docs/_core-concept/subscriber.md/subscriber) mixins.
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
|
|
@@ -9,7 +11,7 @@ Fetch extends the mixins Fetcher and [Subscriber](#docs/_core-concept/subscriber
|
|
|
9
11
|
## Basic usage
|
|
10
12
|
In order to work properly the <b>sonic-fetch</b> component needs at least the following attributes.
|
|
11
13
|
- **serviceURL** : A base service url. This attribute can be inherited from an ancestor.
|
|
12
|
-
*ex :
|
|
14
|
+
*ex : /docs-mock-api*
|
|
13
15
|
- **endPoint** : the specific location where requests for information are sent (see the api docs).
|
|
14
16
|
*ex : api/users | api/users?page=2 | api/users/2*
|
|
15
17
|
- **dataProvider *(Required)*** : An ID that is used as a reference to the object storing the data returned by the API.
|
|
@@ -19,7 +21,7 @@ This attribute can be inherited from an ancestor.
|
|
|
19
21
|
|
|
20
22
|
<sonic-code>
|
|
21
23
|
<template>
|
|
22
|
-
<sonic-fetch serviceURL="
|
|
24
|
+
<sonic-fetch serviceURL="/docs-mock-api" endPoint="api/users?page=2" dataProvider="myDataObj"></sonic-fetch>
|
|
23
25
|
<sonic-button dataProvider="myDataObj" debug>Hover to see the data</sonic-button>
|
|
24
26
|
</template>
|
|
25
27
|
</sonic-code>
|
|
@@ -29,7 +31,7 @@ If no **endPoint** is specified it will be filled by the **dataProvider ID** ins
|
|
|
29
31
|
|
|
30
32
|
<sonic-code>
|
|
31
33
|
<template>
|
|
32
|
-
<sonic-fetch serviceURL="
|
|
34
|
+
<sonic-fetch serviceURL="/docs-mock-api" dataProvider="api/users?page=2" ></sonic-fetch>
|
|
33
35
|
<sonic-button dataProvider="api/users?page=2" debug>Hover to see the data</sonic-button>
|
|
34
36
|
</template>
|
|
35
37
|
</sonic-code>
|
|
@@ -46,19 +48,19 @@ For example if the data is `{my:{data:{a:1,b:2}}}` and the key is `key="my.data"
|
|
|
46
48
|
|
|
47
49
|
<sonic-code>
|
|
48
50
|
<template>
|
|
49
|
-
<sonic-fetch serviceURL="
|
|
51
|
+
<sonic-fetch serviceURL="/docs-mock-api" dataProvider="api/users/2" ></sonic-fetch>
|
|
50
52
|
<sonic-button dataProvider="api/users/2" debug>dataProvider object</sonic-button>
|
|
51
53
|
<!-- Get the user ID -->
|
|
52
|
-
<sonic-fetch serviceURL="
|
|
54
|
+
<sonic-fetch serviceURL="/docs-mock-api" dataProvider="id" endPoint="api/users/2" key="data.id"></sonic-fetch>
|
|
53
55
|
<sonic-button dataProvider="id" debug>data.id</sonic-button>
|
|
54
56
|
<!-- Get the user First name -->
|
|
55
|
-
<sonic-fetch serviceURL="
|
|
57
|
+
<sonic-fetch serviceURL="/docs-mock-api" dataProvider="first_name" endPoint="api/users/2" key="data.first_name"></sonic-fetch>
|
|
56
58
|
<sonic-button dataProvider="first_name" debug>data.first_name</sonic-button>
|
|
57
59
|
<!-- Get the user Last name -->
|
|
58
|
-
<sonic-fetch serviceURL="
|
|
60
|
+
<sonic-fetch serviceURL="/docs-mock-api" dataProvider="last_name" endPoint="api/users/2" key="data.last_name"></sonic-fetch>
|
|
59
61
|
<sonic-button dataProvider="last_name" debug>data.last_name</sonic-button>
|
|
60
62
|
<!-- Get the user email -->
|
|
61
|
-
<sonic-fetch serviceURL="
|
|
63
|
+
<sonic-fetch serviceURL="/docs-mock-api" dataProvider="email" endPoint="api/users/2" key="data.email"></sonic-fetch>
|
|
62
64
|
<sonic-button dataProvider="email" debug>data.email</sonic-button>
|
|
63
65
|
</template>
|
|
64
66
|
</sonic-code>
|
|
@@ -80,7 +82,7 @@ The noLoader attribute disables display of the default loader
|
|
|
80
82
|
|
|
81
83
|
<sonic-code>
|
|
82
84
|
<template>
|
|
83
|
-
<sonic-fetch noLoader serviceURL="
|
|
85
|
+
<sonic-fetch noLoader serviceURL="/docs-mock-api" endPoint="api/users?page=2" dataProvider="myDataObj"></sonic-fetch>
|
|
84
86
|
<sonic-button dataProvider="myDataObj" debug>Basic fetch with noLoader attribute</sonic-button>
|
|
85
87
|
</template>
|
|
86
88
|
</sonic-code>
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
# if
|
|
2
2
|
|
|
3
|
-
The **sonic-if** component shows its
|
|
3
|
+
The **sonic-if** component shows its slot when **`.condition`** is true. In Lit, bind `.condition` from store-driven state (`@subscribe` on a `formDataProvider` field) — live preview and **TypeScript source are the same file** (`docs-lit-demo`):
|
|
4
4
|
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<sonic-checkbox label="Show evacuation plan" name="togglePlan" unique checked value="true"></sonic-checkbox>
|
|
9
|
-
<sonic-if data-bind ::condition="$togglePlan" class="mt-4 block">
|
|
10
|
-
<sonic-image src="https://www.thebaron.info/assets/mail/concorde-evacuation.jpg" rounded="md" ratio="654/463"></sonic-image>
|
|
11
|
-
</sonic-if>
|
|
12
|
-
</div>
|
|
13
|
-
</template>
|
|
14
|
-
</sonic-code>
|
|
5
|
+
<docs-lit-demo for="docs-toggle-if-demo"></docs-lit-demo>
|
|
6
|
+
|
|
7
|
+
Plain HTML without Lit: [HTML integration](#docs/_misc/html-integration.md/html-integration).
|
|
15
8
|
|
|
16
9
|
|
|
@@ -43,13 +43,13 @@ export class ListDemo extends LitElement {
|
|
|
43
43
|
<h2 class="text-xl font-medium mb-4">Liste des utilisateurs</h2>
|
|
44
44
|
<sonic-list
|
|
45
45
|
fetch
|
|
46
|
-
serviceurl="
|
|
46
|
+
serviceurl="/docs-mock-api"
|
|
47
47
|
dataprovider="api/users"
|
|
48
48
|
key="data"
|
|
49
49
|
class="grid grid-cols-1 gap-4"
|
|
50
|
-
items=${this.items}
|
|
51
|
-
noItems=${this.noItems}
|
|
52
|
-
skeleton=${this.skeleton}
|
|
50
|
+
.items=${this.items}
|
|
51
|
+
.noItems=${this.noItems}
|
|
52
|
+
.skeleton=${this.skeleton}
|
|
53
53
|
debug
|
|
54
54
|
></sonic-list>
|
|
55
55
|
</div>
|