Skip to content

feat: Add remaining getting-started snippets.#398

Draft
kinyoklion wants to merge 28 commits intomainfrom
rlamb/port-remaining-getting-started
Draft

feat: Add remaining getting-started snippets.#398
kinyoklion wants to merge 28 commits intomainfrom
rlamb/port-remaining-getting-started

Conversation

@kinyoklion
Copy link
Copy Markdown
Member

No description provided.

…ing-started

Introduces sdk-meta/snippets/, a self-contained Go module that owns the
canonical source for LaunchDarkly SDK code samples and renders them
into downstream consumers. Slice scope: python-server-sdk "Getting
Started" flow, end-to-end.

What's here
-----------
- cmd/snippets — CLI with three subcommands:
    render   — rewrite the body of every JSX element marked with an
               SDK_SNIPPET:RENDER comment in a consumer checkout
    verify   — recompute hashes and fail if any managed region drifted
    validate — run each snippet inside a per-language Docker validator
- internal/model — snippet file format (YAML frontmatter + one fenced
  code block) loaded from sdks/<id>/snippets/.
- internal/render — minimal {{ var }} / {{ if var }}...{{ end }}
  templating engine with a runtime mode for validation and a JS
  template-literal mode for the ld-application adapter.
- internal/markers — host-syntax-aware scanner for SDK_SNIPPET:RENDER
  comments (`// …`, `{/* … */}`, `/* … */`); hashes the rendered region
  to catch hand-edits.
- internal/adapters/ldapplication — first adapter target. Discovers
  consumer files via each sdk.yaml's ld-application.get-started-file
  field and rewrites only the JSX children of marked <Snippet>
  elements, preserving surrounding whitespace.
- internal/validate — orchestrator that builds a Docker image per
  language and runs the snippet verbatim against a real LaunchDarkly
  environment. The SDK key and flag key come from the caller's
  LAUNCHDARKLY_SDK_KEY / LAUNCHDARKLY_FLAG_KEY env vars (the same
  convention the hello-* sample apps use), are forwarded into the
  container, and never end up in committed files.
- sdks/python-server-sdk — sdk descriptor plus four snippets sourced
  verbatim from the existing get-started flow:
    getting-started/mkdir
    getting-started/install
    getting-started/main-py
    getting-started/run
- validators/languages/python — Docker image plus run.sh that pip-
  installs the snippet's own requirements, runs the entrypoint, and
  matches the expected flag-evaluation line within a timeout.
- docs/AUTHORING.md — snippet authoring guide.

Naming and module path
----------------------
- Go module: github.com/launchdarkly/sdk-meta/snippets
- Adapter target: --target=ld-application (renders into the LD
  application UI). A future --target=ld-docs adapter is planned for
  the docs site; not implemented yet.

Out of scope (kept in design doc, not in this commit): all SDKs other
than python-server-sdk, the ld-docs MDX adapter, GitHub Actions,
signed-binary release pipeline, sdk-meta capability integration,
region/version conditional rendering.

Verified locally
----------------
- go build ./...           : clean
- go test ./...            : render + markers tests pass
- snippets render --target=ld-application --out=<app-checkout>: idempotent
- snippets verify --target=ld-application --out=<app-checkout>: ok
- snippets validate (with LAUNCHDARKLY_SDK_KEY + LAUNCHDARKLY_FLAG_KEY):
  matches "*** The <flag> feature flag evaluates to ..." against a
  real LD environment.
Findings 1-19 from the multi-agent review (artifacts/multi-review-sdk-meta-396.md).
All worth-fixing items are addressed in this commit; the four "could not
prove" hardening items are deferred (they remain open documentation in
the review file).

Security and correctness
------------------------
- #1, #5: Marker hash now covers the full <Tag …>…</Tag> element, not just
  the children, so attribute-only edits (e.g. lang="python" → lang="go")
  are detected by `verify`. The `hash=` field is required at verify time;
  a missing hash is an error rather than a skip.
- #2: validation.entrypoint must be a plain filename (filepath.Base equal,
  no path separators or ..). Blocks the "snippet writes to ~/.ssh" class
  of attack via author-controlled YAML.
- #3: ld-application.get-started-file is rejected if it's absolute or if
  filepath.Rel(appDir, full) starts with "..". Blocks the same class via
  the consumer-side path.
