@fias/create-fias-plugin 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -7,7 +7,7 @@ var path = require('path');
7
7
 
8
8
  var TEMPLATE_DIR = path.resolve(__dirname, 'templates/default');
9
9
 
10
- function copyDir(src, dest, replacements) {
10
+ function copyDir(src, dest, replacements, opts) {
11
11
  fs.mkdirSync(dest, { recursive: true });
12
12
 
13
13
  var entries = fs.readdirSync(src, { withFileTypes: true });
@@ -17,8 +17,17 @@ function copyDir(src, dest, replacements) {
17
17
  var destPath = path.join(dest, entry.name);
18
18
 
19
19
  if (entry.isDirectory()) {
20
- copyDir(srcPath, destPath, replacements);
20
+ copyDir(srcPath, destPath, replacements, opts);
21
21
  } else {
22
+ if (opts.inPlace && fs.existsSync(destPath)) {
23
+ console.error(
24
+ 'Error: refusing to overwrite existing file: ' + path.relative(opts.rootDest, destPath),
25
+ );
26
+ console.error(
27
+ 'In --in-place mode, the target directory must not already contain plugin files.',
28
+ );
29
+ process.exit(1);
30
+ }
22
31
  var content = fs.readFileSync(srcPath, 'utf-8');
23
32
  var keys = Object.keys(replacements);
24
33
  for (var k = 0; k < keys.length; k++) {
@@ -29,27 +38,59 @@ function copyDir(src, dest, replacements) {
29
38
  }
30
39
  }
31
40
 
32
- var projectName = process.argv[2];
33
-
34
- if (!projectName) {
41
+ function printUsage() {
35
42
  console.error('Usage: npm create @fias/fias-plugin <project-name>');
36
- process.exit(1);
43
+ console.error(' or: npm create @fias/fias-plugin -- --in-place (scaffold into current directory)');
44
+ console.error(' or: npm create @fias/fias-plugin . (alias for --in-place)');
45
+ }
46
+
47
+ var args = process.argv.slice(2);
48
+ var inPlace = false;
49
+ var positional = [];
50
+ for (var a = 0; a < args.length; a++) {
51
+ if (args[a] === '--in-place' || args[a] === '.') {
52
+ inPlace = true;
53
+ } else if (args[a].indexOf('--') === 0) {
54
+ console.error('Unknown option: ' + args[a]);
55
+ printUsage();
56
+ process.exit(1);
57
+ } else {
58
+ positional.push(args[a]);
59
+ }
37
60
  }
38
61
 
39
- var targetDir = path.resolve(process.cwd(), projectName);
62
+ var projectName;
63
+ var targetDir;
40
64
 
41
- if (fs.existsSync(targetDir)) {
42
- console.error('Error: Directory "' + projectName + '" already exists.');
43
- process.exit(1);
65
+ if (inPlace) {
66
+ targetDir = process.cwd();
67
+ projectName = positional[0] || path.basename(targetDir);
68
+ } else {
69
+ projectName = positional[0];
70
+ if (!projectName) {
71
+ printUsage();
72
+ process.exit(1);
73
+ }
74
+ targetDir = path.resolve(process.cwd(), projectName);
75
+ if (fs.existsSync(targetDir)) {
76
+ console.error('Error: Directory "' + projectName + '" already exists.');
77
+ process.exit(1);
78
+ }
44
79
  }
45
80
 
46
- console.log('Creating FIAS plugin project: ' + projectName);
81
+ console.log(
82
+ inPlace
83
+ ? 'Scaffolding FIAS plugin into current directory (' + projectName + ')'
84
+ : 'Creating FIAS plugin project: ' + projectName,
85
+ );
47
86
 
48
- copyDir(TEMPLATE_DIR, targetDir, { name: projectName });
87
+ copyDir(TEMPLATE_DIR, targetDir, { name: projectName }, { inPlace: inPlace, rootDest: targetDir });
49
88
 
50
- console.log('\nPlugin created at ./' + projectName + '\n');
89
+ console.log('\nPlugin scaffolded.\n');
51
90
  console.log('Next steps:');
52
- console.log(' cd ' + projectName);
91
+ if (!inPlace) {
92
+ console.log(' cd ' + projectName);
93
+ }
53
94
  console.log(' npm install');
54
95
  console.log(' npx fias-dev login # Authenticate with FIAS platform (opens browser)');
55
96
  console.log(' npm start # Start dev server + real AI harness (uses credits)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fias/create-fias-plugin",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -2,7 +2,7 @@
2
2
 
3
3
  This project is a FIAS platform plugin — a React application that runs in a sandboxed iframe within the FIAS marketplace. This file provides the context AI coding assistants need to build, test, and submit plugins effectively.
4
4
 
5
- For other AI tool instruction files, see `AGENTS.md` (identical content).
5
+ For other AI tool instruction files, see `CLAUDE.md` (identical content).
6
6
 
7
7
  ## Project Structure
8
8
 
@@ -268,6 +268,8 @@ const [count, setCount] = usePersistentState<number>('counter', 0);
268
268
  // Automatically persists to storage on change
269
269
  ```
270
270
 
271
+ **Writes are debounced (SDK ≥ 1.8.0).** The in-memory value updates synchronously, but the underlying `storage_write` is coalesced (250 ms trailing-edge debounce, 1 s max-wait, flushed on unmount). Safe to call from `requestAnimationFrame` and other high-frequency handlers. For state that updates every frame (game positions, drag coordinates), still prefer plain `useState` — persisting transient state is wasteful and not useful on reload.
272
+
271
273
  ### `fias` — Imperative utilities
272
274
 
273
275
  ```tsx
@@ -317,9 +319,15 @@ fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' |
317
319
 
318
320
  These are hard limits enforced by the platform. Code that violates these will fail review or be blocked at runtime.
319
321
 
322
+ ### Hitting a limit? Tell the user about Entity requests
323
+
324
+ Don't just say "can't do that" when the user asks for something blocked by these rules — most often a direct external API call, a third-party service, or a capability the platform hasn't surfaced yet. The Fias team adds new **Entities** on request: platform-managed wrappers that hold credentials, handle auth, and expose the capability to plugins through `useEntityInvocation()` (or `useImageGeneration()` for image models). The plugin never bypasses the sandbox — it invokes the new Entity like any other model.
325
+
326
+ When you hit a constraint, say so AND tell the user: file an Entity request on the Customer Support arche at `https://platform.fias.io/a/arche_customer_support`. Describe the service, endpoints, and auth method needed; once the Entity ships, the plugin can call it via `useEntityInvocation` with no sandbox change. Then propose a fallback that works inside the current sandbox (mock data, a simpler path, deferring the call) so the build keeps moving while the request is in flight.
327
+
320
328
  ### Sandboxing
321
329
 
322
- - Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin"`
330
+ - Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin allow-downloads"`
323
331
  - **No `fetch()` or `XMLHttpRequest`** — all network access is blocked
324
332
  - **No access** to parent DOM, cookies, or localStorage
325
333
  - **No external scripts or stylesheets** — everything must be bundled
@@ -338,6 +346,8 @@ These are hard limits enforced by the platform. Code that violates these will fa
338
346
  - `storage_read`: 300/minute
339
347
  - `storage_list`, `storage_delete`: 60/minute
340
348
 
349
+ A separate **runaway-loop detector** fires if any method is called >50 times in 5 s and blocks that method for 10 s. Errors include the target, e.g. `Runaway loop detected: "storage_write" (path: __state/gameState) …` — the path tells you where to debounce at the source.
350
+
341
351
  ### Security Rules (enforced during review)
342
352
 
343
353
  - No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
@@ -415,9 +425,17 @@ npx fias-dev validate
415
425
  ```bash
416
426
  npm run submit
417
427
  # Builds, validates, packages, uploads, submits for AI review
418
- # First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
419
428
  ```
420
429
 
430
+ The first time you list a plugin in the marketplace there is a one-time
431
+ listing fee; subsequent submissions of the same plugin only pay a small
432
+ per-review fee. The harness fetches the exact amount from the server and
433
+ shows it before you confirm — never assume a hardcoded price.
434
+
435
+ If the AI review rejects your submission, the listing-fee portion is
436
+ refunded automatically (the per-review portion is not, since review work
437
+ happened either way).
438
+
421
439
  ## Common Patterns
422
440
 
423
441
  ### Theme-Aware Card Component
@@ -268,6 +268,8 @@ const [count, setCount] = usePersistentState<number>('counter', 0);
268
268
  // Automatically persists to storage on change
269
269
  ```
270
270
 
271
+ **Writes are debounced (SDK ≥ 1.8.0).** The in-memory value updates synchronously, but the underlying `storage_write` is coalesced (250 ms trailing-edge debounce, 1 s max-wait, flushed on unmount). Safe to call from `requestAnimationFrame` and other high-frequency handlers. For state that updates every frame (game positions, drag coordinates), still prefer plain `useState` — persisting transient state is wasteful and not useful on reload.
272
+
271
273
  ### `fias` — Imperative utilities
272
274
 
273
275
  ```tsx
@@ -317,9 +319,15 @@ fias.showToast('Saved!', 'success'); // Toast: 'info' | 'success' | 'warning' |
317
319
 
318
320
  These are hard limits enforced by the platform. Code that violates these will fail review or be blocked at runtime.
319
321
 
322
+ ### Hitting a limit? Tell the user about Entity requests
323
+
324
+ Don't just say "can't do that" when the user asks for something blocked by these rules — most often a direct external API call, a third-party service, or a capability the platform hasn't surfaced yet. The Fias team adds new **Entities** on request: platform-managed wrappers that hold credentials, handle auth, and expose the capability to plugins through `useEntityInvocation()` (or `useImageGeneration()` for image models). The plugin never bypasses the sandbox — it invokes the new Entity like any other model.
325
+
326
+ When you hit a constraint, say so AND tell the user: file an Entity request on the Customer Support arche at `https://platform.fias.io/a/arche_customer_support`. Describe the service, endpoints, and auth method needed; once the Entity ships, the plugin can call it via `useEntityInvocation` with no sandbox change. Then propose a fallback that works inside the current sandbox (mock data, a simpler path, deferring the call) so the build keeps moving while the request is in flight.
327
+
320
328
  ### Sandboxing
321
329
 
322
- - Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin"`
330
+ - Plugins run in an iframe with `sandbox="allow-scripts allow-forms allow-same-origin allow-downloads"`
323
331
  - **No `fetch()` or `XMLHttpRequest`** — all network access is blocked
324
332
  - **No access** to parent DOM, cookies, or localStorage
325
333
  - **No external scripts or stylesheets** — everything must be bundled
@@ -338,6 +346,8 @@ These are hard limits enforced by the platform. Code that violates these will fa
338
346
  - `storage_read`: 300/minute
339
347
  - `storage_list`, `storage_delete`: 60/minute
340
348
 
349
+ A separate **runaway-loop detector** fires if any method is called >50 times in 5 s and blocks that method for 10 s. Errors include the target, e.g. `Runaway loop detected: "storage_write" (path: __state/gameState) …` — the path tells you where to debounce at the source.
350
+
341
351
  ### Security Rules (enforced during review)
342
352
 
343
353
  - No `eval()`, `Function()`, `innerHTML`, or dynamic code execution
@@ -415,9 +425,17 @@ npx fias-dev validate
415
425
  ```bash
416
426
  npm run submit
417
427
  # Builds, validates, packages, uploads, submits for AI review
418
- # First listing: 5000 credits ($50). Re-submissions: 100 credits ($1).
419
428
  ```
420
429
 
430
+ The first time you list a plugin in the marketplace there is a one-time
431
+ listing fee; subsequent submissions of the same plugin only pay a small
432
+ per-review fee. The harness fetches the exact amount from the server and
433
+ shows it before you confirm — never assume a hardcoded price.
434
+
435
+ If the AI review rejects your submission, the listing-fee portion is
436
+ refunded automatically (the per-review portion is not, since review work
437
+ happened either way).
438
+
421
439
  ## Common Patterns
422
440
 
423
441
  ### Theme-Aware Card Component