terradart_core 0.35.0
terradart_core: ^0.35.0 copied to clipboard
terradart core runtime — Stack, Resource, Provider, Variable, Data, TfArg, TfRef, and LifecycleOptions for Dart-first Terraform synthesis.
Changelog #
0.35.0 - 2026-10-03 #
- No API changes. Lockstep release with the
terradartcommand's--jsonoutput, fixed exit codes,--no-input,--dry-run,terradart help <topic>and bundled agent skill.
0.34.0 - 2026-10-03 #
runEnvironmentstakesdefaultEnv, the memberterradartruns against when neither--envnorTERRADART_ENVnames one; it records it in the manifest (default). Without--env, environments that share a directory write onlydefaultEnvinstead of exiting 64. AdefaultEnvthat is not one of the environments throwsArgumentError.
0.33.0 - 2026-10-02 #
runStack(args, build)andrunEnvironments(args, Env.values, build, {dir, workspace, backendConfig})are the entry pointsbin/infra.dartcalls:runEnvironmentstakes the members of an enum of the project's own, writestf-out/<name>per member (every member, or the one--env <name>names; an unknown name exits 64 listing the members), and throwsArgumentErrorwhen environments share a directory that neither a workspace nor a partial backend configuration tells apart. Both describe what they wrote to theterradartcommand in the fileTERRADART_MANIFESTnames (terradartManifestVariable).- The
<Stack>Outputsreader's missing-defineStateErrornames the define file that carries the variable (--dart-define-from-file=.terradart/dart_defines.json) and theterradart apply/terradart outputscommand that writes it, or says noaddDartDefineOutputcarries it.
0.32.1 - 2026-10-02 #
- No API changes. Republishes the 0.32.0 workspace so
terradart_appwrite,terradart_cloudflare,terradart_awsandterradart_migratereach pub.dev; the 0.32.0 publish workflow stopped them at a wrapper-count check that also counted hand-written files, and now counts only generated wrappers (#877).
0.32.0 - 2026-10-02 #
- Breaking:
TfRef<T>is a sealed subtype ofTfArg<T>, so an attribute getter passes straight into an argument of its type (labels: other.labels,addOutput('id', topic.id)).TfArg.refandTfArgRefare removed; matchTfRef()/AttributeRef(:owner, :attr)where code matchedTfArgRef(:ref).AppConstant.ref(...)is unchanged. See MIGRATING.md. RefTo.alsoAs(attribute)reads another attribute of the referenced block (null for a reference built from a value). Generated IAM adjuncts default thelocation/project/region/zonethey share with their parent to it.Stack.addDartDefineOutput({name = 'dart_defines', only, description})declares an output whose value maps each variableoutputEnvironment()would pass to its value — the JSON--dart-define-from-filereads, fromterraform output -json dart_defines. It carries the non-sensitive outputs registered by synth time, or thoseonlynames;Stack.dartDefineOutputslists them. Registration throwsArgumentErrorfor a name that is not a Terraform identifier or is taken; synth reportsInvalidDartDefineOutputwhenonlynames an unregistered or sensitive output, two outputs share a variable, or the file carries no output.- The generated
<Stack>Outputsreader hasconstconstructors —fromTerraformJson,fromEnvironment, andfromDartDefine(), which reads the variables compiled into the app (bool.hasEnvironment/String.fromEnvironment). A missing define throwsStateErrornaming the variable and the output. Stack.addConfigurationAliasregisters a provider alias the calling module passes in (configuration_aliases = [google.eu]). Synth lists it onrequired_providersand emits noproviderblock for it; a resource selects the instance withprovider:. The alias carries no configuration arguments.Stack.addExternalProviderregisters a provider configuration that lives in a file besidemain.tf.json. Synth lists it onrequired_providersand emits noproviderblock, so a resource can still select the instance withprovider:.- Breaking:
StacktakesbackendandrequiredVersion(default'>= 1.11.0') as constructor arguments;setBackendandsetRequiredVersionare removed, andwriteTodefaults to'tf-out'.TfTimeoutsfields areDuration?(written as Go duration strings; a negative one is anInvalidTimeout), andTfTimeouts.of/isDurationare removed.outputEnvironment()returns anOutputEnvironment, anIterable<({String name, TfArg<String> value})>for a list of blocks (a Cloud Run container'senv) whosevariablesis theTfArg<Map<String, String>>a map argument takes (environment: .new(variables: outputEnvironment().variables)on anAwsLambdaFunction,environmentVariableson a Cloud Function).TfArg.literalis aconstfactory, so an explicit type argument is writtenTfArg<String>.literal(...).goDurationString/parseGoDurationare inpackage:terradart_core/internal.dart. See MIGRATING.md. - Breaking:
TfJsonEncoder,hasTemplateSequenceandtemplateVariableNamesmove fromterradart_core.darttopackage:terradart_core/internal.dart, the library for tools that generate or read TerraDart code. See MIGRATING.md. - Breaking:
Resource.provideris theStackProvider?the block uses instead of a'name.alias'string, andModuleCall.providersis aMap<String, StackProvider>.Stack.addProviderregisters a configuration from the constructor body and returns it, soprovider: eupasses the instance.Resource.defaultProvider(the type prefix by default) is the provider a block without one uses; synth emits it when it differs from the prefix.MissingProvidercovers an instance the Stack does not register (unregisteredInstance). See MIGRATING.md. - Breaking:
LifecycleOptions.ignoreChangesis a sealedIgnoreChanges(IgnoreChanges.all,IgnoreChanges.of([...])) instead ofList<String>, andreplaceTriggeredByis aList<ReplaceTrigger>—ResourceandTfRefimplement it, so a whole resource goes in as it is ([template, template.id]).LifecycleOptions.conditionstakesLifecycleCondition.pre/.post(precondition/postconditionblocks).createBeforeDestroy: falseandpreventDestroy: falseare written instead of dropped. Synth reportsInvalidLifecyclefor a data source inreplaceTriggeredBy,'all'inside.of, or an empty condition message. See MIGRATING.md. - Breaking:
Sensitive<T>is the type of an argument Terraform marks sensitive: a sealedTfArg<T>withSensitive.variableandSensitive.expressionbut no.literal.TfArgVariable,TfArgExpressionand everyTfRef(attribute getters) implement it. See MIGRATING.md. - Breaking:
Stack.variable<T>(name, {type, description, defaultValue, sensitive, nullable})declares a variable and returns itsTfArgVariable<T>handle, which an argument takes as is (location: region); the Terraformtypeis derived fromTunlesstype:says otherwise.addVariableis removed,addExternalVariableisexternalVariable<T>(returning the handle), andTfVariable.typeis a sealedTfType(TfType.string,.list(...),.set(...),.map(...),.object({...}),.tuple([...]),.optional(...)) instead of a string.TfArgVariable.interpolationis${var.<name>}. See MIGRATING.md. - Breaking:
TerraformEnumis removed. A Terraform enum is an extension type overTfArg<String>whose members arestatic constTfArgLiterals (the README shows the shape), so it passes to aTfArg<String>slot directly.TfArgLiteral.toTfJson()throwsArgumentErrorfor a plain Dartenum. See MIGRATING.md. - Breaking:
Resource,DataandModuleCalltake the local name as their first positional argument:Resource(this.localName, {...}),ModuleCall(this.localName, {required this.source, ...}). See MIGRATING.md. - Breaking:
Stack.addregisters a data source as well as a resource, andStack.addDatais removed. See MIGRATING.md. - Breaking:
dependsOnonResource,DataandModuleCallis aList<TfAddressed>—dependsOn: [schema, api].DependencyTarget,ResourceDependencyandRefDependencyare removed (Terraform rejects an attribute independs_on). Synth output is unchanged. See MIGRATING.md. - Breaking: synth validation is one sealed type.
Stack.synth()/writeTo()throw oneSynthExceptionlisting everySynthIssue(NoProviders,MissingProvider,ProviderConflict,UndeclaredVariable,UnregisteredReference,SensitiveLiteral,InvalidTimeout,InvalidMoveTarget,UnresolvableConstant) instead of throwingStateError/SensitiveLiteralError/ArgumentErrorat the first problem;Stack.validate()returns the issues without throwing.SensitiveLiteralError,TfJsonEncoder.validateProvidersandTfJsonEncoder.encodeArgMapWithSensitiveare removed, and the remainingTfJsonEncoderhelpers no longer validate. - New check: a reference (or
depends_on/replace_triggered_by) to a resource, data source or module the Stack does not hold is anUnregisteredReference.Stack.addExternalBlock(address)/externalBlocksdeclare a block a hand-written file holds. add,addModule,variableandexternalVariablethrowArgumentErrorfor a name that is not a Terraform identifier.- Every Dart example in a doc comment compiles (
tool/doc_snippets.dart): theTfTimeouts,TfMoved,ModuleCall,Stack.outputEnvironment,Stack.addModule,AppExports,S3Backend.r2andTfArgexamples name every required argument. Doc comments only.
0.31.0 - 2026-10-01 #
- The
appExportsfile also holds<Stack>Outputs, a typed reader of the Stack's non-sensitive outputs: a lowerCamelCase getter per output, typed like its value, from<Stack>Outputs.fromTerraformJson(Map<String, Object?>)(terraform output -json) or<Stack>Outputs.fromEnvironment(Map<String, String>)(ORDERS_TOPIC_IDfororders_topic_id; aStringis the raw value, any other type JSON). Getters read lazily and throwStateErroron a missing or mistyped value. WithappExportsset,addOutputthrowsArgumentErrorwhen the output's getter is not a usable Dart identifier or its getter or variable is another output's. The file now importsdart:convert. Stack.outputEnvironment({Iterable<String>? only})returns the environmentfromEnvironmentreads —Map<String, TfArg<String>>from variable name to the output's value (aStringas is, any other type${jsonencode(...)}), for the non-sensitive outputs registered so far — to pass as a Cloud Run service's or function'senv. ThrowsStateErrorwithoutappExports, andArgumentErrorfor a name inonlythat is not a registered non-sensitive output or an output with no environment value.- Breaking —
Stack.addOutputandStack.addConstantreplaceaddExport.addOutput<T>(name, TfArg<T> value, {description, sensitive})declares a Terraformoutput;addConstant<T>(name, AppConstant<T>)declares astatic constof the generated<Stack>Constantsclass, from a sealedAppConstant<T>:.ref(TfRef<T>)(the literal the attribute is set to, resolved at synth),.value(T)orAppConstant.fromEnvironment(name).Tis any JSON value type,List<E>orMap<String, V>of them, each optionally nullable. The constants file is a constructor parameter,appExports: AppExports(path, {name}), andStack.outputs/Stack.constantsexpose what was registered. Registration rejects a bad or duplicate name, an unsupported type, a.valuethat is not aT, a whole-resource.ref, a constant withoutappExports, and a non-sensitive output that reads a sensitive field. Synth throwsStateErrorwhen a.refconstant's attribute is not a literal (naming what sets it), is a sensitive field, or belongs to an unregistered block. WithappExportsset,writeTorewrites the file on every synth.SynthResult.dartConstantsbecomesdartSource+dartSourcePath, andStack.synth()loses itsstackNameparameter (AppExports.namesets the class prefix). Removed:AppExport,ResourceIdExport,ResourceAttributeExport,StringExport,EnvBackedExport,setAppExportsOutputPath,appExportsOutputPath, and the barrel exports of the synth internalsDartConstantsEmitter,LiteralResolver,OutputEmitter,OutputEmissionResult,TerraformOutputSpecandDartConstantSpec. See MIGRATING.md. AppConstant.valueType,TfOutput.valueTypeandTfOutput.toTfJsonare@internal: synth reads them, andvalueTypereturns a type the barrel does not export.RefToList—encodeAson aTfArg<List<RefTo<R>>>: a literal list encodes element by element, a whole-list value (TfArg.variable('subnet_ids')) passes through.RefTo<R>— a reference to a resource of typeR, for arguments that name another resource. An extension type over a record, soRis checked at compile time and erased at run time. A generatedrefgetter returns one (RefTo.of), the argument that takes it picks the attribute it emits (encodeAs('self_link')),pinned('id')keeps a given attribute, andRefTo.literal/RefTo.variable/RefTo.expression/RefTo.argcarry values from outside the Stack (dot shorthands:.literal('...')). No argument takes one yet.- Breaking — requires Dart 3.10 (
sdk: ^3.10.0, was^3.6.0). Source is formatted in the Dart 3.7+ tall style. - Dartdoc on
TfAddressed.tfAddressandResource.tfAddress, which every factory inherits. No API changes.
0.30.0 - 2026-09-28 #
- Sensitive-field paths accept a
*segment for a map of blocks:env_vars.*.valuechecksvaluein every entry ofenv_vars, and a plain literal fails synth with the entry's key in the field path (env_vars.API_KEY.value).
0.29.0 - 2026-09-27 #
Lockstep release. No terradart_core API changes; terradart_time and terradart_aws publish for the first time on this version.
0.28.1 - 2026-09-13 #
Lockstep release with terradart_migrate 0.28.1 (passthrough emission fix — a bare Map / List parameter no longer comes out as TfArg.literal). No terradart_core API changes.
0.28.0 - 2026-09-13 #
Added #
TfArg.expression(TfArgExpression) — a raw Terraform expression, emitted verbatim as the tf.json template it is:TfArg.expression(r'${lower(var.name)}-x'),TfArg.expression<int>(r'${var.replicas * 2}'). Accepted on sensitive fields likeTfArg.refandTfArg.variable(no value is stored in it); thevar.<name>references inside it are checked against the Stack's declarations at synth time; a plain value with no${ ... }/%{ ... }sequence is rejected with anArgumentError. Replaces theTfArg.literal(r'${...}')workaround. Breaking for exhaustiveswitches overTfArg— see MIGRATING.md (#662).hasTemplateSequence/templateVariableNames— the template scanner behind it (escapes, quoted strings and directives handled), exported for tools that generateTfArgcode.- Provider aliases —
StackProvider.alias(provider "google" { alias = "eu" }), settable on every provider class in the workspace, andResource.provider/Data.providersettable from every curated factory's newprovider:parameter (provider: 'google.eu', orprovider: 'google-beta'on a GA type). Synth emitsprovider.<name>as a list when a name has more than one configuration (Terraform's JSON form for aliases; a single default configuration keeps the object form) and rejects aprovider:with no matching registration, the same name registered twice without an alias or with the same alias twice, and an alias that is not a Terraform identifier. Breaking for hand-writtenStackProviderimplementations (one getter) — see MIGRATING.md (#666). TfTimeouts/Resource.timeouts— thetimeouts { ... }block as a Dart value (create,read,update,delete), on every curated factory and data source through the sharedtimeouts:parameter, emitted verbatim under the block'stimeoutskey. Provider-neutral likelifecycle: the values are the Go duration strings Terraform writes ('30m','1h30m') — references are not allowed there — and which operations a type declares isterraform validate's business, not synth's.TfTimeouts.of(create: Duration(minutes: 30))builds one fromDurations; a value that is not a duration string throws (#671).TfArg.workspace()—${terraform.workspace}, the selected workspace's name, as a named argument instead of a hand-written expression. Sugar overTfArg.expression, so nothing about state layout or workspace selection changes (#671).- Partial backend configuration — every field of
GcsBackendandS3Backendis optional, so a block whose values arrive at init time (terraform init -backend-config=bucket=...) is expressible:const GcsBackend()emitsbackend "gcs" {}.S3Backend.r2keeps its endpoint and flag preset withbucket/keyleft out (#671). ModuleCall/Stack.addModule— amodule "<name>" { ... }call as a Dart value:source,version,inputs,providers,count/for_eachanddepends_on, emitted under the top-levelmodulekey in registration order. A module's outputs are read back asTfRefs (call.output<String>('member')→${module.<name>.member}), so they flow into anyTfArgslot,depends_on,lifecycleand exports. Synth validates what Terraform would: an input named like a meta-argument is rejected at construction, aprovidersvalue must name a registered provider configuration, andvar.<name>references inside the inputs are checked like a resource's. A repeated local name throwsDuplicateModuleError(#665).- A Stack that only calls modules needs no provider —
Stack(providers: [])synthesizes when the stack registers at least oneModuleCalland no resource or data source of its own: the child modules pin what they use, sorequired_providersis omitted rather than empty. A stack with a resource still needs its provider (#665). Stack.addMoved/TfMoved—moved { from = ... to = ... }blocks, emitted under the top-levelmovedkey in registration order, so a renamed resource (or acount/for_eachinstance unrolled into its own resource) keeps its state. Registration rejects an empty or repeatedfromandfrom == to; synth rejects atothat names no resource of the Stack (amodule.address passes) (#663).
Changed #
- Sensitive nested fields accept any Terraform template — an unescaped
${ ... }or%{ ... }anywhere in the string — where they accepted only a string starting with${(#662). SensitiveLiteralError— the recovery hint mentionsTfArg.expressionfor values Terraform computes.
0.27.0 - 2026-08-30 #
Lockstep release across the workspace. Breaking — see MIGRATING.md.
Added #
S3Backend—terraform { backend "s3" { ... } }configuration, alongside the existingGcsBackendandLocalBackend. Covers S3 and the S3-compatible stores (Cloudflare R2, MinIO, Backblaze B2) viaendpointsplus theskip_*flags.S3Backend.r2(accountId:, bucket:, key:)fills in the R2 endpoint,region = "auto", path-style addressing, and all five skip flags. Optional fields are omitted from the emitted JSON when null; an explicitfalseis emitted.TfVariable+Stack.addVariable— declare thevariable "<name>" { ... }blocks thatTfArg.variablereferences. Synth emits them under the top-levelvariablekey, and omits the key when a stack declares none (Terraform rejects an emptyvariableblock).Stack.addExternalVariable— acceptTfArg.variablereferences to a variable declared in a hand-written file beside the generatedmain.tf.json, without emitting a block for it. For declarationsTfVariablecannot model (validation { ... }) and for existing stacks that keep avariables.tf.- Undeclared-variable check at synth time — synth throws a
StateErrornaming the variable and the resources referencing it when aTfArg.variablereference has no matchingaddVariabledeclaration, including references nested inside literal Maps and Lists. Previously such a config synthesised cleanly and failed atterraform planwith "Reference to undeclared input variable". Breaking — see MIGRATING.md.
Fixed #
- Docs —
Stack.backendandbackends.dartdescribed S3 as living in "provider-specific packages" and named anS3Backendthat no package shipped. Both now describe what core actually provides.
Changed #
SensitiveLiteralError— the recovery hint now points ataddVariableinstead of telling the user to hand-write avariableblock.
0.26.0 - 2026-08-24 #
Lockstep release with terradart_cloudflare 0.26.0 (catalog filled at the 5.23.0 pin). No terradart_core API changes.
0.25.3 - 2026-08-23 #
Lockstep release with terradart_appwrite 0.25.3 (catalog filled at the 2.0.0-beta.1 pin). No terradart_core API changes.
0.25.2 - 2026-08-22 #
Lockstep release with terradart_cloudflare 0.25.2 (initial release). No terradart_core API changes.
0.25.1 - 2026-08-19 #
Lockstep release with terradart_google_beta 0.25.1 (beta-only catalog filled). No terradart_core API changes.
0.25.0 - 2026-08-15 #
Lockstep release with terradart_google 0.25.0 (GA hashicorp/google catalog filled). No terradart_core API changes.
0.24.0 - 2026-07-03 #
Lockstep release with terradart_google 0.24.0 (typed nested helpers). No terradart_core API changes.
0.23.0 - 2026-07-02 #
Lockstep release. Breaking — see MIGRATING.md.
Breaking #
- Removed the
StackProvider.toTfJson()backwards-compat shim; readconfigArgsdirectly.Backend.toTfJson()/TfArg.toTfJson()are unchanged (real, distinct APIs).
0.22.0 - 2026-06-30 #
Lockstep release for Waves 76–77. No API changes in terradart_core.
0.21.0 - 2026-06-28 #
Lockstep release for Wave 74. No API changes in terradart_core.
0.20.0 - 2026-06-21 #
Lockstep release for Wave 73. No API changes in terradart_core.
0.19.0 - 2026-06-21 #
Lockstep release. No API changes vs 0.18.0.
0.18.0 - 2026-06-21 #
Lockstep release. No API changes vs 0.17.1.
0.17.1 - 2026-06-21 #
Lockstep release. No terradart_core API changes.
0.17.0 - 2026-06-21 #
Lockstep release. No terradart_core API changes.
0.16.0 - 2026-06-21 #
Lockstep release. No terradart_core API changes.
0.15.0 - 2026-06-20 #
Lockstep release. No terradart_core API changes.
0.14.0 - 2026-06-16 #
Lockstep release. No terradart_core API changes.
0.13.0 - 2026-06-14 #
Lockstep release. Breaking — see MIGRATING.md.
Breaking #
TimeProvider/TimeSleepmoved toterradart_google(package:terradart_google/time.dart) — core is provider-neutral again.- Removed the unimplemented provider-aliasing surface:
StackProvider.providerAlias,ProviderBinding, andResource.provider. None of it ever reached the synthesized JSON; aliasing returns when multi-provider stacks land. Stack.synth()validates provider coverage: a registered resource / data source whose type prefix (segment before the first_) has no matching providerproviderNamenow throwsStateErrorinstead of silently falling back to an unpinned implied provider.
0.12.19 #
Added #
TimeProvider—hashicorp/timestack provider (~> 0.12) for propagation waits.TimeSleep— hand-writtentime_sleepresource wrapper.
0.12.18 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.18 (Wave 31 Private CA template + pool IAM).
0.12.17 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.17 (Apis.required helper).
0.12.16 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.16 (Wave 30 Private CA certificate).
0.12.15 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.15 (Wave 29 Private CA certificate authority).
0.12.14 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.14 (Wave 28 Private CA pool).
0.12.13 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.13 (Wave 27 Certificate Manager trust + issuance; GoogleProject example backfill).
0.12.12 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.12 (sealed exactly_one_of enforcement on seven factories; see MIGRATING.md).
0.12.11 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.11 (Wave 25 VPC Access connector; Wave 26 Certificate Manager; Artifact Registry remote docker/maven/npm enums).
0.12.10 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.10 (Wave 23 DNS, Eventarc, Cloud Run worker pool, IAP).
0.12.9 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.9 (Wave 22 BigQuery Analytics Hub IAM + Compute regional Armor).
0.12.8 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.8 (Waves 17–21 Eventarc + Compute/BigQuery/Storage/SQL/Firebase).
0.12.7 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.7 (Waves 12–15 Monitoring + Compute/KMS/Pub/Sub/Storage).
0.12.6 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.6 (Wave 10 GKE Backup + Wave 11 Logging project ops).
0.12.5 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.5 (Wave 9 GKE Hub + example/docs debt).
0.12.4 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.4 (Wave 8 GKE core curated factories).
0.12.3 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.3 (WIF provider sealed trust-source breaking fix).
0.12.2 #
No user-facing API changes. Lockstep version bump for terradart_google v0.12.2 (two new curated factories). The terradart_core public surface is unchanged from 0.12.1.
0.12.1 #
No user-facing API changes. Lockstep version bump alongside the terradart_agent v0.12.1 fix (MCP structuredContent object shape). The terradart_core public surface is unchanged from 0.12.0.
0.12.0 #
No user-facing API changes. Lockstep version bump alongside the rest of the workspace for the v0.12.0 release (terradart_codegen static-catalog emission, terradart_google generated catalog, and the new terradart_agent / terradart-mcp package). The terradart_core public surface is unchanged from 0.11.0.
0.11.0 2026-MM-DD #
BREAKING — pre-1.0 polish wave on the terradart_core public surface. Coordinated changes from ADR-0016 (codegen identifier rename) and ADR-0017 (Stack API surface). v0.x permits breaking changes; the 0.11.x line continues to stage the 1.0 surface. See MIGRATING.md for before / after snippets covering every item below.
Stack.synth({required outDir})split —Stack.synth() → SynthResultis now the pure in-memory step that returns the encodedtfJsonplus the optionaldartConstantssource for AppExports.Stack.writeTo(outDir) → Future<void>is the new file-IO wrapper that always writesmain.tf.jsonand, when AppExports produced Dart constants ANDsetAppExportsOutputPathwas called, also writes the generated constants file at that path.writeTothrowsStateErroratomically — before any disk write — whenaddExportwas called withoutsetAppExportsOutputPath.StackSynthremoved from the public barrel — callstack.synth()instead ofStackSynth.synth(stack). The class is annotated@internal; advanced users may still import it via the deep pathpackage:terradart_core/src/synth/stack_synth.dart.Stack,Resource,Datapromoted toabstract base class— user subclasses must now be declaredfinal class XxxStack extends Stack(orbase/sealed).implements Stack/implements Resource/implements Dataare no longer permitted (was a state-bypass foot-gun).Resource.$sensitiveFields→Resource.sensitiveFieldsandResource.$supportsDeletionProtection→Resource.supportsDeletionProtection— dollar-prefix dropped. Both annotated@protected(frompackage:meta); non-subclass reads require an// ignore: invalid_use_of_protected_memberdirective with rationale. Privileged in-library consumers (theTfJsonEncodersynth call sites) already carry the ignore comment with justification.TerraformEnuminterface added —abstract interface class TerraformEnum { String get terraformValue; }is re-exported from theterradart_corebarrel.TfArgLiteral.toTfJsonenum dispatch now routes throughif (v is TerraformEnum); the previous duck-typeddynamic.terraformValuecast and its// ignore: avoid_dynamic_callsdirective are retired. Hand-rolled Terraform-mapped enums must addimplements TerraformEnumand@override final String terraformValue;; codegen-emitted enums get this automatically.
Non-breaking improvements #
encodeArg/encodeArgMap/encodeArgMapWithSensitivereturn types tightened fromdynamictoObject?/Map<String, Object?>— non-breaking at runtime, but call sites benefit from static type checking.- Internal
_DedupKeyvalue type rewritten as a Dart 3 named record. Drops the unusedpackage:meta/meta.dartimport. dart:convertimport prefixes unified acrosslib/andtest/(as dart_convert/as conv/as convert→ no prefix everywhere).
0.10.0 2026-MM-DD #
No user-facing API changes. Workspace consistency bump alongside terradart_google 0.10.0 (Firestore document curation + FirestoreFields.encode helper).
0.9.0 - 2026-05-21 #
BREAKING — pre-1.0 polish wave consuming dogfood findings (issues #52-#57). v0.x permits breaking changes; 1.0 semver lock is deferred until cookbook recipes + real-apply feedback have absorbed more cycles. The 0.9.x line is the staging ground for the 1.0 surface:
Stack.synth({required String outDir})is now concrete — default implementation writes pretty-printed${outDir}/main.tf.jsonand createsoutDirrecursively. Subclasses may still override; existing overrides that delegated toStackSynth.synth(this)+JsonEncoder.withIndent(' ')can be deleted in favour of the default.JsonEncoder→TfJsonEncoder— the synth-time JSON encoder class no longer shadowsdart:convert'sJsonEncoder. Consumers that imported the class directly need to rename references; consumers using onlyStack/StackSynthare unaffected.Stack({bool devMode = false})— new constructor parameter. When true, synth-time injection flipsdeletion_protection: falseon registered resources whoseResource.$supportsDeletionProtectionis true and that did not explicitly set the field. Intended for dogfood / sample apps; production stacks leave it false.Resource.$supportsDeletionProtection— new capability getter (defaultfalse); codegen overrides totrueon 6 curated resources with adeletion_protectionattribute.LocalBackendadded —StackBackendimplementation matching the existingGcsBackendshape. Pass viaStack(backend: const LocalBackend()); no more handwrittentf-out/terraform.tf.TfArg.variable(String name)+TfArgVariable<T>added — third peer of the sealedTfArg<T>family. Emits"${var.<name>}"interpolation. Canonical pattern for sensitive runtime values.SensitiveLiteralErroradded — thrown byTfJsonEncoder.encodeArgMapWithSensitivewhen aTfArgLiteralis assigned to a sensitive field. v0.x silently masked the value to empty string, which caused apply-time HTTP 400. Recovery: switch toTfArg.variable(...)or the<field>_wowrite-only variant.encodeArgMapWithSensitivesignature — gains a requiredString resourceAddressparameter (used bySensitiveLiteralErrorfor diagnostic messages). Internal callers (TfJsonEncoder.resourceBlock) updated.
See MIGRATING.md for the full rename table + sed recipes.
0.8.0-dev - 2026-05-19 #
No user-facing API changes. Workspace consistency bump alongside terradart_google 0.8.0-dev (Wave 7: 23 new GA resources across Data ops + observability + CI/CD — Cloud Build, Artifact Registry, Logging, Monitoring, BigQuery, plus event-driven adjacent: Eventarc, Pub/Sub schema, Storage notification).
0.7.0-dev - 2026-05-19 #
No user-facing API changes. Workspace consistency bump alongside terradart_google 0.7.0-dev (Wave 6: 25 new GA resources across the L7 Application Load Balancer stack — Global + Regional/Internal cores, Health checks, MIG/Autoscaler, NEG, Cloud Armor, SSL Policy).
0.6.0-dev - 2026-05-18 #
No user-facing API changes. Workspace consistency bump alongside terradart_google 0.6.0-dev (Wave 5: 22 new GA resources across IAM completion + Cloud SQL + *_iam_member fill).
0.5.0-dev #
BREAKING — Plan 5.X: schemantic removal + Resource type flattening.
- Dropped
Resource<S>generic —Resourceis now flat. User wrappers extendResourcewithout a type parameter. - Dropped
Resource.schemafield. - Dropped
SchemaCarrier<S>interface. - Dropped
ResourceRef<S>.placeholder—ResourceRefno longer carries a schemantic-instance type parameter (it pins toObject?since the value never escapes). - Dropped
TfArgRef.literalOrPlaceholder. - Deleted
placeholder.dart. - Existing dead tests around the above (e.g.
r.schema.nameassertions) removed. - See ADR-0013 for full rationale.
0.4.0-dev - 2026-05-17 #
No user-facing API changes. Workspace consistency bump alongside terradart_codegen 0.4.0-dev (Plan 5.D: codegen correctness improvements — MM YAML deprecation parsing fix, encode skeleton fix + Gate 6, paramOrder measurement tool, min_items assert hints).
0.3.0-dev - 2026-05-16 #
No user-facing API changes. Workspace consistency bump alongside terradart_google 0.3.0-dev (Wave 4: 21 new GA resources across 6 Firebase / Cloud Functions / Firestore services).
0.2.1-dev - 2026-05-16 #
No API change since 0.2.0-dev. Workspace consistency bump after the 0.2.0-dev publish run partially failed; 0.2.1-dev republishes through a re-ordered publish pipeline.
0.2.0-dev - 2026-05-16 #
Added #
TfArg.duration(Duration)factory — converts a DartDurationto the"{seconds}s"form Terraform expects for duration-string fields (rotation_period,message_retention_duration, the's'-suffixed forms ofack_deadline_seconds, etc.). Sub-second precision and negative durations are rejected withArgumentError.
Fixed #
JsonEncoder.encodeArgMapWithSensitivenow masks sensitive paths through nested-block (List<Map>) structures. Previously, paths likecustomer_encryption.encryption_keywere left as plaintext intf-out/main.tf.jsonbecause the masker only walked top-level keys. Ref interpolations (${...}) continue to pass through unchanged so Terraform wiring is preserved.
0.1.0-dev - 2026-05-14 #
Added #
-
TfArg<MyEnum>.literal(MyEnum.foo)now encodes typed Dart enums to Terraform strings via a new.terraformValueconvention. Declare your enum as:enum MyEnum { foo('FOO'), bar('BAR'); const MyEnum(this.terraformValue); final String terraformValue; }and
TfArgLiteral.toTfJson()will serializeMyEnum.fooas the string"FOO". ThrowsArgumentError(not silent wrong-output) if you pass an enum value whose type does not implement the convention. -
String / int / num / bool literals continue to pass through
toTfJson()unchanged.
Notes #
- No breaking changes to
Stack/Resource/Data/StackSynth/Provider/Variable<T>/LifecycleOptions/AppExport/TfArg/TfRef.
0.0.4-dev - 2026-05-11 #
- No user-facing API changes. Version bumped for workspace consistency with Phase 4.1 (
terradart wrapsubcommand + DataSource emitters + 13terradart_googlewrappers migrated to generator output).
0.0.3-dev - 2026-05-09 #
- Fix: rename terradart_core main library file to match package name.
0.0.2-dev - 2026-05-09 #
- CI automated publishing via OIDC trusted publisher.
- Fix: prepare_publish.sh now syncs version from tag name.
0.0.1-dev - 2026-05-09 #
Added #
- Initial pre-alpha release of
terradartcore runtime. Stackabstract base — registerResource<S>/Data<S>instances viaadd(...)/addData(...).StackSynth.synth(stack)returningSynthResult(Terraform JSON + optional Dart constants).Resource<S>/Data<S>typed nodes;Provider,Variable<T>,LifecycleOptions,AppExport.TfArg.literal(...)/TfArg.ref(...)argument helpers.
Notes #
- Pre-alpha — surface and emitted Dart symbols may change between 0.0.x releases.