SDK generation

The SDK plugin generates a typed package from the database's stored definitions. While the database is open, it updates that package in the configured output directory.

The generated code calls the language binding and provides typed access to stored definitions.

Enable sdk in the plugin selection at open, with the output settings below. Calls without it report the unavailable status.

Query results recursively omit fields containing empty objects or arrays. Generated rows keep zero values for absent fields, such as nil for an any field. Null, empty strings, false, zero and array positions stay intact in the returned document.

Native time values

Generated native time fields use time.Time for timestamps and time.Duration for durations. Use stardust.InstantFromTicks, InstantToTicks, DurationFromTicks and DurationToTicks for native tick conversion.

The explicit tick conversions refuse precision loss and values outside the target range. These conversions perform no native operation.

A native time schema requires one string member, #utc or #dur, and sets additionalProperties to false. For example, this schema describes a native timestamp:

TRON184 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
044C 74797065txt "type"
096C 737472696E67txt "string"
100F 0A 04000000 0900 0000map leaf, size 10: @4 → @9
1A4C 23757463txt "#utc"
1F0F 0A 1A000000 1000 0000map leaf, size 10: @1A → @10
294C 23757463txt "#utc"
2E0E 0D 00 0100 010000 00 29000000array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @29
3BAC 70726F70657274 696573txt "properties"
460F 0A 3B000000 1F00 0000map leaf, size 10: @3B → @1F
508C 72657175697265 64txt "required"
590F 0A 50000000 2E00 0000map leaf, size 10: @50 → @2E
6314 14 616464697469 6F6E616C50726F70 657274696573txt "additionalProperties"
7901bit false
7A0F 0A 63000000 7900 0000map leaf, size 10: @63 → @79
844C 74797065txt "type"
896C 6F626A656374txt "object"
900F 0A 84000000 8900 0000map leaf, size 10: @84 → @89
9A07 16 A8040000 4600 0000 59000000 7A00 0000 90000000map branch, size 22, depth 0 (root), slots 3, 5, 7, 10: @46 → @59 → @7A → @90

Use #dur in both places for a duration schema. Schema fields support lists and stored-schema references. Scalar time fields also type query filters and rows. Native time expression results use the same binding types. An ordinary string schema stays text, including strings with a date-time format. Native ticks are signed 64-bit counts of 100 ns. Timestamp ticks count from the Unix epoch in UTC. Duration ticks can be negative.

Settings

Set go.dir to a dedicated directory for the generated packages:

TRON163 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
043C 646972txt "dir"
08AC 67656E2F70656F 706C65txt "gen/people"
130F 0A 04000000 0800 0000map leaf, size 10: @4 → @8
1D4C 23647572txt "#dur"
227C 5054302E323553txt "PT0.25S"
2A0F 0A 1D000000 2200 0000map leaf, size 10: @1D → @22
344C 23647572txt "#dur"
394C 50543253txt "PT2S"
3E0F 0A 34000000 3900 0000map leaf, size 10: @34 → @39
488C 6D61785F776169 74txt "max_wait"
510F 0A 48000000 3E00 0000map leaf, size 10: @48 → @3E
5B8C 6465626F756E63 65txt "debounce"
640F 0A 5B000000 2A00 0000map leaf, size 10: @5B → @2A
6E2C 676Ftxt "go"
710F 0A 6E000000 1300 0000map leaf, size 10: @6E → @13
7B07 12 25000000 5100 0000 64000000 7100 0000map branch, size 18, depth 0 (root), slots 0, 2, 5: @51 → @64 → @71
8D3C 73646Btxt "sdk"
910F 0A 8D000000 7B00 0000map leaf, size 10: @8D → @7B
MemberValueResult
go.dirDirectory pathA dedicated directory for the generated packages. The plugin controls all its contents. A relative path is relative to the working directory of the process at open. A missing directory is created.
debounce#dur duration, PT0.25S when absentHow long definition commits must stay quiet before the plugin generates again.
max_wait#dur duration, PT2S when absentThe longest wait after the first definition commit.

Without the go member, the plugin publishes no Go files. Settings apply only to the database that is open. To change them, open the database again.

Runs

The plugin generates once at open, and again after commits that change definitions. Data commits do not start a run. A run reads all definitions from one committed snapshot.

Each successful render replaces the output directory's contents, including the startup run. See file ownership before choosing an output directory.

Call status to inspect these fields:

FieldContents
enabledWhether the plugin is enabled
pendingWhether generation remains
runsRun count
fingerprintGeneration model fingerprint
outputsOutputs, each with language, directory and file counts written and removed
diagnosticsDiagnostics with severity, code, definition type and path, pointer, member, language and message
failureLast run failure, with code and message; absent after a successful run

When to call sync