- #4: Template tokenizer was treating any token starting with "end"
  (`endTime`, `endIndex`, …) as `{{ end }}`. Switched HasPrefix → equality.
- #6: Marker scanner now tracks <Tag depth so a nested same-tag pair
  (`<Snippet><Snippet>…</Snippet></Snippet>`) doesn't silently truncate
  to the inner close. Same-prefix tags (`<SnippetGroup>`) don't count
  as opens of `<Snippet>`.
- #7: runtimeInputs has a `sdk-key` arm wired to LAUNCHDARKLY_SDK_KEY,
  with a defensive check that flag-key/sdk-key inputs cannot declare a
  runtime-default (those values must always come from the environment).
- #8: validation.requirements rejects newlines and lines starting with
  `-` so a snippet can't smuggle `--extra-index-url` through to pip.
- #9: Backtick-string scanner now tracks `${ … }` expression depth, so a
  nested template literal inside an interpolation expression doesn't
  prematurely end the outer string.
- #10: Render path split into RenderForLDApplicationTemplate (escaping
  for backtick literals) and RenderForJSXText (no escaping; for bare
  text). The bare-text path no longer corrupts backslashes / backticks
  in user-visible output.
- #11: Atomic write — temp file in the same directory, fsync, rename;
  source file mode is preserved.
- #12: Validator Docker tag is a content hash of the validator dir, so
  concurrent runs against the same validator share the cached image and
  runs against different validators cannot interleave.
- #13: run.sh redacts LAUNCHDARKLY_SDK_KEY from the log dump on failure.
- #14: Bare-vs-backticks decision is driven by the snippet's intent
  (interpolation / multiline / JSX-special chars), not by what's already
  in the file. Re-renders no longer stay sticky on the wrapped form.
- #17: snippet frontmatter and sdk.yaml are decoded with KnownFields(true)
  so a typo like `Entrpoint:` is a hard error.

Cosmetic / docs
---------------
- #15: AUTHORING.md notes the uppercase-first JSX-component-tag constraint.
- #16: hashLen const + comment documenting the 12-hex-char (~48 bit)
  budget is for accidental-drift detection only, not integrity.
- #19: go.mod uses `go 1.24` (drops the patch version).

Tests
-----
- New tests for: endFoo regression, unmatched/unclosed `{{ if/end }}`,
  unknown variable, empty template, RenderForJSXText backslash round-trip,
  HasInterpolation, ContainsJSXSpecial, scanner edge cases (no markers,
  block-style, unterminated comment, gap, self-closing, missing close,
  nested same-tag, similar-prefix tag, nested-backtick), full-element
  hash detects attribute edits, descriptor-traversal rejects, runtimeInputs
  rejects runtime-default on key types, requirements rejects pip flags,
  entrypoint rejects path components.

Verified end-to-end
-------------------
- go build ./... / go vet ./... / go test ./...     all clean
- snippets render --target=ld-application --out=<app>: rewrites the file
  with full-element hashes; second run reports "no changes".
- snippets verify ok on the freshly rendered file.
- snippets verify rejects: a one-byte attribute edit (lang="python" →
  lang="go") AND a marker with the hash= field stripped.
- snippets validate (with real LAUNCHDARKLY_SDK_KEY / LAUNCHDARKLY_FLAG_KEY)
  matches the expected flag-evaluation line.
Walk back the part of the prior review-feedback commit that hashed the
full <Tag …>…</Tag> region. The scope=content contract says the
consumer owns the element's attributes; locking them down forces a
re-render every time someone tweaks `withCopyButton` / `label="…"` /
`className` and offers nothing the design promised.

