@intentius/chant-lexicon-fly 0.16.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 +91 -0
- package/dist/codegen/docs-cli.d.ts +3 -0
- package/dist/codegen/docs-cli.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +27 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/generate-cli.d.ts +6 -0
- package/dist/codegen/generate-cli.d.ts.map +1 -0
- package/dist/codegen/generate-lexicon.d.ts +18 -0
- package/dist/codegen/generate-lexicon.d.ts.map +1 -0
- package/dist/codegen/generate-typescript.d.ts +11 -0
- package/dist/codegen/generate-typescript.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +15 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/naming.d.ts +14 -0
- package/dist/codegen/naming.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +17 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/composites/fly-deploy.d.ts +110 -0
- package/dist/composites/fly-deploy.d.ts.map +1 -0
- package/dist/describe-resources.d.ts +74 -0
- package/dist/describe-resources.d.ts.map +1 -0
- package/dist/detect.d.ts +22 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/export-resources.d.ts +25 -0
- package/dist/export-resources.d.ts.map +1 -0
- package/dist/generated/index.d.ts +42 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/runtime.d.ts +5 -0
- package/dist/generated/runtime.d.ts.map +1 -0
- package/dist/import/generator.d.ts +21 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/live-export.d.ts +42 -0
- package/dist/import/live-export.d.ts.map +1 -0
- package/dist/import/parser.d.ts +40 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/integrity.json +9 -0
- package/dist/lint/post-synth/fly-helpers.d.ts +15 -0
- package/dist/lint/post-synth/fly-helpers.d.ts.map +1 -0
- package/dist/lint/post-synth/fly010-machine-requires-image.d.ts +10 -0
- package/dist/lint/post-synth/fly010-machine-requires-image.d.ts.map +1 -0
- package/dist/lint/post-synth/fly011-mount-references-declared-volume.d.ts +12 -0
- package/dist/lint/post-synth/fly011-mount-references-declared-volume.d.ts.map +1 -0
- package/dist/lint/post-synth/index.d.ts +3 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -0
- package/dist/lint/rules/guest-sizing.d.ts +9 -0
- package/dist/lint/rules/guest-sizing.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +7 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/no-secret-literals.d.ts +13 -0
- package/dist/lint/rules/no-secret-literals.d.ts.map +1 -0
- package/dist/lint/rules/valid-region.d.ts +9 -0
- package/dist/lint/rules/valid-region.d.ts.map +1 -0
- package/dist/lsp/completions.d.ts +9 -0
- package/dist/lsp/completions.d.ts.map +1 -0
- package/dist/lsp/hover.d.ts +9 -0
- package/dist/lsp/hover.d.ts.map +1 -0
- package/dist/manifest.json +4 -0
- package/dist/meta.json +285 -0
- package/dist/op/activities/flaps.d.ts +29 -0
- package/dist/op/activities/flaps.d.ts.map +1 -0
- package/dist/op/activities/fly-apply.d.ts +382 -0
- package/dist/op/activities/fly-apply.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +12 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/ownership.d.ts +27 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/package-cli.d.ts +3 -0
- package/dist/package-cli.d.ts.map +1 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/pseudo.d.ts +23 -0
- package/dist/pseudo.d.ts.map +1 -0
- package/dist/serializer.d.ts +36 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/spec/fetch.d.ts +12 -0
- package/dist/spec/fetch.d.ts.map +1 -0
- package/dist/spec/parse.d.ts +58 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/types/index.d.ts +590 -0
- package/dist/validate-cli.d.ts +3 -0
- package/dist/validate-cli.d.ts.map +1 -0
- package/dist/validate.d.ts +15 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +76 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +208 -0
- package/src/codegen/generate-cli.ts +36 -0
- package/src/codegen/generate-lexicon.ts +52 -0
- package/src/codegen/generate-typescript.ts +69 -0
- package/src/codegen/generate.ts +106 -0
- package/src/codegen/naming.ts +45 -0
- package/src/codegen/package.ts +25 -0
- package/src/composites/fly-deploy.ts +169 -0
- package/src/describe-resources.test.ts +188 -0
- package/src/describe-resources.ts +0 -0
- package/src/detect.ts +64 -0
- package/src/export-resources.ts +123 -0
- package/src/generated/.gitkeep +0 -0
- package/src/generated/index.d.ts +590 -0
- package/src/generated/index.ts +47 -0
- package/src/generated/lexicon-fly.json +285 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.test.ts +123 -0
- package/src/import/generator.ts +242 -0
- package/src/import/live-export.test.ts +150 -0
- package/src/import/live-export.ts +107 -0
- package/src/import/parser.test.ts +132 -0
- package/src/import/parser.ts +185 -0
- package/src/import/roundtrip.test.ts +77 -0
- package/src/index.ts +23 -0
- package/src/lint/post-synth/fly-helpers.ts +25 -0
- package/src/lint/post-synth/fly010-machine-requires-image.test.ts +59 -0
- package/src/lint/post-synth/fly010-machine-requires-image.ts +39 -0
- package/src/lint/post-synth/fly011-mount-references-declared-volume.test.ts +92 -0
- package/src/lint/post-synth/fly011-mount-references-declared-volume.ts +61 -0
- package/src/lint/post-synth/index.ts +9 -0
- package/src/lint/rules/guest-sizing.ts +100 -0
- package/src/lint/rules/index.ts +15 -0
- package/src/lint/rules/no-secret-literals.ts +67 -0
- package/src/lint/rules/rules.test.ts +127 -0
- package/src/lint/rules/valid-region.ts +58 -0
- package/src/lsp/completions.test.ts +10 -0
- package/src/lsp/completions.ts +14 -0
- package/src/lsp/hover.test.ts +10 -0
- package/src/lsp/hover.ts +14 -0
- package/src/op/activities/describe-resources.integration.test.ts +105 -0
- package/src/op/activities/flaps.test.ts +29 -0
- package/src/op/activities/flaps.ts +44 -0
- package/src/op/activities/fly-apply.integration.test.ts +245 -0
- package/src/op/activities/fly-apply.test.ts +513 -0
- package/src/op/activities/fly-apply.ts +1014 -0
- package/src/op/activities/index.ts +48 -0
- package/src/ownership.ts +32 -0
- package/src/package-cli.ts +24 -0
- package/src/plugin.test.ts +17 -0
- package/src/plugin.ts +285 -0
- package/src/pseudo.test.ts +32 -0
- package/src/pseudo.ts +29 -0
- package/src/serializer.test.ts +232 -0
- package/src/serializer.ts +293 -0
- package/src/skills/chant-fly-ops.md +55 -0
- package/src/skills/chant-fly-patterns.md +71 -0
- package/src/skills/chant-fly.md +123 -0
- package/src/spec/fetch.ts +34 -0
- package/src/spec/parse.ts +323 -0
- package/src/validate-cli.ts +5 -0
- package/src/validate.ts +28 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fly Op activities — resolved by the core activity registry when a project's
|
|
3
|
+
* `chant.config.ts` lists the `fly` lexicon. Contributes the native flaps
|
|
4
|
+
* applier (`flyApply`), which speaks the Fly Machines REST API directly (no
|
|
5
|
+
* flyctl, no state file), and the mudflaps emulator lifecycle it is tested
|
|
6
|
+
* against.
|
|
7
|
+
*/
|
|
8
|
+
export {
|
|
9
|
+
flyApply,
|
|
10
|
+
flyDelete,
|
|
11
|
+
applyApp,
|
|
12
|
+
applyMachine,
|
|
13
|
+
destroyMachine,
|
|
14
|
+
deleteApp,
|
|
15
|
+
listMachines,
|
|
16
|
+
pruneMachines,
|
|
17
|
+
waitForMachine,
|
|
18
|
+
acquireLease,
|
|
19
|
+
releaseLease,
|
|
20
|
+
withLease,
|
|
21
|
+
resolveEndpoint,
|
|
22
|
+
parsePlan,
|
|
23
|
+
isAppRequest,
|
|
24
|
+
isMachineRequest,
|
|
25
|
+
machineAppSegment,
|
|
26
|
+
resolveApp,
|
|
27
|
+
appNameFromRequest,
|
|
28
|
+
isChantOwned,
|
|
29
|
+
configEqual,
|
|
30
|
+
isLeaseConflict,
|
|
31
|
+
defaultFlyHttp,
|
|
32
|
+
DEFAULT_FLAPS_BASE_URL,
|
|
33
|
+
LEASE_NONCE_HEADER,
|
|
34
|
+
} from "./fly-apply";
|
|
35
|
+
export type { FlyApplyArgs, FlyPlan, FlapsRequest, FlapsMachine, FlyHttp, WaitOpts, ApplyCtx } from "./fly-apply";
|
|
36
|
+
|
|
37
|
+
// mudflaps (Fly Machines API emulator) lifecycle — boots/tears down the local
|
|
38
|
+
// flaps target flyApply is exercised against.
|
|
39
|
+
export {
|
|
40
|
+
flapsUp,
|
|
41
|
+
flapsDown,
|
|
42
|
+
flapsRunCommand,
|
|
43
|
+
flapsRmCommand,
|
|
44
|
+
flapsExistsCommand,
|
|
45
|
+
flapsHealthUrl,
|
|
46
|
+
flapsEndpoint,
|
|
47
|
+
} from "./flaps";
|
|
48
|
+
export type { FlapsUpArgs, FlapsDownArgs } from "./flaps";
|
package/src/ownership.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fly lexicon's ownership convention (#743, decision D2), declared here so
|
|
3
|
+
* core carries no fly-specific branch — the #686 seam: each lexicon owns its key
|
|
4
|
+
* names; core keeps only the generic stamp/detect logic
|
|
5
|
+
* (@intentius/chant/ownership), the same way aws declares `AWS_TAG_OWNERSHIP_KEYS`
|
|
6
|
+
* and azure `AZURE_TAG_OWNERSHIP_KEYS`.
|
|
7
|
+
*
|
|
8
|
+
* The convention is two-tier, because flaps resources differ in what they can
|
|
9
|
+
* carry:
|
|
10
|
+
*
|
|
11
|
+
* 1. Primary marker — machine `config.metadata`. Machines carry a flat
|
|
12
|
+
* `Record<string, string>`, so chant stamps `managed-by: chant` (+ stack/env)
|
|
13
|
+
* there. This is the k8s-label / AWS-tag analogue and the basis for
|
|
14
|
+
* owned-only machine prune (a metadata filter — a foreign machine survives).
|
|
15
|
+
* The serializer stamps it (see serializer.ts); the applier reads it via
|
|
16
|
+
* `isChantOwned` → core `hasOwnershipMarker(..., FLY_METADATA_OWNERSHIP_KEYS)`.
|
|
17
|
+
*
|
|
18
|
+
* 2. Secondary boundary — the app. Volumes, secrets, certs, and IPs carry no
|
|
19
|
+
* arbitrary metadata, so for them the Fly app is the ownership boundary
|
|
20
|
+
* (like a CloudFormation stack): chant owns the app's declared-type
|
|
21
|
+
* resources and prunes owned orphans within app scope. See the app-scoped
|
|
22
|
+
* prune block in op/activities/fly-apply.ts for the limitation this leaves.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { ChannelKeys } from "@intentius/chant/ownership";
|
|
26
|
+
|
|
27
|
+
/** Machine `config.metadata` keys for chant's ownership markers (tier 1, D2). */
|
|
28
|
+
export const FLY_METADATA_OWNERSHIP_KEYS: ChannelKeys = {
|
|
29
|
+
managedBy: "managed-by",
|
|
30
|
+
stack: "chant-stack",
|
|
31
|
+
env: "chant-env",
|
|
32
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Thin entry point for `npm run bundle` in lexicon-fly.
|
|
4
|
+
* Generates src/generated/ files and writes dist/ bundle.
|
|
5
|
+
*
|
|
6
|
+
* NOTE: Uses top-level await (matching AWS/Azure pattern) to avoid
|
|
7
|
+
* event loop references from async wrappers keeping the process alive.
|
|
8
|
+
*/
|
|
9
|
+
import { packageLexicon } from "./codegen/package";
|
|
10
|
+
import { writeBundleSpec } from "@intentius/chant/codegen/package";
|
|
11
|
+
import { dirname, join } from "path";
|
|
12
|
+
import { fileURLToPath } from "url";
|
|
13
|
+
|
|
14
|
+
const pkgDir = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const distDir = join(pkgDir, "..", "dist");
|
|
16
|
+
|
|
17
|
+
const verbose = process.argv.includes("--verbose") || !process.argv.includes("--quiet");
|
|
18
|
+
const force = process.argv.includes("--force");
|
|
19
|
+
|
|
20
|
+
const { spec, stats } = await packageLexicon({ verbose, force });
|
|
21
|
+
writeBundleSpec(spec, distDir);
|
|
22
|
+
|
|
23
|
+
console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules`);
|
|
24
|
+
console.error(`dist/ written to ${distDir}`);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { flyPlugin } from "./plugin";
|
|
3
|
+
import { isLexiconPlugin } from "@intentius/chant/lexicon";
|
|
4
|
+
|
|
5
|
+
describe("fly plugin", () => {
|
|
6
|
+
it("is a valid LexiconPlugin", () => {
|
|
7
|
+
expect(isLexiconPlugin(flyPlugin)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("has the correct name", () => {
|
|
11
|
+
expect(flyPlugin.name).toBe("fly");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("has a serializer", () => {
|
|
15
|
+
expect(flyPlugin.serializer).toBeDefined();
|
|
16
|
+
});
|
|
17
|
+
});
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import type { LexiconPlugin, SkillDefinition, IntrinsicDef, InitTemplateSet } from "@intentius/chant/lexicon";
|
|
2
|
+
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
3
|
+
import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";
|
|
4
|
+
import type { CompletionContext, CompletionItem, HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
|
|
5
|
+
import type { McpToolContribution, McpResourceContribution } from "@intentius/chant/mcp/types";
|
|
6
|
+
import { createSkillsLoader } from "@intentius/chant/lexicon-plugin-helpers";
|
|
7
|
+
import type { TemplateParser } from "@intentius/chant/import/parser";
|
|
8
|
+
import type { TypeScriptGenerator } from "@intentius/chant/import/generator";
|
|
9
|
+
import { flySerializer } from "./serializer";
|
|
10
|
+
import { rules } from "./lint/rules";
|
|
11
|
+
import { postSynthChecks } from "./lint/post-synth";
|
|
12
|
+
import { completions } from "./lsp/completions";
|
|
13
|
+
import { hover } from "./lsp/hover";
|
|
14
|
+
import { detectTemplate as detectFlyTemplate } from "./detect";
|
|
15
|
+
import { FlyParser } from "./import/parser";
|
|
16
|
+
import { FlyGenerator } from "./import/generator";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* fly lexicon plugin.
|
|
20
|
+
*
|
|
21
|
+
* Implements all required LexiconPlugin lifecycle methods.
|
|
22
|
+
*/
|
|
23
|
+
export const flyPlugin: LexiconPlugin = {
|
|
24
|
+
name: "fly",
|
|
25
|
+
serializer: flySerializer,
|
|
26
|
+
|
|
27
|
+
// ── Required lifecycle methods ────────────────────────────────
|
|
28
|
+
|
|
29
|
+
async generate(options?: { verbose?: boolean }): Promise<void> {
|
|
30
|
+
const { generate } = await import("./codegen/generate");
|
|
31
|
+
await generate(options);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async validate(options?: { verbose?: boolean }): Promise<void> {
|
|
35
|
+
const { validate } = await import("./validate");
|
|
36
|
+
const { printValidationResult } = await import("@intentius/chant/codegen/validate");
|
|
37
|
+
const result = await validate();
|
|
38
|
+
printValidationResult(result);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async coverage(options?: { verbose?: boolean; minOverall?: number }): Promise<void> {
|
|
42
|
+
// TODO: Implement coverage analysis
|
|
43
|
+
console.error("Coverage analysis not yet implemented");
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async package(options?: { verbose?: boolean; force?: boolean }): Promise<void> {
|
|
47
|
+
const { packageLexicon } = await import("./codegen/package");
|
|
48
|
+
const { writeBundleSpec } = await import("@intentius/chant/codegen/package");
|
|
49
|
+
const { join, dirname } = await import("path");
|
|
50
|
+
const { fileURLToPath } = await import("url");
|
|
51
|
+
|
|
52
|
+
const { spec, stats } = await packageLexicon(options);
|
|
53
|
+
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
54
|
+
writeBundleSpec(spec, join(pkgDir, "dist"));
|
|
55
|
+
|
|
56
|
+
console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// ── Optional extensions ────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
lintRules() {
|
|
62
|
+
return rules;
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
postSynthChecks() {
|
|
66
|
+
return postSynthChecks;
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
skills: createSkillsLoader(import.meta.url, [
|
|
70
|
+
{
|
|
71
|
+
file: "chant-fly.md",
|
|
72
|
+
name: "chant-fly",
|
|
73
|
+
description: "Author, lint, and deploy Fly apps and machines from a chant project, applied straight to the Machines API",
|
|
74
|
+
triggers: [
|
|
75
|
+
{ type: "file-pattern" as const, value: "*.fly.ts" },
|
|
76
|
+
{ type: "context" as const, value: "fly" },
|
|
77
|
+
{ type: "context" as const, value: "fly.io" },
|
|
78
|
+
],
|
|
79
|
+
parameters: [
|
|
80
|
+
{
|
|
81
|
+
name: "resourceType",
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "Fly resource type to work with (App, Machine, Volume, IPAddress, Certificate, Secret)",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
examples: [
|
|
87
|
+
{
|
|
88
|
+
title: "Author an App and a Machine",
|
|
89
|
+
output: "new App({ name: \"my-app\" });\nnew Machine({ region: \"iad\", config: new MachineConfig({ image: \"flyio/hellofly:latest\" }) })",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: "Deploy against mudflaps offline",
|
|
93
|
+
input: "Apply the App + Machine without a Fly account",
|
|
94
|
+
output: "cd examples/local-fly && chant run fly",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
file: "chant-fly-patterns.md",
|
|
100
|
+
name: "chant-fly-patterns",
|
|
101
|
+
description: "Volumes and mounts, IP assignments, certificates, apply-only secrets, and the app-boundary ownership model for Fly",
|
|
102
|
+
triggers: [
|
|
103
|
+
{ type: "context" as const, value: "fly volumes" },
|
|
104
|
+
{ type: "context" as const, value: "fly secrets" },
|
|
105
|
+
{ type: "context" as const, value: "fly certificates" },
|
|
106
|
+
],
|
|
107
|
+
parameters: [],
|
|
108
|
+
examples: [
|
|
109
|
+
{
|
|
110
|
+
title: "Machine with a mounted volume",
|
|
111
|
+
input: "Attach a volume to a machine",
|
|
112
|
+
output: "new Volume({ name: \"data\", region: \"iad\", size_gb: 10 });\nnew Machine({ config: new MachineConfig({ mounts: [{ volume: \"data\", path: \"/data\" }] }) })",
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
file: "chant-fly-ops.md",
|
|
118
|
+
name: "chant-fly-ops",
|
|
119
|
+
description: "Operate a live Fly deploy — wait on stuck machines, resolve lease conflicts, prune safely, and target a real org versus the emulator",
|
|
120
|
+
triggers: [
|
|
121
|
+
{ type: "context" as const, value: "fly apply" },
|
|
122
|
+
{ type: "context" as const, value: "fly prune" },
|
|
123
|
+
{ type: "context" as const, value: "flaps lease" },
|
|
124
|
+
],
|
|
125
|
+
parameters: [],
|
|
126
|
+
examples: [
|
|
127
|
+
{
|
|
128
|
+
title: "Deploy to a real Fly org",
|
|
129
|
+
input: "Point the same code at a real org",
|
|
130
|
+
output: "export FLY_API_TOKEN=... && chant run fly",
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
]),
|
|
135
|
+
|
|
136
|
+
mcpTools() {
|
|
137
|
+
return []; // TODO: Implement MCP tools
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
mcpResources() {
|
|
141
|
+
return []; // TODO: Implement MCP resources
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
detectTemplate(data: unknown) {
|
|
145
|
+
return detectFlyTemplate(data);
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
templateParser(): TemplateParser {
|
|
149
|
+
return new FlyParser();
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
templateGenerator(): TypeScriptGenerator {
|
|
153
|
+
return new FlyGenerator();
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
pseudoParameters(): string[] {
|
|
157
|
+
return [
|
|
158
|
+
"Fly::Region",
|
|
159
|
+
"Fly::OrgSlug",
|
|
160
|
+
"Fly::AppName",
|
|
161
|
+
];
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
initTemplates(template?: string): InitTemplateSet {
|
|
165
|
+
if (template === "volume") {
|
|
166
|
+
return {
|
|
167
|
+
src: {
|
|
168
|
+
"infra.ts": `import { App, Machine, MachineConfig, MachineGuest, MachineMount, Volume, Fly } from "@intentius/chant-lexicon-fly";
|
|
169
|
+
|
|
170
|
+
// An app, a persistent volume, and a machine that mounts it. The serializer
|
|
171
|
+
// (#738) turns these into the flaps create bodies flyApply POSTs. The region
|
|
172
|
+
// comes from Fly.Region (resolved from FLY_REGION, defaulting to "iad").
|
|
173
|
+
const app = new App({ name: "my-app", org_slug: Fly.OrgSlug });
|
|
174
|
+
|
|
175
|
+
const data = new Volume({
|
|
176
|
+
name: "data",
|
|
177
|
+
region: Fly.Region,
|
|
178
|
+
size_gb: 10,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const web = new Machine({
|
|
182
|
+
name: "web",
|
|
183
|
+
region: Fly.Region,
|
|
184
|
+
config: new MachineConfig({
|
|
185
|
+
image: "flyio/hellofly:latest",
|
|
186
|
+
guest: new MachineGuest({ cpu_kind: "shared", cpus: 1, memory_mb: 256 }),
|
|
187
|
+
mounts: [new MachineMount({ volume: "data", path: "/data" })],
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
export { app, data, web };
|
|
192
|
+
`,
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (template === "service") {
|
|
198
|
+
return {
|
|
199
|
+
src: {
|
|
200
|
+
"infra.ts": `import { App, Machine, MachineConfig, MachineGuest, MachineService, MachinePort, Fly } from "@intentius/chant-lexicon-fly";
|
|
201
|
+
|
|
202
|
+
// An app and a machine exposing a public HTTPS service on port 443 to internal
|
|
203
|
+
// 8080. The region comes from Fly.Region (resolved from FLY_REGION, defaulting
|
|
204
|
+
// to "iad").
|
|
205
|
+
const app = new App({ name: "my-app", org_slug: Fly.OrgSlug });
|
|
206
|
+
|
|
207
|
+
const web = new Machine({
|
|
208
|
+
name: "web",
|
|
209
|
+
region: Fly.Region,
|
|
210
|
+
config: new MachineConfig({
|
|
211
|
+
image: "flyio/hellofly:latest",
|
|
212
|
+
guest: new MachineGuest({ cpu_kind: "shared", cpus: 1, memory_mb: 256 }),
|
|
213
|
+
services: [
|
|
214
|
+
new MachineService({
|
|
215
|
+
protocol: "tcp",
|
|
216
|
+
internal_port: 8080,
|
|
217
|
+
ports: [new MachinePort({ port: 443, handlers: ["tls", "http"] })],
|
|
218
|
+
}),
|
|
219
|
+
],
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
export { app, web };
|
|
224
|
+
`,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Default: one app and one machine — the smallest complete deploy.
|
|
230
|
+
return {
|
|
231
|
+
src: {
|
|
232
|
+
"infra.ts": `import { App, Machine, MachineConfig, MachineGuest, Fly } from "@intentius/chant-lexicon-fly";
|
|
233
|
+
|
|
234
|
+
// One Fly app and one machine — the smallest complete deploy. The serializer
|
|
235
|
+
// (#738) turns these into the flaps create bodies flyApply POSTs. The region
|
|
236
|
+
// comes from Fly.Region (resolved from FLY_REGION, defaulting to "iad").
|
|
237
|
+
const app = new App({ name: "my-app", org_slug: Fly.OrgSlug });
|
|
238
|
+
|
|
239
|
+
const web = new Machine({
|
|
240
|
+
name: "web",
|
|
241
|
+
region: Fly.Region,
|
|
242
|
+
config: new MachineConfig({
|
|
243
|
+
image: "flyio/hellofly:latest",
|
|
244
|
+
guest: new MachineGuest({ cpu_kind: "shared", cpus: 1, memory_mb: 256 }),
|
|
245
|
+
}),
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
export { app, web };
|
|
249
|
+
`,
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
completionProvider(ctx: CompletionContext) {
|
|
255
|
+
return completions(ctx);
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
hoverProvider(ctx: HoverContext) {
|
|
259
|
+
return hover(ctx);
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
async docs(options?) {
|
|
263
|
+
const { generateDocs } = await import("./codegen/docs");
|
|
264
|
+
return generateDocs(options);
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
// State: the read-back seam for plan + drift (#767). Lists live Fly resources
|
|
268
|
+
// over flaps, keyed by chant entity name, with an ownership verdict core's
|
|
269
|
+
// change set reads. Endpoint + auth reuse the applier (FLY_FLAPS_BASE_URL /
|
|
270
|
+
// FLY_API_TOKEN), so it reads the same target flyApply writes.
|
|
271
|
+
async describeResources(options) {
|
|
272
|
+
const { describeResources } = await import("./describe-resources");
|
|
273
|
+
return describeResources(options);
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
// Live export (cloud → code): read live flaps state and regenerate App/Machine
|
|
277
|
+
// TypeScript. Strips server-written fields to the authored shape; honors the
|
|
278
|
+
// `owned` filter (machine marker; app-boundary inference for the marker-less
|
|
279
|
+
// app, logged per the seam contract). Reuses the applier's endpoint/auth and
|
|
280
|
+
// listMachines so it reads the same target flyApply writes.
|
|
281
|
+
async exportResources(options) {
|
|
282
|
+
const { exportResources } = await import("./export-resources");
|
|
283
|
+
return exportResources(options);
|
|
284
|
+
},
|
|
285
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Fly, Region, OrgSlug, AppName, PseudoParameter } from "./pseudo";
|
|
3
|
+
|
|
4
|
+
describe("fly pseudo-parameters", () => {
|
|
5
|
+
it("Region toJSON returns Ref object", () => {
|
|
6
|
+
expect(Region.toJSON()).toEqual({ Ref: "Fly::Region" });
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("OrgSlug toJSON returns Ref object", () => {
|
|
10
|
+
expect(OrgSlug.toJSON()).toEqual({ Ref: "Fly::OrgSlug" });
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("AppName toJSON returns Ref object", () => {
|
|
14
|
+
expect(AppName.toJSON()).toEqual({ Ref: "Fly::AppName" });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("Region toString returns interpolation syntax", () => {
|
|
18
|
+
expect(Region.toString()).toBe("${Fly::Region}");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("all pseudo-parameters are instances of PseudoParameter", () => {
|
|
22
|
+
expect(Region).toBeInstanceOf(PseudoParameter);
|
|
23
|
+
expect(OrgSlug).toBeInstanceOf(PseudoParameter);
|
|
24
|
+
expect(AppName).toBeInstanceOf(PseudoParameter);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("Fly namespace contains all pseudo-parameters", () => {
|
|
28
|
+
expect(Fly.Region).toBe(Region);
|
|
29
|
+
expect(Fly.OrgSlug).toBe(OrgSlug);
|
|
30
|
+
expect(Fly.AppName).toBe(AppName);
|
|
31
|
+
});
|
|
32
|
+
});
|
package/src/pseudo.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PseudoParameter, createPseudoParameters } from "@intentius/chant/pseudo-parameter";
|
|
2
|
+
|
|
3
|
+
export { PseudoParameter };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fly pseudo-parameters — environment-resolved values a project references
|
|
7
|
+
* without hard-coding. Each serializes to a `{ Ref: "Fly::..." }` marker that
|
|
8
|
+
* the serializer resolves from an environment variable at build time (see
|
|
9
|
+
* PSEUDO_ENV_MAP in ./serializer.ts), mirroring gcp's `GCP.Region` /
|
|
10
|
+
* `GCP.ProjectId`.
|
|
11
|
+
*
|
|
12
|
+
* - Region → the target Fly region (FLY_REGION), e.g. `iad`.
|
|
13
|
+
* - OrgSlug → the owning org slug (FLY_ORG / FLY_ORG_SLUG).
|
|
14
|
+
* - AppName → the target app name (FLY_APP_NAME).
|
|
15
|
+
*/
|
|
16
|
+
export const { Region, OrgSlug, AppName } = createPseudoParameters({
|
|
17
|
+
Region: "Fly::Region",
|
|
18
|
+
OrgSlug: "Fly::OrgSlug",
|
|
19
|
+
AppName: "Fly::AppName",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fly namespace containing all pseudo-parameters.
|
|
24
|
+
*/
|
|
25
|
+
export const Fly = {
|
|
26
|
+
Region,
|
|
27
|
+
OrgSlug,
|
|
28
|
+
AppName,
|
|
29
|
+
} as const;
|