Generation runs automatically at open and after definition commits, according to the debounce settings. Opening the database or committing definitions can finish before the generated files are ready. Call sync before compiling or reading files that must include those definitions. Applications that can use the files after automatic generation finishes do not need an explicit sync.

The call requests a render immediately, bypassing debounce. It also rebuilds files when definitions have not changed. Its options accept only the common maxWait option. A timeout leaves the requested run active. Repeated waits after a timeout join that run. Inspect status when generation reports failed. Without an output language, sync validates the model inline and writes no files.

The Go packages

The plugin writes one Go package for each definition type that has definitions, in a subdirectory of go.dir named after the type. The generated names belong to your definitions, so they live in these packages and not in the stardust binding package. Each package also declares ModelFingerprint, which identifies the definitions that the packages were generated from.

  • schemas: one struct for each object schema, with a field for each property and json tags. An optional scalar property is a pointer with omitempty. A property that refers to another stored object schema with $ref is a pointer to its struct. Native time references use the time types described above. Other non-object references use any.
  • queries: for each stored query, a row struct, a parameter struct when the query has inputs, and a function that runs the query and returns iter.Seq2 over decoded rows. When the shape of the row is not known before the query runs, for example with resultMode positional, the row type is any.
  • mutations: for each stored mutation, a parameter struct and a function that runs the mutation and returns its commit information.
  • expressions: for each stored expression, a parameter struct and a function that evaluates it with the stored macros of the database and decodes its result.

The inputs of a query or mutation are its parameters and its other variables:

  • A declared parameter is a pointer with omitempty. A nil pointer keeps the stored default. A non-nil pointer supplies the value, including an empty collection. A supplied null parameter reports missing_parameter.
  • A variable that appears only in a predicate, such as ?max in [<= ?age ?max], is a required parameter and not a pointer.
  • Every other variable bound by the query clauses is an optional filter, a pointer with omitempty. A nil filter leaves the variable unbound, so it matches any value. A value binds the variable before the query runs, so the rows narrow to that value. An entity variable, such as ?item in [?item key ?key], is a *stardust.Link.

The query calculates aggregate and window result variables after the clauses run. These variables are not filter inputs.

For example, these definitions:

TRON717 B
Offset (hex)Bytes (hex)Meaning
00054 52 4F 4Emagic TRON
0044C 74797065txt "type"
0096C 737472696E67txt "string"
0100F 0A 04000000 0900 0000map leaf, size 10: @4 → @9
01A4C 74797065txt "type"
01F7C 696E7465676572txt "integer"
0270F 0A 1A000000 1F00 0000map leaf, size 10: @1A → @1F
0313C 616765txt "age"
0350F 0A 31000000 2700 0000map leaf, size 10: @31 → @27
03F4C 6E616D65txt "name"
0440F 0A 3F000000 1000 0000map leaf, size 10: @3F → @10
04E07 0E 00810000 3500 0000 44000000map branch, size 14, depth 0 (root), slots 8, 15: @35 → @44
05C4C 6E616D65txt "name"
0610E 0D 00 0100 010000 00 5C000000array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @5C
06EAC 70726F70657274 696573txt "properties"
0790F 0A 6E000000 4E00 0000map leaf, size 10: @6E → @4E
0838C 72657175697265 64txt "required"
08C0F 0A 83000000 6100 0000map leaf, size 10: @83 → @61
0964C 74797065txt "type"
09B6C 6F626A656374txt "object"
0A20F 0A 96000000 9B00 0000map leaf, size 10: @96 → @9B
0AC07 12 28040000 7900 0000 8C000000 A200 0000map branch, size 18, depth 0 (root), slots 3, 5, 10: @79 → @8C → @A2
0BE5C 3F6E616D65txt "?name"
0C40E 0D 00 0100 010000 00 BE000000array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @BE
0D13C 6D696Etxt "min"
0D502 12000000000000 00i64 18
0DE0F 0A D1000000 D500 0000map leaf, size 10: @D1 → @D5
0E82C 3F65txt "?e"
0EB4C 6E616D65txt "name"
0F05C 3F6E616D65txt "?name"
0F60E 15 00 0700 030000 00 E8000000 EB0000 00 F0000000array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @E8 → @EB → @F0
10B2C 3F65txt "?e"
Show all 72 nodes
Offset (hex)Bytes (hex)Meaning
10E3C 616765txt "age"
1124C 3F616765txt "?age"
1170E 15 00 0700 030000 00 0B010000 0E0100 00 12010000array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @10B → @10E → @112
12C2C 3E3Dtxt ">="
12F4C 3F616765txt "?age"
1344C 3F6D696Etxt "?min"
1390E 15 00 0700 030000 00 2C010000 2F0100 00 34010000array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @12C → @12F → @134
14E0E 15 00 0700 030000 00 F6000000 170100 00 39010000array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @F6 → @117 → @139
1635C 7768657265txt "where"
1690F 0A 63010000 4E01 0000map leaf, size 10: @163 → @14E
1734C 66696E64txt "find"
1780F 0A 73010000 C400 0000map leaf, size 10: @173 → @C4
182AC 706172616D6574 657273txt "parameters"
18D0F 0A 82010000 DE00 0000map leaf, size 10: @182 → @DE
19707 12 2A000000 6901 0000 78010000 8D01 0000map branch, size 18, depth 0 (root), slots 1, 3, 5: @169 → @178 → @18D
1A95C 3F6974656Dtxt "?item"
1AF0E 0D 00 0100 010000 00 A9010000array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @1A9
1BC5C 3F6974656Dtxt "?item"
1C23C 6B6579txt "key"
1C64C 3F6B6579txt "?key"
1CB0E 15 00 0700 030000 00 BC010000 C20100 00 C6010000array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @1BC → @1C2 → @1C6
1E00E 0D 00 0100 010000 00 CB010000array leaf, size 13, shift 0, bitmap 0001, count 1 (root): @1CB
1ED5C 7768657265txt "where"
1F30F 0A ED010000 E001 0000map leaf, size 10: @1ED → @1E0
1FD4C 66696E64txt "find"
2020F 0A FD010000 AF01 0000map leaf, size 10: @1FD → @1AF
20C07 0E 0A000000 F301 0000 02020000map branch, size 14, depth 0 (root), slots 1, 3: @1F3 → @202
21A1C 2Atxt "*"
21C2C 3F6Etxt "?n"
21F02 02000000000000 00i64 2
2280E 15 00 0700 030000 00 1A020000 1C0200 00 1F020000array leaf, size 21, shift 0, bitmap 0007, count 3 (root): @21A → @21C → @21F
23DDC 71756572696573 2F6974656D73txt "queries/items"
24B0F 0A 3D020000 0C02 0000map leaf, size 10: @23D → @20C
255EC 736368656D6173 2F706572736F6Etxt "schemas/person"
2640F 0A 55020000 AC00 0000map leaf, size 10: @255 → @AC
26E07 0E 00880000 4B02 0000 64020000map branch, size 14, depth 1, slots 11, 15: @24B → @264
27C14 12 657870726573 73696F6E732F646F 75626C65txt "expressions/double"
2900F 0A 7C020000 2802 0000map leaf, size 10: @27C → @228
29AEC 71756572696573 2F62795F616765txt "queries/by_age"
2A90F 0A 9A020000 9701 0000map leaf, size 10: @29A → @197
2B307 12 08030000 6E02 0000 90020000 A902 0000map branch, size 18, depth 0 (root), slots 3, 8, 9: @26E → @290 → @2A9
Offset (hex)Bytes (hex)Meaning

give schemas.Person, queries.ByAge, queries.Items and expressions.Double with their row and parameter structs. Import each package from the module that contains go.dir:

go404 B
for row, err := range queries.ByAge(db, queries.ByAgeParameters{}, stardust.RunOptions{}) {
	if err != nil {
		return err
	}
	fmt.Println(row.Name)
}

key := any("b")
for item, err := range queries.Items(db, queries.ItemsParameters{Key: &key}, stardust.RunOptions{}) {
	if err != nil {
		return err
	}
	fmt.Println(item.Item)
}

doubled, err := expressions.Double(db, expressions.DoubleParameters{N: 21})
Definition typeGo type
stringstring
integerint64
numberfloat64
booleanbool
object without a schemamap[string]any
arrayA slice of the item type
Native timestamptime.Time
Native durationtime.Duration
Entitystardust.Link
Not knownany

Names

A name comes from the path of its definition: people/by_name becomes PeopleByName. A name never depends on the other definitions, so a change to one definition never renames another. Each definition type has its own package, so a schema and a query can have the same name. When two definitions of one type claim one name, or one file name, or a definition claims a name that its package declares for itself, the run fails with the code Model_Errors. A run with naming errors publishes nothing and keeps the files that the plugin published before. status lists each error in diagnostics. Rename one of the definitions to correct it.

Go struct tags cannot represent some field names, including names with commas or backticks. Such names cause Invalid_Field. Rename the field to correct the error.

File ownership

The plugin owns the entire configured output directory. Every successful render removes its previous contents, including edited files, unrelated files and subdirectories. Keep application source, database files and other data outside this directory. Do not edit generated files: the next run replaces your changes. The plugin retains only its internal .stardust-sdk.lock file between runs.

The plugin renders and validates the package before it removes any files. Rendering errors leave the previous directory contents unchanged. A filesystem error during cleanup or writing can leave the directory empty or partly written. The next successful run clears that partial output and writes a fresh package. Use sync before a build and check its result.