`verify` now:
- requires a hash= field on every marker (#5, unchanged)
- compares it against the SHA-256 of src[RegionStart:RegionEnd] (just
  the children, as originally documented)
- accepts attribute-only edits, rejects child edits

Tests updated: TestVerify_AcceptsAttributeEdit and
TestVerify_RejectsChildEdit pin the new contract; the dead
TestFullElementHash_DetectsAttributeEdit is removed; the now-unused
Match.FullElementHash method is dropped.

End-to-end re-checked: render is idempotent, verify ok, an attribute
edit passes verify, a child edit fails verify with a children-hash
error, and `snippets validate` against a real LD environment still
prints `*** The sample-feature feature flag evaluates to True`.
…taging

Today's validate.go hardcodes a Python branch and only handles snippets that
are a single self-contained file. The remaining gonfalon SDKs need
per-language harnesses, multi-file staging (java pom.xml, rust Cargo.toml,
ios Podfile, etc.), and at least one runtime (iOS) that can't run in a
Linux Docker container.

Changes:
- Each `validators/languages/<runtime>/` now carries a `runner.yaml`
  declaring `mode: docker | native`, `runs-on: <runner-hint>`, and
  `image-prefix:` for docker runtimes. Python's runner.yaml is included.
- `internal/validate/runner.go` (new) loads the runner descriptor with
  KnownFields(true) so a typo there is rejected at validate time.
- `internal/validate/validate.go` is restructured around:
    * `envInputs` carrying the four EXAM-HELLO env values
      (LAUNCHDARKLY_SDK_KEY / FLAG_KEY / MOBILE_KEY / CLIENT_SIDE_ID)
    * `stageSnippet` writing the entrypoint snippet AND every snippet
      named in `validation.companions:` to the staging dir at each
      snippet's `file:` path (with stage-path traversal guard)
    * `runDocker` and `runNative` paths selected by the runner descriptor
    * `requireEnvForInputs` failing fast when a snippet declares an
      input of a key type but the corresponding env var isn't set
- `internal/model/model.go` extends Validation with `Runtime`,
  `Companions`, plus a longer doc comment for the existing fields. The
  python.snippet.md still uses the back-compat `validation.entrypoint`
  fallback (no change to existing snippet content).
- `runtimeInputs` adds `mobile-key` and `client-side-id` arms (also
  rejecting runtime-default for those, matching sdk-key/flag-key).
- Tests updated for the new envInputs signature; new
  TestCheckStagePathRejectsTraversal replaces the old entrypoint-only
  guard test.

Verified: `go build ./... && go test ./...` clean; Python end-to-end
validate against the real LD env still prints
`*** The sample-feature feature flag evaluates to True` and exits 0.
Verifies the per-runtime dispatcher refactor end-to-end on a second SDK
beyond python. The validator builds a Go 1.24 image, stages the snippet,
runs `go mod init` + `go mod tidy` (matching the gonfalon flow) and
execs the program with CI=1 so the listener loop short-circuits after
the first evaluation.

Adds shared/lib.sh under validators/ for harness boilerplate
(require_env, await_success_line, dump_redacted, fail_with_log) and
switches the docker build context to validators/ so each Dockerfile
can pull from `shared/` alongside its own `languages/<runtime>/` dir.
The python harness is updated to source the shared lib; behavior
unchanged.

Verified: snippets render idempotent on go.tsx, snippets verify ok,
snippets validate --sdk=go-server-sdk against the real LD env prints
`*** The 'sample-feature' feature flag evaluates to true.` and exits 0.
…validator

Both Node SDKs share the validators/languages/node/ harness: it inits a
package.json (npm init -y) when one isn't present, installs whatever the
snippet's `validation.requirements` lines name, and runs the program with
CI=1 so the listener loop short-circuits after the first evaluation.

Verified node-server-sdk end-to-end against the real LD env (SDK key in
caller env). node-client-sdk requires LAUNCHDARKLY_CLIENT_SIDE_ID which
isn't set locally; the dispatcher's requireEnvForInputs fails fast with
a clear error, and CI will exercise it once the secret is provisioned.
Validator wraps the snippet in a one-liner that sets $stdout.sync = true
before loading it; otherwise Ruby block-buffers stdout when redirected
to a file and the success line never reaches the validator's grep
within the timeout. Snippet content is unchanged.

Verified end-to-end against the real LD env.
Validator uses the system composer pre-installed in the image (rather
than bootstrapping composer.phar like the snippet does — composer.phar
is what the user-facing snippet shows; the validator just needs the
deps available). Verified end-to-end against the real LD env.
.github/workflows/snippets-validate.yml runs the validator for each
snippet-bearing SDK as a parallel matrix cell. fail-fast is disabled
so every cell runs to completion. After the matrix finishes, a final
`summary` job downloads each cell's artifact (status + log), writes a
markdown table to GITHUB_STEP_SUMMARY listing each SDK / result /
3-line excerpt of the failure log, and exits non-zero if any cell
failed so the PR check goes red.

Required repo secrets (configure once at the sdk-meta repo level):
- LAUNCHDARKLY_SDK_KEY        (required)
- LAUNCHDARKLY_CLIENT_SIDE_ID (required for client SDKs)
- LAUNCHDARKLY_MOBILE_KEY     (required for mobile SDKs)

Initial matrix covers the SDKs ported so far (python, go, node-server,
node-client, ruby, php). Additional SDKs are added by appending rows
to matrix.include as their snippet dirs and validator harnesses land
in this PR.
Validator reproduces gonfalon's `cargo new` + `cargo add` flow inside a
Rust 1.83 image: bootstrap a Cargo project, copy the snippet's
src/main.rs over the default template, add the SDK + tokio
dependencies, and `cargo run`. Long timeout (300s) accommodates the
dependency-graph compile on cold cache.

Local validation deferred to CI (cargo cold-build of the LD SDK takes
several minutes). rust-server-sdk row added to the matrix so the
ubuntu-latest cell exercises it on every PR.
…jection

Drops the static repo-secret approach in favor of the same OIDC-based
flow the hello-* sample apps use. Each matrix cell now calls
launchdarkly/gh-actions/actions/verify-hello-app@verify-hello-app-v2.0.1
which:
- assumes the AWS role from `vars.AWS_ROLE_ARN`
- pulls the LaunchDarkly Sandbox account credential from Secrets
  Manager and injects it as LAUNCHDARKLY_SDK_KEY,
  LAUNCHDARKLY_CLIENT_SIDE_ID, or LAUNCHDARKLY_MOBILE_KEY according
  to the use_*_key flag
- runs `command:` and asserts its output contains the EXAM-HELLO
  `feature flag evaluates to true` line

Each matrix row declares `key-type: server | client | mobile` so the
right flag is set per SDK. We still set LAUNCHDARKLY_FLAG_KEY=sample-
feature ourselves; the action only handles the SDK-side credential.

Required setup at the sdk-meta repo level:
- `vars.AWS_ROLE_ARN` — the same repo *variable* every hello-* repo
  already references. Not a secret.
- `permissions: id-token: write` is set on the workflow; no static
  LD secrets are stored on this repo.
The original gonfalon snippet printed 'Feature flag X is true', which
diverges from the EXAM-HELLO standard (`feature flag evaluates to
true`) every other SDK follows. Validator surfaced this as a 'fix on
red' case — the SDK was working, the print line was wrong.

Verified end-to-end with LAUNCHDARKLY_CLIENT_SIDE_ID set.
Validator synthesizes a minimal .csproj around the snippet's Program.cs
(gonfalon's flow uses Visual Studio's 'new console app' wizard which we
can't reproduce in CI), pulls the package(s) named in
validation.requirements via 'dotnet add package', and runs with CI=1.

Verified end-to-end against the real LD env.
The .NET client SDK uses a mobile key (Configuration.Default takes the
mobile key as its first arg, not an SDK key or client-side ID). Snippet
input is typed `mobile-key`; validator pulls the value from
LAUNCHDARKLY_MOBILE_KEY at runtime.

Local validation 401'd against the test environment because the mobile
key value provided was actually a server SDK key (`sdk-...` prefix
instead of `mob-...`); CI will exercise it once the right value is
provisioned. Snippet + validator + render markers are functionally
correct.
Same gotcha as node-client-sdk: original gonfalon snippet printed
'Feature flag X is true' which doesn't match the EXAM-HELLO standard.
Updated to 'The 'X' feature flag evaluates to true.' Verified
end-to-end with the real mobile key (mob-...).
Java is the slice's first multi-fragment-snippet SDK: gonfalon shows
five separate XML/shell blocks (mvn-generate, cd-into, pom-dependency,
pom-build, pom-compiler) plus the App.java program plus the run line.
Each is its own snippet file with its own render marker; only
app-java carries `validation.runtime: jvm` and is the one the
validator actually exercises.

The JVM validator synthesizes a complete pom.xml around the snippet's
App.java rather than reproducing gonfalon's `mvn archetype:generate +
manual fragment pasting` flow. The synthesized pom pins
launchdarkly-java-server-sdk 7.13.4 (current latest on Maven Central
as of 2026-04-27); gonfalon's snippet shows \${version} fetched from
Maven Central asynchronously, which we don't reach out to from inside
the harness.

Two adjustments surfaced 'on red' that are baked into the validator:
- mvn archetype:generate writes `package com.launchdarkly.tutorial;`
  on the first line of App.java; the gonfalon snippet drops that
  (instructs the user to keep their auto-generated first line). The
  harness prepends it back so javac can resolve the mainClass.
- the gonfalon Run command uses `mvn ... assembly:single` to attach
  the maven-assembly-plugin to the build; the harness mirrors that.

Verified end-to-end against the real LD env.
The browser validator runs the snippet's index.html in headless
Chromium (Playwright v1.59.1 from the pinned mcr.microsoft.com image)
and polls the page body text for the EXAM-HELLO success line. The
js-client snippet renders 'The <flag> feature flag evaluates to <value>.'
into a div, which matches the regex used by the other validators.

Verified end-to-end with the real LD client-side ID. Page text matched
within the 30s window; SDK initialized + flag evaluation rendered
correctly.

Note: the snippet keeps gonfalon's deprecated unpkg URL
(launchdarkly-js-client-sdk@3 — pre-rename, pre-v4). The current
package is @launchdarkly/js-client-sdk at v4.x with a renamed
createClient API. Migrating is a separate fix-on-red task tracked
against the audit findings.
Roku snippets are rendered into gonfalon but carry no automated
validation: BrightScript only runs on Roku devices and the
proprietary BrightScript simulator, neither of which has a public CI
runtime. The snippet is reviewed manually against a real device when
changed; the version-staleness sweep still tracks the upstream
package.zip release.

Five snippets (mkdir, manifest, source/main.brs, components/AppScene.xml,
components/AppScene.brs) all rendered with markers in roku.tsx; the
'Download package.zip' step and the Run instructions stay
hand-authored prose since they're not Snippet elements.

Not added to the validation matrix — the validate command would skip
roku-client-sdk anyway because no snippet declares validation.runtime.
Five snippets (stack-new, package-yaml, stack-yaml, main-hs, run) all
rendered with markers in haskell.tsx. The Haskell program follows the
EXAM-HELLO output pattern (printf '*** The %s feature flag evaluates
to %s'), so the validator (when added) just needs a stack image with
launchdarkly-server-sdk + text pre-installed. Stack cold-build is
multi-minute, so per-validate cost will be material; the image must
prebuild deps to keep CI cycle time reasonable.

The snippet retains a known bug from the gonfalon source — line 121
calls lookupEnv on the rendered featureKey value (looking up an env
var named 'sample-feature') rather than 'LAUNCHDARKLY_FLAG_KEY'. The
Nothing path defaults featureFlagKey to 'sample-feature' so the
program coincidentally works in the test env; left as a fix-on-red
target for when the validator lands.

Not added to the validation matrix yet — `validate --sdk=haskell-
server-sdk` will fail because no snippet declares validation.runtime.
Renders into gonfalon are exercised by the existing render+verify
tests.
Seven snippets (rebar3-new, rebar-config, sup-childspecs, app-src,
server-erl, run-shell, run-call) all rendered. Erlang's Get Started
flow is fundamentally interactive (rebar3 shell + manual
gen_server:call), so the existing snippets don't follow EXAM-HELLO
output. Validator is left for a follow-up that wraps the gen_server
in a runnable harness module.
The scanner was treating any `'` (or `"`) as the start of a string
literal, scanning until the matching close quote — including across
many lines. JSX text often contains apostrophes inside words (`SDK's
shared libraries`, `let's`), which would consume hundreds of bytes
and skip past following render markers.

skipPlainString now returns i+1 when no closing quote appears before
the next `\\n`, treating the opening character as not-a-string-start.
Multi-line strings in JS/JSX use backticks (handled by skipBacktick),
so this is a safe heuristic.

Symptom that surfaced this: lua-server-sdk's gonfalon page has
'C++ SDK\\'s shared libraries' in a `<p>` between two markers. The
scanner ate from that apostrophe through the end of the next snippet's
backtick-template body, missing the `luarocks-install`, `hello-lua`,
and `run` markers entirely. After the fix, all five lua markers are
detected and their hashes update on `render`.

feat(snippets): port lua-server-sdk + erlang-server-sdk + haskell-server-sdk

All three carry `# Validator pending` comments in their hello-world
frontmatter — none of them currently has a CI runtime in this repo:
  - haskell needs a stack image with launchdarkly-server-sdk
    pre-built; cold-build is multi-minute
  - erlang's gonfalon flow is interactive (rebar3 shell + manual
    gen_server:call), so the snippet needs a wrapper module before
    a validator can match the EXAM-HELLO output
  - lua wraps the C++ Server SDK and needs cmake + boost + openssl
    plus luarocks; specialized image deferred

Each set of snippets renders into gonfalon idempotently. CI matrix is
unchanged — they're not validatable until a runtime lands.
…ing)

Each SDK has 9 snippets covering the gonfalon flow (mkdir, clone-sdk,
main-cpp, cmakelists, build-mkdir, cmake-make/ninja/msvc, cmake-build,
run). Validator pending: each per-validate cycle requires a Docker
image with cmake + boost + openssl + ninja and a checkout of cpp-sdks;
first build is multi-minute even with prebuilt deps.

Snippet output is non-canonical EXAM-HELLO ('Feature flag X is true');
when the validator lands the snippet will need a fix-on-red to print
the canonical 'feature flag evaluates to true' line.
Previously, any `{{ name }}` in a snippet body was treated as our own
substitution. That breaks Vue snippets, where `{{ flagValue }}` is
Vue's own runtime template syntax that needs to survive into the
rendered output untouched.

Renderer now takes a `declaredInputs` set. Names not in that set
round-trip as literal `{{ name }}` in both the runtime path
(validator) and the ld-application path (gonfalon). Conditionals
(`{{ if name }}…{{ end }}`) still require a declared input — Vue
uses `v-if`, not `{{ if }}`, so an undeclared conditional is almost
certainly an authoring mistake.

Tests updated. New regressions:
  - TestRenderRuntimePassesThroughUnknownVar
  - TestRenderRuntimeRejectsUnknownCondVar
  - TestRenderForJSXTextPassesForeignTemplate
  - HasInterpolation cases for foreign templates