Each file is written through an atomic rename. Rebuilding the whole directory is not atomic. A destination lock prevents concurrent publication and reports Destination_Busy when another publisher holds it. Use a separate output directory for each database, with no directory nested inside another output directory. Cleanup removes symlinks inside the output directory without following them. The output directory itself and the lock file must not be symlinks. Directory inspection is limited to 65,536 entries, 64 levels and 16 MiB of stored paths. An inspection failure leaves the previous contents unchanged.

Operations

OperationReturnsSummary
Sdk.Statusdocumentsdk.status() returns the SDK generator's owned status document.
Sdk.Syncnonesdk.sync(options) waits until the SDK generator published the definitions committed before the call.

Sdk.Status

go40 B
func (v Sdk) Status() (*Document, error)

Errors

StatusWhen
StatusUnavailablethe database does not enable the sdk plugin
StatusClosedthe database is closed
StatusOutOfMemorynative memory is exhausted

Notes

The document describes generation outputs, diagnostics and the last run: see runs.

Examples

Describe the SDK generator

go288 B
syncOptions, err := stardust.Parse([]byte(`{maxWait {#dur PT5S}}`), stardust.DUST)
if err != nil {
	return err
}
defer syncOptions.Close()
if err := db.Sdk().Sync(syncOptions); err != nil {
	return err
}

result, err := db.Sdk().Status()
if err != nil {
	return err
}
defer result.Close()
TRON191 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
044C 72756E73txt "runs"
0902 01000000000000 00i64 1
120F 0A 04000000 0900 0000map leaf, size 10: @4 → @9
1C7C 656E61626C6564txt "enabled"
2409bit true
250F 0A 1C000000 2400 0000map leaf, size 10: @1C → @24
2F7C 70656E64696E67txt "pending"
3701bit false
380F 0A 2F000000 3700 0000map leaf, size 10: @2F → @37
42BC 66696E67657270 72696E74txt "fingerprint"
4E14 47 736861323536 3A66306531373061 6335346236376230 6639613865343739 3439323261353836 6332316461323931 3062303736366463 6433666562343130 6234313662363165 38txt "sha256:f0e170ac54b67b0f9…"
970F 0A 42000000 4E00 0000map leaf, size 10: @42 → @4E
A107 16 280A0000 1200 0000 25000000 3800 0000 97000000map branch, size 22, depth 0 (root), slots 3, 5, 9, 11: @12 → @25 → @38 → @97

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

Sdk.Sync

go42 B
func (v Sdk) Sync(options *Document) error

Parameters

ParameterTypeOwnershipDefaultMeaning
options*DocumentBorrowed for the callAbsent selects operation defaultsThe borrowed call options, or none. Optional maxWait is a nonnegative native duration, such as {maxWait {#dur PT5S}}. Without it, or with zero, the call checks once.

Errors

StatusWhen
StatusInvalidArgumentoptions is not an object, or an option is invalid, duplicated or unsupported
StatusFailedthe plugin's work failed; status describes the failure
StatusTimeoutmaxWait passed before the work finished
StatusUnavailablethe database does not enable the sdk plugin
StatusClosedthe database is closed

Notes

Database close interrupts an active wait. See when to call sync.

Examples

Run the SDK generator and wait for it

go288 B
syncOptions, err := stardust.Parse([]byte(`{maxWait {#dur PT5S}}`), stardust.DUST)
if err != nil {
	return err
}
defer syncOptions.Close()
if err := db.Sdk().Sync(syncOptions); err != nil {
	return err
}

result, err := db.Sdk().Status()
if err != nil {
	return err
}
defer result.Close()
TRON191 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
044C 72756E73txt "runs"
0902 01000000000000 00i64 1
120F 0A 04000000 0900 0000map leaf, size 10: @4 → @9
1C7C 656E61626C6564txt "enabled"
2409bit true
250F 0A 1C000000 2400 0000map leaf, size 10: @1C → @24
2F7C 70656E64696E67txt "pending"
3701bit false
380F 0A 2F000000 3700 0000map leaf, size 10: @2F → @37
42BC 66696E67657270 72696E74txt "fingerprint"
4E14 47 736861323536 3A66306531373061 6335346236376230 6639613865343739 3439323261353836 6332316461323931 3062303736366463 6433666562343130 6234313662363165 38txt "sha256:f0e170ac54b67b0f9…"
970F 0A 42000000 4E00 0000map leaf, size 10: @42 → @4E
A107 16 280A0000 1200 0000 25000000 3800 0000 97000000map branch, size 22, depth 0 (root), slots 3, 5, 9, 11: @12 → @25 → @38 → @97

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release