Four snippets (podfile, pod-install, app-delegate, view-controller)
all rendered with markers in ios.tsx. The Podfile retains gonfalon's
'6.1.0' fallback as runtime-default so the rendered output keeps
that fallback (current latest is 10.2.0 — bump deferred until iOS
validator lands).

Known bug carried verbatim: ViewController.swift's updateUi label
uses '(flagKey)' and '(result)' instead of Swift's string interpolation
'\(flagKey)' / '\(result)'. Fix-on-red when the iOS validator runs.

Validator pending: iOS validation requires a macos-* runner with
Xcode and 'xcodebuild test'; deferred.
Five snippets (build-gradle, main-activity, activity-main-xml,
main-application, manifest) all rendered with markers in android.tsx.

Validator pending — Android needs setup-android + Linux emulator boot
in CI; tractable but slow and deferred.
Seven snippets covering the flutter create + pub add + ios/Podfile +
android/build.gradle + lib/main.dart + run command, all rendered with
markers in flutter.tsx. Validator pending — flutter-action on a Linux
runner is feasible; deferred.
Completes the 23-SDK port of every gonfalon getStarted file.

react-native-client-sdk: five snippets (create-expo, install, app-tsx,
welcome-tsx, run) all rendered with markers in reactNative.tsx.

react-client-sdk: covers two gonfalon variants under one SDK directory:
  - legacy variant (create-react-app) — render markers added to
    react/legacy.tsx for create, install, index-tsx, run. App.tsx is
    intentionally NOT marker-rendered: the gonfalon block uses
    `camelCase(featureKey)` to camelCase the flag key for the React
    useFlags hook, and our template engine doesn't yet support a
    `| camelCase` filter. The canonical App.tsx content is captured
    in legacy-app-tsx.snippet.md for the future docs adapter; the
    gonfalon block stays hand-authored until the filter lands.
  - createApp variant (Vite) — gonfalon's createApp.tsx loads its
    main.tsx / App.tsx via `?raw` Vite imports and substitutes via
    .replaceAll('environmentId', ...). Render markers don't fit
    naturally without restructuring that file. The five snippets
    (create-vite, install, main-tsx, app-tsx, run-dev) are present
    in this dir but rendering into gonfalon's createApp.tsx is
    deferred until that file is migrated off the assetSource
    pattern.

All ported snippets currently have validators deferred — RN bundler /
Expo, Vite + Playwright, and CRA + Playwright are heavier to wire than
the Linux Docker server SDKs. Tracked for follow-up.
@kinyoklion kinyoklion changed the title feat: Add server snippets. feat: Add remaining getting-started snippets. Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant