# Programming Guides

Learn how to use Protocol Buffers in your projects.

##### [Language Guide (editions)](https://protobuf.dev/programming-guides/editions/)

Covers how to use the editions revisions of the Protocol Buffers language in your project.

##### [Language Guide (proto 2)](https://protobuf.dev/programming-guides/proto2/)

Covers how to use the proto2 revision of Protocol Buffers language in your project.

##### [Language Guide (proto 3)](https://protobuf.dev/programming-guides/proto3/)

Covers how to use the proto3 revision of the Protocol Buffers language in your project.

##### [Proto Limits](https://protobuf.dev/programming-guides/proto-limits/)

Covers the limits to number of supported elements in proto schemas.

##### [Style Guide](https://protobuf.dev/programming-guides/style/)

Provides direction for how best to structure your proto definitions.

##### [Enum Behavior](https://protobuf.dev/programming-guides/enum/)

Explains how enums currently work in Protocol Buffers vs. how they should work.

##### [Encoding](https://protobuf.dev/programming-guides/encoding/)

Explains how Protocol Buffers encodes data to files or to the wire.

##### [ProtoJSON Format](https://protobuf.dev/programming-guides/json/)

Describes the spec details of the canonical JSON representation for Protobuf messages.

##### [Techniques](https://protobuf.dev/programming-guides/techniques/)

Describes some commonly-used design patterns for dealing with Protocol Buffers.

##### [Third-Party Add-ons](https://protobuf.dev/programming-guides/addons/)

Links out to many open source projects that seek to add useful functionality on top of Protocol Buffers.

##### [Extension Declarations](https://protobuf.dev/programming-guides/extension_declarations/)

Describes in detail what extension declarations are, why we need them, and how we use them.

##### [Application Note: Field Presence](https://protobuf.dev/programming-guides/field_presence/)

Explains the various presence-tracking disciplines for protobuf fields. It also explains the behavior of explicit presence-tracking for singular proto3 fields with basic types.

##### [Symbol Visibility](https://protobuf.dev/programming-guides/symbol_visibility/)

Explains the terminology and functionality of visibility, introduced in edition 2024.

##### [Proto Serialization Is Not Canonical](https://protobuf.dev/programming-guides/serialization-not-canonical/)

Explains how serialization works and why it is not canonical.

##### [Deserializing Debug Proto Representations](https://protobuf.dev/programming-guides/deserialize-debug/)

How to log debugging information in Protocol Buffers.


# Ruby Reference

Reference documentation for working with protocol buffer classes in Ruby.

##### [Ruby Generated Code Guide](https://protobuf.dev/reference/ruby/ruby-generated/)

Describes the API of message objects that the protocol buffer compiler generates for any given protocol definition.


# Techniques

Describes some commonly-used design patterns for dealing with Protocol Buffers.

You can also send design and usage questions to the [Protocol Buffers discussion group](http://groups.google.com/group/protobuf).

## Common Filename Suffixes

It is fairly common to write messages to files in several different formats. We recommend using the following file extensions for these files.

Content| Extension
[Text Format](https://protobuf.dev/reference/protobuf/textformat-spec)| `.txtpb`
[Wire Format](https://protobuf.dev/programming-guides/encoding)| `.binpb`
[JSON Format](https://protobuf.dev/programming-guides/proto3#json)| `.json`

For Text Format specifically, `.textproto` is also fairly common, but we recommend `.txtpb` for its brevity.

## Streaming Multiple Messages

If you want to write multiple messages to a single file or stream, it is up to you to keep track of where one message ends and the next begins. The Protocol Buffer wire format is not self-delimiting, so protocol buffer parsers cannot determine where a message ends on their own. The easiest way to solve this problem is to write the size of each message before you write the message itself. When you read the messages back in, you read the size, then read the bytes into a separate buffer, then parse from that buffer. (If you want to avoid copying bytes to a separate buffer, check out the `CodedInputStream` class (in both C++ and Java) which can be told to limit reads to a certain number of bytes.)

## Large Data Sets

Protocol Buffers are not designed to handle large messages. As a general rule of thumb, if you are dealing in messages larger than a megabyte each, it may be time to consider an alternate strategy.

That said, Protocol Buffers are great for handling individual messages _within_ a large data set. Usually, large data sets are a collection of small pieces, where each small piece is structured data. Even though Protocol Buffers cannot handle the entire set at once, using Protocol Buffers to encode each piece greatly simplifies your problem: now all you need is to handle a set of byte strings rather than a set of structures.

Protocol Buffers do not include any built-in support for large data sets because different situations call for different solutions. Sometimes a simple list of records will do while other times you want something more like a database. Each solution should be developed as a separate library, so that only those who need it need pay the costs.

## Self-describing Messages

Protocol Buffers do not contain descriptions of their own types. Thus, given only a raw message without the corresponding `.proto` file defining its type, it is difficult to extract any useful data.

However, the contents of a .proto file can itself be represented using protocol buffers. The file `src/google/protobuf/descriptor.proto` in the source code package defines the message types involved. `protoc` can output a `FileDescriptorSet`—which represents a set of .proto files—using the `--descriptor_set_out` option. With this, you can define a self-describing protocol message like so:

    syntax = "proto3";

    import "google/protobuf/any.proto";
    import "google/protobuf/descriptor.proto";

    message SelfDescribingMessage {
      // Set of FileDescriptorProtos which describe the type and its dependencies.
      google.protobuf.FileDescriptorSet descriptor_set = 1;

      // The message and its type, encoded as an Any message.
      google.protobuf.Any message = 2;

By using classes like `DynamicMessage` (available in C++ and Java), you can then write tools which can manipulate `SelfDescribingMessage`s.

All that said, the reason that this functionality is not included in the Protocol Buffer library is because we have never had a use for it inside Google.

This technique requires support for dynamic messages using descriptors. Check that your platforms support this feature before using self-describing messages.


# Protocol Buffers Language Specification (Proto2 Syntax)

Language specification reference for the proto2 syntax and its relationship to Protobuf Editions.

The syntax is specified using [Extended Backus-Naur Form (EBNF)](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form):

    |   alternation
    ()  grouping
    []  option (zero or one time)
    {}  repetition (any number of times)

For more information about using proto2, see the [language guide](https://protobuf.dev/programming-guides/proto2).

## Lexical Elements

### Letters and Digits

    letter = "A" ... "Z" | "a" ... "z"
    capitalLetter =  "A" ... "Z"
    decimalDigit = "0" ... "9"
    octalDigit   = "0" ... "7"
    hexDigit     = "0" ... "9" | "A" ... "F" | "a" ... "f"

### Identifiers

    ident = letter { letter | decimalDigit | "_" }
    fullIdent = ident { "." ident }
    messageName = ident
    enumName = ident
    fieldName = ident
    oneofName = ident
    mapName = ident
    serviceName = ident
    rpcName = ident
    streamName = ident
    messageType = [ "." ] { ident "." } messageName
    enumType = [ "." ] { ident "." } enumName
    groupName = capitalLetter { letter | decimalDigit | "_" }

### Integer Literals

    intLit     = decimalLit | octalLit | hexLit
    decimalLit = [-] ( "1" ... "9" ) { decimalDigit }
    octalLit   = [-] "0" { octalDigit }
    hexLit     = [-] "0" ( "x" | "X" ) hexDigit { hexDigit }

### Floating-point Literals

    floatLit = [-] ( decimals "." [ decimals ] [ exponent ] | decimals exponent | "."decimals [ exponent ] ) | "inf" | "nan"
    decimals  = [-] decimalDigit { decimalDigit }
    exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals

### Boolean

    boolLit = "true" | "false"

### String Literals

    strLit = strLitSingle { strLitSingle }
    strLitSingle = ( "'" { charValue } "'" ) | ( '"' { charValue } '"' )
    charValue = hexEscape | octEscape | charEscape | unicodeEscape | unicodeLongEscape | /[^\0\n\\]/
    hexEscape = '\' ( "x" | "X" ) hexDigit [ hexDigit ]
    octEscape = '\' octalDigit [ octalDigit [ octalDigit ] ]
    charEscape = '\' ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | '\' | "'" | '"' )
    unicodeEscape = '\' "u" hexDigit hexDigit hexDigit hexDigit
    unicodeLongEscape = '\' "U" ( "000" hexDigit hexDigit hexDigit hexDigit hexDigit |
                                  "0010" hexDigit hexDigit hexDigit hexDigit

### EmptyStatement

    emptyStatement = ";"

### Constant

    constant = fullIdent | ( [ "-" | "+" ] intLit ) | ( [ "-" | "+" ] floatLit ) |
                    strLit | boolLit | MessageValue

`MessageValue` is defined in the [Text Format Language Specification](https://protobuf.dev/reference/protobuf/textformat-spec#fields).

## Syntax

The syntax statement is used to define the protobuf version. If `syntax` is omitted, the protocol compiler will use `proto2`. For the sake of clarity, it’s recommended to always explicitly include a `syntax` statement in your `.proto` files.

    syntax = "syntax" "=" ("'" "proto2" "'" | '"' "proto2" '"') ";"

## Import Statement

The import statement is used to import another .proto’s definitions.

    import = "import" [ "weak" | "public" ] strLit ";"

Example:

    import public "other.proto";

## Package

The package specifier can be used to prevent name clashes between protocol message types.

    package = "package" fullIdent ";"

Example:

    package foo.bar;

## Option

Options can be used in proto files, messages, enums and services. An option can be a protobuf defined option or a custom option. For more information, see [Options](https://protobuf.dev/programming-guides/proto2#options) in the language guide.

    option = "option" optionName  "=" constant ";"
    optionName = ( ident | bracedFullIdent ) { "." ( ident | bracedFullIdent ) }
    bracedFullIdent = "(" ["."] fullIdent ")"

For examples:

    option java_package = "com.example.foo";

## Fields

Fields are the basic elements of a protocol buffer message. Fields can be normal fields, group fields, oneof fields, or map fields. A field has a type, name, and field number. In proto2, fields also have a label (`required`, `optional`, or `repeated`).

    label = "required" | "optional" | "repeated"
    type = "double" | "float" | "int32" | "int64" | "uint32" | "uint64"
          | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64"
          | "bool" | "string" | "bytes" | messageType | enumType
    fieldNumber = intLit;

### Normal field

Each field has a type, name and field number. It may have field options. Note that labels are optional only for oneof fields.

    field = [ label ] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
    fieldOptions = fieldOption { ","  fieldOption }
    fieldOption = optionName "=" constant

Examples:

    optional foo.bar nested_message = 2;
    repeated int32 samples = 4 [packed=true];

### Group field

**Note that this feature is deprecated and should not be used when creating new message types. Use nested message types instead.**

Groups are one way to nest information in message definitions. The group name must begin with capital letter.

    group = label "group" groupName "=" fieldNumber messageBody

Example:

    repeated group Result = 1 {
        required string url = 1;
        optional string title = 2;
        repeated string snippets = 3;

### Oneof and oneof field

A oneof consists of oneof fields and a oneof name. Oneof fields do not have labels.

    oneof = "oneof" oneofName "{" { option | oneofField } "}"
    oneofField = type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"

Example:

    oneof foo {
        string name = 4;
        SubMessage sub_message = 9;

### Map field

A map field has a key type, value type, name, and field number. The key type can be any integral or string type. Note, the key type may not be an enum.

    mapField = "map" "<" keyType "," type ">" mapName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
    keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" |
              "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"

Example:

    map<string, Project> projects = 3;

## Extensions and Reserved

Extensions and reserved are message elements that declare a range of field numbers or field names.

### Extensions

Extensions declare that a range of field numbers in a message are available for third-party extensions. Other people can declare new fields for your message type with those numeric tags in their own .proto files without having to edit the original file.

    extensions = "extensions" ranges ";"
    ranges = range { "," range }
    range =  intLit [ "to" ( intLit | "max" ) ]

Examples:

    extensions 100 to 199;
    extensions 4, 20 to max;

For more on this topic, see [Extension Declarations](https://protobuf.dev/programming-guides/extension_declarations).

### Reserved

Reserved declares a range of field numbers or field names in a message that can not be used.

    reserved = "reserved" ( ranges | strFieldNames ) ";"
    strFieldNames = strFieldName { "," strFieldName }
    strFieldName = "'" fieldName "'" | '"' fieldName '"'

Examples:

    reserved 2, 15, 9 to 11;
    reserved "foo", "bar";

## Top Level definitions

### Enum definition

The enum definition consists of a name and an enum body. The enum body can have options, enum fields, and reserved statements.

    enum = "enum" enumName enumBody
    enumBody = "{" { option | enumField | emptyStatement | reserved } "}"
    enumField = ident "=" [ "-" ] intLit [ "[" enumValueOption { ","  enumValueOption } "]" ]";"
    enumValueOption = optionName "=" constant

Example:

    enum EnumAllowingAlias {
      option allow_alias = true;
      EAA_UNSPECIFIED = 0;
      EAA_STARTED = 1;
      EAA_RUNNING = 2 [(custom_option) = "hello world"];

### Message definition

A message consists of a message name and a message body. The message body can have fields, nested enum definitions, nested message definitions, extend statements, extensions, groups, options, oneofs, map fields, and reserved statements. A message cannot contain two fields with the same name in the same message schema.

    message = "message" messageName messageBody
    messageBody = "{" { field | enum | message | extend | extensions | group |
    option | oneof | mapField | reserved | emptyStatement } "}"

Example:

    message Outer {
      option (my_option).a = true;
      message Inner {   // Level 2
        required int64 ival = 1;
      map<int32, string> my_map = 2;
      extensions 20 to 30;

None of the entities declared inside a message may have conflicting names. All of the following are prohibited:

    message MyMessage {
      optional string foo = 1;
      message foo {}

    message MyMessage {
      optional string foo = 1;
      oneof foo {
        string bar = 2;

    message MyMessage {
      optional string foo = 1;
      extend Extendable {
        optional string foo = 2;

    message MyMessage {
      optional string foo = 1;
      enum E {
        foo = 0;

### Extend

If a message in the same or imported .proto file has reserved a range for extensions, the message can be extended.

    extend = "extend" messageType "{" {field | group} "}"

Example:

    extend Foo {
      optional int32 bar = 126;

### Service definition

    service = "service" serviceName "{" { option | rpc | emptyStatement } "}"
    rpc = "rpc" rpcName "(" [ "stream" ] messageType ")" "returns" "(" [ "stream" ]
    messageType ")" (( "{" { option | emptyStatement } "}" ) | ";" )

Example:

    service SearchService {
      rpc Search (SearchRequest) returns (SearchResponse);

## Proto file

    proto = [syntax] { import | package | option | topLevelDef | emptyStatement }
    topLevelDef = message | enum | extend | service

An example `.proto` file:

    syntax = "proto2";
    import public "other.proto";
    option java_package = "com.example.foo";
    enum EnumAllowingAlias {
      option allow_alias = true;
      EAA_UNSPECIFIED = 0;
      EAA_STARTED = 1;
      EAA_RUNNING = 1;
      EAA_FINISHED = 2 [(custom_option) = "hello world"];
    message Outer {
      option (my_option).a = true;
      message Inner {   // Level 2
        required int64 ival = 1;
      repeated Inner inner_message = 2;
      optional EnumAllowingAlias enum_field = 3;
      map<int32, string> my_map = 4;
      extensions 20 to 30;
    message Foo {
      optional group GroupMessage = 1 {
        optional bool a = 1;


# Avoid Cargo Culting

Avoid using features where they are not needed.

Do not [cargo cult](https://en.wikipedia.org/wiki/Cargo_cult_programming) settings in proto files. If you are creating a new proto file based on existing schema definitions, don’t apply option settings except for those that you understand the need for.

## Best Practices Specific to Editions

Avoid applying [editions features](https://protobuf.dev/editions/features) except when they’re actually necessary. Features in `.proto` files signal the use of either experimental future behaviors or deprecated past behaviors. Best practices for the latest edition will always be the default. New proto schema definition content should remain feature-free, except if you want to early-adopt a feature for future behavior that’s being rolled out.

Copying-forward feature settings without understanding why they are set can lead to unexpected behaviors in your code.


# Protocol Buffers Edition 2023 Language Specification

Language specification reference for edition 2023 of the Protocol Buffers language.

The syntax is specified using [Extended Backus-Naur Form (EBNF)](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form):

    |   alternation
    ()  grouping
    []  option (zero or one time)
    {}  repetition (any number of times)

## Lexical Elements

### Letters and Digits

    letter = "A" ... "Z" | "a" ... "z"
    capitalLetter =  "A" ... "Z"
    decimalDigit = "0" ... "9"
    octalDigit   = "0" ... "7"
    hexDigit     = "0" ... "9" | "A" ... "F" | "a" ... "f"

### Identifiers

    ident = letter { letter | decimalDigit | "_" }
    fullIdent = ident { "." ident }
    messageName = ident
    enumName = ident
    fieldName = ident
    oneofName = ident
    mapName = ident
    serviceName = ident
    rpcName = ident
    streamName = ident
    messageType = [ "." ] { ident "." } messageName
    enumType = [ "." ] { ident "." } enumName
    groupName = capitalLetter { letter | decimalDigit | "_" }

### Integer Literals

    intLit     = decimalLit | octalLit | hexLit
    decimalLit = [-] ( "1" ... "9" ) { decimalDigit }
    octalLit   = [-] "0" { octalDigit }
    hexLit     = [-] "0" ( "x" | "X" ) hexDigit { hexDigit }

### Floating-point Literals

    floatLit = [-] ( decimals "." [ decimals ] [ exponent ] | decimals exponent | "."decimals [ exponent ] ) | "inf" | "nan"
    decimals  = [-] decimalDigit { decimalDigit }
    exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals

### Boolean

    boolLit = "true" | "false"

### String Literals

    strLit = strLitSingle { strLitSingle }
    strLitSingle = ( "'" { charValue } "'" ) | ( '"' { charValue } '"' )
    charValue = hexEscape | octEscape | charEscape | unicodeEscape | unicodeLongEscape | /[^\0\n\\]/
    hexEscape = '\' ( "x" | "X" ) hexDigit [ hexDigit ]
    octEscape = '\' octalDigit [ octalDigit [ octalDigit ] ]
    charEscape = '\' ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | '\' | "'" | '"' )
    unicodeEscape = '\' "u" hexDigit hexDigit hexDigit hexDigit
    unicodeLongEscape = '\' "U" ( "000" hexDigit hexDigit hexDigit hexDigit hexDigit |
                                  "0010" hexDigit hexDigit hexDigit hexDigit

### EmptyStatement

    emptyStatement = ";"

### Constant

    constant = fullIdent | ( [ "-" | "+" ] intLit ) | ( [ "-" | "+" ] floatLit ) |
                    strLit | boolLit | MessageValue

`MessageValue` is defined in the [Text Format Language Specification](https://protobuf.dev/reference/protobuf/textformat-spec#fields).

## Edition

The edition statement replaces the legacy `syntax` keyword, and is used to define the edition that this file is using.

    edition = "edition" "=" [ ( "'" decimalLit "'" ) | ( '"' decimalLit '"' ) ] ";"

## Import Statement

The import statement is used to import another .proto’s definitions.

    import = "import" [ "weak" | "public" ] strLit ";"

Example:

    import public "other.proto";

## Package

The package specifier can be used to prevent name clashes between protocol message types.

    package = "package" fullIdent ";"

Example:

    package foo.bar;

## Option

Options can be used in proto files, messages, enums and services. An option can be a protobuf defined option or a custom option. For more information, see [Options](https://protobuf.dev/programming-guides/proto2#options) in the language guide. Options are also be used to control [Feature Settings](https://protobuf.dev/editions/features).

    option = "option" optionName  "=" constant ";"
    optionName = ( ident | "(" ["."] fullIdent ")" )

For examples:

    option java_package = "com.example.foo";
    option features.enum_type = CLOSED;

## Fields

Fields are the basic elements of a protocol buffer message. Fields can be normal fields, group fields, oneof fields, or map fields. A field has a label, type and field number.

    label = [ "repeated" ]
    type = "double" | "float" | "int32" | "int64" | "uint32" | "uint64"
          | "sint32" | "sint64" | "fixed32" | "fixed64" | "sfixed32" | "sfixed64"
          | "bool" | "string" | "bytes" | messageType | enumType
    fieldNumber = intLit;

### Normal field

Each field has a label, type, name, and field number. It may have field options.

    field = [label] type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
    fieldOptions = fieldOption { ","  fieldOption }
    fieldOption = optionName "=" constant

Examples:

    foo.bar nested_message = 2;
    repeated int32 samples = 4 [packed=true];

### Oneof and oneof field

A oneof consists of oneof fields and a oneof name. Oneof fields do not have labels.

    oneof = "oneof" oneofName "{" { option | oneofField } "}"
    oneofField = type fieldName "=" fieldNumber [ "[" fieldOptions "]" ] ";"

Example:

    oneof foo {
        string name = 4;
        SubMessage sub_message = 9;

### Map field

A map field has a key type, value type, name, and field number. The key type can be any integral or string type. Note, the key type may not be an enum.

    mapField = "map" "<" keyType "," type ">" mapName "=" fieldNumber [ "[" fieldOptions "]" ] ";"
    keyType = "int32" | "int64" | "uint32" | "uint64" | "sint32" | "sint64" |
              "fixed32" | "fixed64" | "sfixed32" | "sfixed64" | "bool" | "string"

Example:

    map<string, Project> projects = 3;

## Extensions and Reserved

Extensions and reserved are message elements that declare a range of field numbers or field names.

### Extensions

Extensions declare that a range of field numbers in a message are available for third-party extensions. Other people can declare new fields for your message type with those numeric tags in their own .proto files without having to edit the original file.

    extensions = "extensions" ranges ";"
    ranges = range { "," range }
    range =  intLit [ "to" ( intLit | "max" ) ]

Examples:

    extensions 100 to 199;
    extensions 4, 20 to max;

### Reserved

Reserved declares a range of field numbers or names in a message or enum that can’t be used.

    reserved = "reserved" ( ranges | reservedIdent ) ";"
    fieldNames = fieldName { "," fieldName }

Examples:

    reserved 2, 15, 9 to 11;
    reserved foo, bar;

## Top Level definitions

### Enum definition

The enum definition consists of a name and an enum body. The enum body can have options, enum fields, and reserved statements.

    enum = "enum" enumName enumBody
    enumBody = "{" { option | enumField | emptyStatement | reserved } "}"
    enumField = fieldName "=" [ "-" ] intLit [ "[" enumValueOption { ","  enumValueOption } "]" ]";"
    enumValueOption = optionName "=" constant

Example:

    enum EnumAllowingAlias {
      option allow_alias = true;
      EAA_UNSPECIFIED = 0;
      EAA_STARTED = 1;
      EAA_RUNNING = 2 [(custom_option) = "hello world"];

### Message definition

A message consists of a message name and a message body. The message body can have fields, nested enum definitions, nested message definitions, extend statements, extensions, groups, options, oneofs, map fields, and reserved statements. A message cannot contain two fields with the same name in the same message schema.

    message = "message" messageName messageBody
    messageBody = "{" { field | enum | message | extend | extensions | group |
    option | oneof | mapField | reserved | emptyStatement } "}"

Example:

    message Outer {
      option (my_option).a = true;
      message Inner {   // Level 2
        required int64 ival = 1;
      map<int32, string> my_map = 2;
      extensions 20 to 30;

None of the entities declared inside a message may have conflicting names. All of the following are prohibited:

    message MyMessage {
      string foo = 1;
      message foo {}

    message MyMessage {
      string foo = 1;
      oneof foo {
        string bar = 2;

    message MyMessage {
      string foo = 1;
      extend Extendable {
        string foo = 2;

    message MyMessage {
      string foo = 1;
      enum E {
        foo = 0;

### Extend

If a message in the same or imported .proto file has reserved a range for extensions, the message can be extended.

    extend = "extend" messageType "{" {field | group} "}"

Example:

    extend Foo {
      int32 bar = 126;

### Service definition

    service = "service" serviceName "{" { option | rpc | emptyStatement } "}"
    rpc = "rpc" rpcName "(" [ "stream" ] messageType ")" "returns" "(" [ "stream" ]
    messageType ")" (( "{" { option | emptyStatement } "}" ) | ";" )

Example:

    service SearchService {
      rpc Search (SearchRequest) returns (SearchResponse);

## Proto file

    proto = [syntax] { import | package | option | topLevelDef | emptyStatement }
    topLevelDef = message | enum | extend | service

An example .proto file:

    edition = "2023";
    import public "other.proto";
    option java_package = "com.example.foo";
    enum EnumAllowingAlias {
      option allow_alias = true;
      EAA_UNSPECIFIED = 0;
      EAA_STARTED = 1;
      EAA_RUNNING = 1;
      EAA_FINISHED = 2 [(custom_option) = "hello world"];
    message Outer {
      option (my_option).a = true;
      message Inner {   // Level 2
        int64 ival = 1 [features.field_presence = LEGACY_REQUIRED];
      repeated Inner inner_message = 2;
      EnumAllowingAlias enum_field = 3;
      map<int32, string> my_map = 4;
      extensions 20 to 30;
      reserved reserved_field;
    message Foo {
      message GroupMessage {
        bool a = 1;
      GroupMessage groupmessage = [features.message_encoding = DELIMITED];


# Abseil Support

The C++ implementation of Protocol Buffers has an explicit dependency on Abseil.

In [version 22.x](https://protobuf.dev/news/v22#abseil-dep), C++ protobuf added an explicit dependency on Abseil.

## Bazel Support

If you are using Bazel, to determine the version of Abseil that your protobuf version supports, you can use the `bazel mod` command:

    $ bazel mod deps abseil-cpp --enable_bzlmod
    <root> (protobuf@30.0-dev)
    └───abseil-cpp@20240722.0
        ├───bazel_skylib@1.7.1
        ├───googletest@1.15.2
        └───platforms@0.0.10

`bazel mod graph` produces the full output:

    $ bazel mod graph --enable_bzlmod
    <root> (protobuf@30.0-dev)
    ├───abseil-cpp@20240722.0
    │   ├───bazel_skylib@1.7.1 (*)
    │   ├───googletest@1.15.2 (*)
    │   └───platforms@0.0.10 (*)
    ├───bazel_features@1.18.0
    │   └───bazel_skylib@1.7.1 (*)
    ├───bazel_skylib@1.7.1
    │   ├───platforms@0.0.10 (*)
    │   └───rules_license@1.0.0 (*)
    ├───googletest@1.15.2
    │   ├───abseil-cpp@20240722.0 (*)
    │   ├───platforms@0.0.10 (*)
    │   └───re2@2024-07-02
    ...

## CMake Support

Our CMake support is best-effort compared to Bazel. To check for support, try the following steps:

  1. Run the `cmake .` command.
  2. Open `_deps/absl-src/CMakeLists.txt`.

Look for the following line:

    project(absl LANGUAGES CXX VERSION 20240722)
    set(ABSL_SOVERSION "2407.0.0")
    include(CTest)


# Python Reference

Reference documentation for working with protocol buffer classes in Python.

##### [Python Generated Code Guide](https://protobuf.dev/reference/python/python-generated/)

Describes exactly what Python definitions the protocol buffer compiler generates for any given protocol definition.

##### [Python Comparison](https://protobuf.dev/reference/python/python-comparison/)

Describes how Python compares objects.

##### [Python API](https://googleapis.dev/python/protobuf/latest/)


# Third-Party Add-ons

Links out to many open source projects that seek to add useful functionality on top of Protocol Buffers.

Many open source projects seek to add useful functionality on top of Protocol Buffers. For a list of links to projects we know about, see the [third-party add-ons wiki page](https://github.com/protocolbuffers/protobuf/blob/master/docs/third_party.md).


# Go Reference

Reference documentation for working with protocol buffer classes in Go

##### [Go Generated Code Guide (Open)](https://protobuf.dev/reference/go/go-generated/)

Describes exactly what Go code the protocol buffer compiler generates for any given protocol definition.

##### [Go Generated Code Guide (Opaque)](https://protobuf.dev/reference/go/go-generated-opaque/)

Describes exactly what Go code the protocol buffer compiler generates for any given protocol definition.

##### [Go FAQ](https://protobuf.dev/reference/go/faq/)

A list of frequently asked questions about implementing protocol buffers in Go, with answer for each.

##### [Go Size Semantics](https://protobuf.dev/reference/go/size/)

Explains how (not) to use proto.Size

##### [Go API](https://pkg.go.dev/google.golang.org/protobuf/proto)

##### [Go Opaque API Migration](https://protobuf.dev/reference/go/opaque-migration/)

Describes the automated migration to the Opaque API.

##### [Go Opaque API: Manual Migration](https://protobuf.dev/reference/go/opaque-migration-manual/)

Describes a manual migration to the Opaque API.

##### [Go Opaque API FAQ](https://protobuf.dev/reference/go/opaque-faq/)

A list of frequently asked questions about the Opaque API.


# PHP Reference

Reference documentation for working with protocol buffer classes in PHP.

##### [PHP Generated Code Guide](https://protobuf.dev/reference/php/php-generated/)

Describes the PHP code that the protocol buffer compiler generates for any given protocol definition.

##### [PHP API](https://protobuf.dev/reference/php/api-docs/)


# Protobuf Editions Overview

An overview of the Protobuf Editions functionality.

Protobuf Editions replace the proto2 and proto3 designations that we have used for Protocol Buffers. Instead of adding `syntax = "proto2"` or `syntax = "proto3"` at the top of proto definition files, you use an edition number, such as `edition = "2024"`, to specify the default behaviors your file will have. Editions enable the language to evolve incrementally over time.

Instead of the hardcoded behaviors that older versions have had, editions represent a collection of [features](https://protobuf.dev/editions/features) with a default value (behavior) per feature. Features are options on a file, message, field, enum, and so on, that specify the behavior of protoc, the code generators, and protobuf runtimes. You can explicitly override a behavior at those different levels (file, message, field, …) when your needs don’t match the default behavior for the edition you’ve selected. You can also override your overrides. The section later in this topic on lexical scoping goes into more detail on that.

**NOTE:** The latest released edition is 2024.

## Lifecycle of a Feature

Editions provide the fundamental increments for the lifecycle of a feature. Features have an expected lifecycle: introducing it, changing its default behavior, deprecating it, and then removing it. For example:

  1. Edition 2031 creates `feature.amazing_new_feature` with a default value of `false`. This value maintains the same behavior as all earlier editions. That is, it defaults to no impact. Not all new features will default to the no-op option, but for the sake of this example, `amazing_new_feature` does.

  2. Developers update their .proto files to `edition = "2031"`.

  3. A later edition, such as edition 2033, switches the default of `feature.amazing_new_feature` from `false` to `true`. This is the desired behavior for all protos, and the reason that the protobuf team created the feature.

Using the Prototiller tool to migrate earlier versions of proto files to edition 2033 adds explicit `feature.amazing_new_feature = false` entries as needed to continue to retain the previous behavior. Developers remove these newly-added settings when they want the new behavior to apply to their .proto files.

  4. At some point, `feature.amazing_new_feature` is marked deprecated in an edition and removed in a later one.

When a feature is removed, the code generators for that behavior and the runtime libraries that support it may also be removed. The timelines will be generous, though. Following the example in the earlier steps of the lifecycle, the deprecation might happen in edition 2034 but not be removed until edition 2036, roughly two years later. Removing a feature will always initiate a major version bump.

You will have the full window of the Google migration plus the deprecation window to upgrade your code.

The preceding lifecycle example used boolean values for the features, but features may also use enums. For example, `features.field_presence` has values `LEGACY_REQUIRED`, `EXPLICIT`, and `IMPLICIT.`

## Migrating to Protobuf Editions

Editions won’t break existing binaries and don’t change a message’s binary, text, or JSON serialization format. Edition 2023 was as minimally disruptive as possible. It established the baseline and combined proto2 and proto3 definitions into a new single definition format.

As more editions are released, default behaviors for features may change. You can have Prototiller do a no-op transformation of your .proto file or you can choose to accept some or all of the new behaviors. Editions are planned to be released roughly once a year.

### Proto2 to Editions

This section shows a proto2 file, and what it might look like after running the Prototiller tool to change the definition files to use Protobuf Editions syntax.

#### Proto2 Syntax

    // proto2 file
    syntax = "proto2";

    package com.example;

    message Player {
      // in proto2, optional fields have explicit presence
      optional string name = 1 [default = "N/A"];
      // proto2 still supports the problematic "required" field rule
      required int32 id = 2;
      // in proto2 this is not packed by default
      repeated int32 scores = 3;

      enum Handed {
        HANDED_UNSPECIFIED = 0;
        HANDED_LEFT = 1;
        HANDED_RIGHT = 2;
        HANDED_AMBIDEXTROUS = 3;

      // in proto2 enums are closed
      optional Handed handed = 4;

      reserved "gender";

#### Editions Syntax

    // Edition version of proto2 file
    edition = "2024";

    package com.example;

    option features.utf8_validation = NONE;
    option features.enforce_naming_style = STYLE_LEGACY;
    option features.default_symbol_visibility = EXPORT_ALL;

    // Sets the default behavior for C++ strings
    option features.(pb.cpp).string_type = STRING;

    message Player {
      // fields have explicit presence, so no explicit setting needed
      string name = 1 [default = "N/A"];
      // to match the proto2 behavior, LEGACY_REQUIRED is set at the field level
      int32 id = 2 [features.field_presence = LEGACY_REQUIRED];
      // to match the proto2 behavior, EXPANDED is set at the field level
      repeated int32 scores = 3 [features.repeated_field_encoding = EXPANDED];

      export enum Handed {
        // this overrides the default editions behavior, which is OPEN
        option features.enum_type = CLOSED;
        HANDED_UNSPECIFIED = 0;
        HANDED_LEFT = 1;
        HANDED_RIGHT = 2;
        HANDED_AMBIDEXTROUS = 3;

      Handed handed = 4;

      reserved gender;

### Proto3 to Editions

This section shows a proto3 file, and what it might look like after running the Prototiller tool to change the definition files to use Protobuf Editions syntax.

#### Proto3 Syntax

    // proto3 file
    syntax = "proto3";

    package com.example;

    message Player {
      // in proto3, optional fields have explicit presence
      optional string name = 1;
      // in proto3 no specified field rule defaults to implicit presence
      int32 id = 2;
      // in proto3 this is packed by default
      repeated int32 scores = 3;

      enum Handed {
        HANDED_UNSPECIFIED = 0;
        HANDED_LEFT = 1;
        HANDED_RIGHT = 2;
        HANDED_AMBIDEXTROUS = 3;

      // in proto3 enums are open
      optional Handed handed = 4;

      reserved "gender";

#### Editions Syntax

    // Editions version of proto3 file
    edition = "2024";

    package com.example;

    option features.utf8_validation = NONE;
    option features.enforce_naming_style = STYLE_LEGACY;
    option features.default_symbol_visibility = EXPORT_ALL;

    // Sets the default behavior for C++ strings
    option features.(pb.cpp).string_type = STRING;

    message Player {
      // fields have explicit presence, so no explicit setting needed
      string name = 1 [default = "N/A"];
      // to match the proto3 behavior, IMPLICIT is set at the field level
      int32 id = 2 [features.field_presence = IMPLICIT];
      // PACKED is the default state, and is provided just for illustration
      repeated int32 scores = 3 [features.repeated_field_encoding = PACKED];

      export enum Handed {
        HANDED_UNSPECIFIED = 0;
        HANDED_LEFT = 1;
        HANDED_RIGHT = 2;
        HANDED_AMBIDEXTROUS = 3;

      Handed handed = 4;

      reserved gender;

### Lexical Scoping

Editions syntax supports lexical scoping, with a per-feature list of allowed targets. For example, in Edition 2023, features can be specified at only the file level or the lowest level of granularity. The implementation of lexical scoping enables you to set the default behavior for a feature across an entire file, and then override that behavior at the message, field, enum, enum value, oneof, service, or method level. Settings made at a higher level (file, message) apply when no setting is made within the same scope (field, enum value). Any features not explicitly set conform to the behavior defined in the edition version used for the .proto file.

The following code sample shows some features being set at the file, field, and enum level.

    edition = "2024";

    option features.enum_type = CLOSED;

    message Person {
      string name = 1;
      int32 id = 2 [features.field_presence = IMPLICIT];

      enum Pay_Type {
        PAY_TYPE_UNSPECIFIED = 1;
        PAY_TYPE_SALARY = 2;
        PAY_TYPE_HOURLY = 3;

      enum Employment {
        option features.enum_type = OPEN;
        EMPLOYMENT_UNSPECIFIED = 0;
        EMPLOYMENT_FULLTIME = 1;
        EMPLOYMENT_PARTTIME = 2;
      Employment employment = 4;

In the preceding example, the presence feature is set to `IMPLICIT`; it would default to `EXPLICIT` if it wasn’t set. The `Pay_Type` `enum` will be `CLOSED`, as it applies the file-level setting. The `Employment` `enum`, though, will be `OPEN`, as it is set within the enum.

### Prototiller

When the Prototiller tool is launched, we will provide both a migration guide and migration tooling to ease the migration to and between editions. The tool will enable you to:

  * convert proto2 and proto3 definition files to the new editions syntax, at scale
  * migrate files from one edition to another
  * manipulate proto files in other ways

### Backward Compatibility

We are building Protobuf Editions to be as minimally disruptive as possible. For example, you can import proto2 and proto3 definitions into editions-based definition files, and vice versa:

    // file myproject/foo.proto
    syntax = "proto2";

    enum Employment {
      EMPLOYMENT_UNSPECIFIED = 0;
      EMPLOYMENT_FULLTIME = 1;
      EMPLOYMENT_PARTTIME = 2;

    // file myproject/edition.proto
    edition = "2024";

    import "myproject/foo.proto";

While the generated code changes when you move from proto2 or proto3 to editions, the wire format does not. You’ll still be able to access proto2 and proto3 data files or file streams using your editions-syntax proto definitions.

### Grammar Changes

There are some grammar changes in editions compared to proto2 and proto3.

#### Syntax Description

Instead of the `syntax` element, you use an `edition` element:

    syntax = "proto2";
    syntax = "proto3";
    edition = "2028";

#### Reserved Names

You no longer put field names and enum value names in quotation marks when reserving them:

    reserved foo, bar;

#### Group Syntax

Group syntax, available in proto2, is removed in editions. The special wire-format that groups used is still available by using `DELIMITED` message encoding.

#### Required Label

The `required` label, available only in proto2, is unavailable in editions. The underlying functionality is still available by using `features.field_presence=LEGACY_REQUIRED`.

#### `import option`

Edition 2024 added support for option imports using the syntax `import option`.

Option imports must come after any other `import` statements.

Unlike normal `import` statements, `import option` only imports custom options defined in a `.proto` file, without importing other symbols.

This means that messages and enums are excluded from the option import. In the following example, the `Bar` message cannot be used as a field type in `foo.proto`, but options with type `Bar` can still be set.

    // bar.proto
    edition = "2024";

    import "google/protobuf/descriptor.proto";

    message Bar {
      bool bar = 1;

    extend proto2.FileOptions {
      bool file_opt1 = 5000;
      Bar file_opt2 = 5001;

    // foo.proto:
    edition = "2024";

    import option "bar.proto";

    option (file_opt1) = true;
    option (file_opt2) = {bar: true};

    message Foo {
      // Bar bar = 1; // This is not allowed

Option imports do not require generated code for its symbols and should thus be provided as `option_deps` in `proto_library` instead of `deps`. This avoids generating unreachable code.

    proto_library(
      name = "foo",
      srcs = ["foo.proto"],
      option_deps = [":custom_option_proto"]
    )

Option imports and `option_deps` are strongly recommended when importing protobuf language features and other custom options to avoid generating unnecessary code.

This replaces `import weak`, which was removed in Edition 2024.

#### `export` / `local` Keywords

`export` and `local` keywords were added in Edition 2024 as modifiers for the symbol visibility of importable symbols, from the default behavior specified by [`features.default_symbol_visibility`](https://protobuf.dev/editions/features#symbol-vis).

This controls which symbols can be imported from other proto files, but does not affect code-generation.

In Edition 2024, these can be set on all `message` and `enum` symbols by default. However, some values of the `default_symbol_visibility` feature further restrict which symbols are exportable.

Example:

    // Top-level symbols are exported by default in Edition 2024
    message LocalMessage {
      int32 baz = 1;
      // Nested symbols are local by default in Edition 2024; applying the `export`
      // keyword overrides this
      export enum ExportedNestedEnum {
        UNKNOWN_EXPORTED_NESTED_ENUM_VALUE = 0;

    // The `local` keyword overrides the default behavior of exporting messages
    local message AnotherMessage {
      int32 foo = 1;

#### `import weak` and Weak Field Option

As of Edition 2024, weak imports are no longer allowed.

If you previously relied on `import weak` to declare a “weak dependency”—to import custom options without generated code for C++ and Go—you should instead migrate to use `import option`.

See [`import option`](https://protobuf.dev/editions/overview#import-option) for more details.

#### `ctype` Field Option

As of Edition 2024, `ctype` field option is no longer allowed. Use the `string_type` feature instead.

See [`features.(pb.cpp).string_type`](https://protobuf.dev/editions/features#string_type) for more details.

#### `java_multiple_files` File Option

As of Edition 2024, the `java_multiple_files` file option no longer available. Use the [`features.(pb.java).nest_in_file_class`](https://protobuf.dev/editions/features#java-nest_in_file) Java feature, instead.


# Java Proto Names

Names that are generated by the Java protoc plugin.

This document contains information on what the fully-qualified Java name of a proto is, based on the different proto options. This name corresponds to the package you need to import to use that message.

## Recommendation

Starting with Edition 2024, best practice is to:

  * Set `option java_package = "com.example.package"`
  * **Do not set** `java_outer_classname` or `features.(pb.java).nest_in_file_class = YES`.

Starting with Edition 2024, the default behaviors have otherwise been improved so that now the default behavior is considered current best practice.

When using Proto2, Proto3, or Edition 2023, best practice is to:

  * Set `option java_multiple_files = true;`
  * Set `option java_outer_classname = "FileNameProto";`
  * Set `option java_package = "com.google.package";`

### Explanation

#### Multiple Files

With `java_multiple_files = true`, the generated Java class for each message will be placed in a separate `.java` file. This makes it much easier to move messages from one `.proto` file to another.

Starting in Edition 2024 this has been replaced by the feature `features.(pb.java).nest_in_file_class` which has default value of `NO`, matching the `java_multiple_files = true` behavior in older syntax.

#### Outer Classname

There is a Java class generated for the `.proto` file itself. The name of the class for the file will be automatically generated if not specified. However, the rules for how that name is generated are overly-complicated and non-obvious. The best policy is to explicitly set the `java_outer_classname` option to the `.proto` file name converted to PascalCase with the `'.'` removed. For example:

  * The file `student_record_request.proto` should set:

        option java_outer_classname = "StudentRecordRequestProto";

Starting in Edition 2024, `java_outer_classname` is still available, but the default behavior has been changed to match this recommendation and so does not need to be set.

#### Java Package

The Java package for generated bindings will be automatically set to the proto package. However, this is usually not conformant with Java conventions. To ensure a conventional Java package name, we recommend explicitly setting the `java_package` option. For example, within Google, the convention is to prepend `com.google.` to the proto package.

## Immutable API Message Names

The Java plugin for protoc will generate names according to this table.

java_multiple_files| java_package| java_outer_classname| Generated full message name
---|---|---|---
true| Not defined|  _ignored_| `com.google.protos.$package.$message`
true| Defined|  _ignored_| `$java_package.$message`
false| Not defined| Not defined| `com.google.protos.$package.$derived_outer_class.$message`
false| Not defined| Defined| `com.google.protos.$package.$java_outer_classname.$message`
false| Defined| Not defined| `$java_package.$derived_outer_class.$message`
false| Defined| Defined| `$java_package.$java_outer_classname.$message`

**Legend**

  * `$message` is the actual name of the proto message.

  * `$package` is the name of the proto package. This is the name specified by the `package` directive in the proto file, which is usually at the top of the file.

  * `$derived_outer_class` is a name generated from the proto file name. Generally it’s computed by removing punctuation from the file name and converting it to PascalCase. For example, if the proto is `foo_bar.proto`, the `$derived_outer_class` value is `FooBar`.

If the generated class name would be the same as one of the messages defined in the proto file, `derived_outer_class` has `OuterClass` appended to it. For example, if the proto is `foo_bar.proto` and contains a `FooBar` message, the `$derived_outer_class` value is `FooBarOuterClass`. The same is true when using the v1 API, whether or not the class name would be the same as one of the messages defined.

  * All other `$names` are the values of the corresponding file options defined in the `.proto` file.


# Rive Luau LSP

  A VS Code language server for Rive's Luau scripting environment.
  Rich tooltips, autocomplete, diagnostics, and IntelliSense — designed for artists and designers learning to code.

## Features

- **Autocomplete** — context-aware suggestions for the entire Rive scripting API, standard Luau library, and your own code
- **Hover documentation** — educational tooltips that explain every type, method, property, and parameter in plain English with practical examples
- **Real-time diagnostics** — catches type errors, missing properties, and undefined variables as you type
- **Go-to-definition** — jump to where any symbol is defined
- **Syntax highlighting** — full Luau grammar support including Markdown code blocks
- **Custom file icons** — `.luau` files get a distinctive icon in the explorer

### Documentation Philosophy

Every tooltip is written for people who **do not code for a living**. The Rive Luau scripting audience is primarily artists and motion designers learning to script. Tooltips explain concepts in visual/conceptual terms, use analogies, show practical examples with context, and warn about common mistakes in plain language.

Examples:

drawPath — "Render a shape on screen. This is the core drawing call in Rive scripting.
You give it two things: path (what to draw) and paint (how it looks)."

clipPath — "Mask all future drawing to only appear inside this shape.
Like cutting a hole in paper — after clipPath(), only the area inside the clip path is visible."

BlendMode — "Controls how overlapping shapes blend together — like Photoshop layer blend modes.
multiply = darken, screen = lighten, overlay = contrast boost."

## CLI Usage (for agents and automation)

The repo includes standalone CLI tools for type checking and static analysis — no VS Code required. Use these from scripts, CI pipelines, or AI coding agents.

### Quick Start

```bash
git clone https://github.com/ivg-design/rive-luau-lsp.git
cd rive-luau-lsp

# Analyze a single file
bin/rive-luau-analyze path/to/script.luau

# Analyze an entire directory
bin/rive-luau-analyze path/to/effects/

# Start the LSP server (for editor/agent integration via stdio)
bin/rive-luau-lsp

### `rive-luau-analyze` — Static Analysis

Runs type checking, linting, and diagnostics on Rive Luau files. Automatically loads the complete Rive API type definitions. Exit code 0 means no errors.

```bash
# Check a script for type errors
bin/rive-luau-analyze effects/Glassifier/Glassifier.luau

# Check all scripts in a directory (respects .luaurc if present)
bin/rive-luau-analyze effects/

# Pass additional luau-lsp flags
bin/rive-luau-analyze myScript.luau --formatter=plain

### `rive-luau-lsp` — Language Server

Starts the full language server over stdio with Rive definitions and documentation pre-loaded. Connect from any LSP-compatible client: Neovim, Emacs, Helix, Zed, or an AI agent.

```bash
# Start LSP server
bin/rive-luau-lsp

# With additional flags
bin/rive-luau-lsp --flag:LuauSomeFlag=true

### Agent Integration Example

An AI coding agent can use the analyze tool to validate Rive Luau code:

```bash
# After generating or modifying a script, validate it:
result=$(bin/rive-luau-analyze generated_script.luau 2>&1)
if [ $? -ne 0 ]; then
    echo "Type errors found:"
    echo "$result"
    # Agent can fix errors and retry
fi

## AI Agent Skills

Ready-to-install skill packages for AI coding agents. Each skill gives the agent access to the Rive Luau type checker, API reference, script patterns, and a validation workflow.

### Claude Code

Install by copying into your personal or project skills directory:

```bash
# Personal (available in all projects)
cp -r skills/claude/rive-luau-lsp ~/.claude/skills/

# Project-level (available in one repo)
cp -r skills/claude/rive-luau-lsp .claude/skills/

Then invoke with `/rive-luau-lsp` or let Claude auto-trigger when working with `.luau` files.

See [`skills/claude/rive-luau-lsp/SKILL.md`](skills/claude/rive-luau-lsp/SKILL.md)

### OpenAI Codex

Install by copying into your user or project skills directory:

```bash
# Personal
cp -r skills/codex/rive-luau-lsp ~/.agents/skills/

# Project-level
cp -r skills/codex/rive-luau-lsp .agents/skills/

Codex will auto-trigger the skill when working with Rive Luau scripts.

See [`skills/codex/rive-luau-lsp/SKILL.md`](skills/codex/rive-luau-lsp/SKILL.md) and [`skills/codex/rive-luau-lsp/agents/openai.yaml`](skills/codex/rive-luau-lsp/agents/openai.yaml)

### Other LSP-Compatible Editors (Cursor, Windsurf, Neovim, etc.)

Point your editor's LSP configuration to the language server:

```json
  "luau": {
    "command": "/path/to/rive-luau-lsp/bin/rive-luau-lsp",
    "filetypes": ["luau"]

## VS Code Extension

### From VSIX (recommended)

1. Download the latest `.vsix` from the [Releases](https://github.com/ivg-design/rive-luau-lsp/releases) page
2. In VS Code, open the Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`)
3. Run **"Extensions: Install from VSIX..."**
4. Select the downloaded `.vsix` file
5. Reload VS Code

### From Source

```bash
git clone https://github.com/ivg-design/rive-luau-lsp.git
cd rive-luau-lsp/extension
npm install
npx @vscode/vsce package --allow-missing-repository
code --install-extension rive-luau-*.vsix

## What's Included

### Language Server (`bin/luau-lsp`)

A modified build of [luau-lsp](https://github.com/JohnnyMorganz/luau-lsp) by JohnnyMorganz with Rive-specific changes:

- **Ancestor-walk require resolution** — Rive resolves `require("lib/Module")` from the script root directory, not the file's directory. The LSP walks up parent directories to find the correct module, eliminating false "Module not found" errors.
- **Local-only documentation** — All hover tooltips render locally without "Learn More" links to external websites.
- **Data namespace type resolution** — `Input<Data.X>` resolves in type annotations via a namespace fallback system, matching Rive editor syntax. `Data.X.new()` returns a typed ViewModel instance with dynamic property access.

### Type Definitions (`definitions/rive-globals.d.luau`)

Complete Rive scripting API type definitions with educational documentation covering:

| Category | Types |
|----------|-------|
| **Core** | `Vector`, `Color`, `Mat2D` |
| **Drawing** | `Path`, `Paint`, `Renderer`, `Gradient`, `PathMeasure`, `ContourMeasure`, `ImageSampler` |
| **Scene** | `NodeData`, `NodeReadData`, `Artboard`, `Animation` |
| **Data Binding** | `ViewModel`, `Property<T>`, `PropertyList`, `DataContext`, `Context`, `Data` namespace |
| **Assets** | `Image`, `Blob`, `AudioSource`, `AudioSound`, `Audio` |
| **Script Protocols** | `Node<T>`, `Layout<T>`, `Converter<T,I,O>`, `PathEffect<T>`, `ListenerAction<T>`, `TransitionCondition<T>` |
| **Data Values** | `DataValue`, `DataValueNumber`, `DataValueString`, `DataValueBoolean`, `DataValueColor` |
| **Events** | `PointerEvent` |
| **Testing** | `Tester`, `Expectation` |

### Standard Library Documentation (`definitions/luau-api-docs.json`)

655 symbol entries covering the entire Luau standard library, all rewritten with educational descriptions:

- **math** — 30 functions + 7 constants (floor, ceil, clamp, lerp, sin, cos, noise, etc.)
- **string** — 17 functions (find, format, gsub, split, sub, etc.)
- **table** — 17 functions (insert, remove, sort, find, move, freeze, etc.)
- **bit32** — 15 functions (band, bor, bxor, lshift, rshift, etc.)
- **Global functions** — print, require, type, tostring, tonumber, assert, error, pcall, xpcall, pairs, ipairs, select, unpack, and more
- **coroutine, debug, os, utf8, buffer** — full coverage

## File Icon

The extension includes a custom icon for `.luau` files that appears automatically in the VS Code explorer (when your icon theme doesn't define its own `.luau` icon).

For a dedicated icon theme, open the Command Palette and select **"Preferences: File Icon Theme"** → **"Rive Luau Icons"**.

## Configuration

| Setting | Default | Description |
|---------|---------|-------------|
| `rive-luau.trace.server` | `"off"` | Traces communication between VS Code and the language server. Set to `"messages"` or `"verbose"` for debugging. |

## Project Structure

rive-luau-lsp/
├── README.md
├── CHANGELOG.md
├── LICENSE                        # MIT
├── ATTRIBUTION.md                 # Credits to upstream projects
├── bin/
│   ├── luau-lsp                   # Language server binary (macOS)
│   ├── rive-luau-analyze          # CLI: static analysis & type checking
│   └── rive-luau-lsp             # CLI: start LSP server (stdio)
├── definitions/
│   ├── rive-globals.d.luau        # Rive API type definitions (with docs)
│   └── luau-api-docs.json         # Standard library documentation
└── extension/                     # VS Code extension source
    ├── package.json               # Extension manifest
    ├── extension.js               # Extension entry point
    ├── icon.png                   # Extension marketplace icon
    ├── README.md                  # Marketplace page content
    ├── language-configuration.json
    ├── bin/
    │   └── luau-lsp               # Language server binary (bundled)
    ├── definitions/
    │   ├── rive-globals.d.luau    # Rive API type definitions (bundled)
    │   └── luau-api-docs.json     # Standard library docs (bundled)
    ├── icons/
    │   ├── luau.svg               # File icon for .luau files
    │   ├── file-icon-theme.json   # Icon theme definition
    │   └── ...                    # Generic fallback icons
    └── syntaxes/
        ├── Luau.tmLanguage.json   # Syntax highlighting grammar
        └── codeblock.json         # Markdown code block injection

## Building from Source

### Prerequisites

- Node.js 18+
- npm
- VS Code 1.80+

### Build the Extension

```bash
cd extension
npm install
npx @vscode/vsce package --allow-missing-repository

### Build the Language Server (optional)

To rebuild the language server binary from source, clone the modified luau-lsp fork and build with CMake:

```bash
git clone https://github.com/ivg-design/luau-lsp.git
cd luau-lsp
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --target luau-lsp -j

Copy the resulting binary to `extension/bin/luau-lsp`.

## Attribution

This project stands on the shoulders of open source software:

- **[Rive](https://github.com/rive-app/rive-runtime)** — Copyright (c) 2020 Rive. The scripting API and type definitions are based on Rive's official documentation and runtime (MIT License)
- **[luau-lsp](https://github.com/JohnnyMorganz/luau-lsp)** — Copyright (c) 2022 JohnnyMorganz. The language server that powers everything (MIT License)
- **[Luau](https://github.com/luau-lang/luau)** — Copyright (c) 2019-2025 Roblox Corporation; Copyright (c) 1994-2019 Lua.org, PUC-Rio. The scripting language itself (MIT License)
- **[Lua](https://www.lua.org)** — Copyright (c) 1994-2019 Lua.org, PUC-Rio. The language Luau is derived from (MIT License)

See [ATTRIBUTION.md](ATTRIBUTION.md) for full details.

## License

[MIT](LICENSE)


# ordered-dictionary-luau

A dictionary that remembers the order in which keys were first inserted.

Get it here:

* [Wally](https://wally.run/package/egomoose/ordered-dictionary)
* [Releases](https://github.com/EgoMoose/ordered-dictionary-luau/releases)

## Examples

There are two constructors that this module provides for creating ordered dictionaries.

The first is a standard luau OOP object that provides a number of useful methods for manipulating the contents of the ordered dictionary.

```lua
local booksBorrowed = OrderedDictionary.new()

booksBorrowed:set("Tom", 2)
booksBorrowed:set("Sarah", 4)
booksBorrowed:set("John", 1)
booksBorrowed:set("Emily", 6)
booksBorrowed:set("Marcus", 0)

for person, books in booksBorrowed:iterate() do
    print(person, books)
end

local name, books = booksBorrowed:popFront() -- "Tom", 2

The second is wrapped within metatables allowing access much like a built-in dictionary. The trade off however is that less methods are accessible.

```lua
local booksBorrowed = OrderedDictionary.meta()

booksBorrowed["Tom"] = 2
booksBorrowed["Sarah"] = 4
booksBorrowed["John"] = 1
booksBorrowed["Emily"] = 6
booksBorrowed["Marcus"] = 0

for person, books in booksBorrowed do
    print(person, books)
end

-- can get the object by calling like a function
local booksBorrowedObject = booksBorrowed()
local name, books = booksBorrowedObject:popBack() -- "Marcus", 0


# luau-unzip

A Luau library to unzip and extract files from ZIP archives. The implementation is based on the [PKZip APPNOTE v6.3.9](https://www.pkware.com/documents/APPNOTE/APPNOTE-6.3.9.TXT).

## Features
- Extract files from ZIP archives.
- Supports `INFLATE` and `STORE` decompression methods.
- Traverse and list archive contents.
- Retrieve archive statistics (file count, directory count, total size) and metadata.

## Usage

```luau
local fs = require("@lune/fs")
local zip = require("luau-unzip")

-- Read the ZIP file
local file = fs.readFile("test.zip")

-- Load the ZIP archive
local reader = zip.load(buffer.fromstring(file))

-- Print directory structure
print("Directory structure:")
reader:walk(function(entry, depth)
    local prefix = string.rep("  ", depth)
    local suffix = if not entry.isDirectory
        then string.format(" (%d bytes), content: %s", entry.size, reader:extract(entry, { type = "text" }) :: string)
        else ""
    print(prefix .. entry.name .. suffix)
end)

-- List children of root directory
print("\nChildren of `/`:")
local assets = reader:listDirectory("/")
for _, entry in assets do
    print(string.format("    %s - %s (%s)", entry.name, if entry.isDirectory then "DIR" else "FILE", entry.method))
end

-- Get archive statistics
local stats = reader:getStats()
print("\nArchive stats:")
print("Files:", stats.fileCount)
print("Directories:", stats.dirCount)
print("Total size:", stats.totalSize, "bytes")

## MSLV (Minimum Supported Luau Version)
This library requires at least Luau [0.669](https://github.com/luau-lang/luau/releases/tag/0.669) (specifically requires the new require-by-string semantics, leading `|`, `buffer` built-in and `idiv` operator support).

## Contributing
Contributions are heavily welcomed! We use Nix Flakes to manage a reproducible development environment. To get started, run `exec nix develop -c $SHELL`, after [installing Nix](https://nixos.wiki/wiki/Nix_Installation_Guide). This is recommended for tests to be consistent, since we rely on `InfoZIP` implementations as sanity checks, although different distributions supply differently patched versions of it.

- We utilize [pesde](https://pesde.dev) for package management. Run `pesde install` to install all dependencies.
- Before submitting a pull request, make sure you include comprehensive test cases for new features, and make sure all tests pass. Tests can be run with `lune run tests`. Refer to other test suites for examples of how to write your own tests.

## License
This project is licensed under the [MIT] license.

[MIT]: https://compeydev.mit-license.org


# 0x5eal/unzip

v0.2.0 Â· published ...

Unzip implementation in pure Luau

Target

Luau

# luau-unzip

[](https://discord.gg/ATVVsNNv3u) [](https://pesde.dev/packages/0x5eal/unzip) [](https://lune-org.github.io/docs)

A Luau library to unzip and extract files from ZIP archives. The implementation is based on the [PKZip APPNOTE v6.3.9](https://www.pkware.com/documents/APPNOTE/APPNOTE-6.3.9.TXT).

## Features

  * Extract files from ZIP archives.
  * Supports `INFLATE` and `STORE` decompression methods.
  * Traverse and list archive contents.
  * Retrieve archive statistics (file count, directory count, total size) and metadata.

## Usage

    local fs = require("@lune/fs")
    local zip = require("luau-unzip")

    -- Read the ZIP file
    local file = fs.readFile("test.zip")

    -- Load the ZIP archive
    local reader = zip.load(buffer.fromstring(file))

    -- Print directory structure
    print("Directory structure:")
    reader:walk(function(entry, depth)
        local prefix = string.rep("  ", depth)
        local suffix = if not entry.isDirectory
            then string.format(" (%d bytes), content: %s", entry.size, reader:extract(entry, { type = "text" }) :: string)
            else ""
        print(prefix .. entry.name .. suffix)
    end)

    -- List children of root directory
    print("\nChildren of `/`:")
    local assets = reader:listDirectory("/")
    for _, entry in assets do
        print(string.format("    %s - %s (%s)", entry.name, if entry.isDirectory then "DIR" else "FILE", entry.method))
    end

    -- Get archive statistics
    local stats = reader:getStats()
    print("\nArchive stats:")
    print("Files:", stats.fileCount)
    print("Directories:", stats.dirCount)
    print("Total size:", stats.totalSize, "bytes")

## MSLV (Minimum Supported Luau Version)

This library requires at least Luau [0.669](https://github.com/luau-lang/luau/releases/tag/0.669) (specifically requires the new require-by-string semantics, leading `|`, `buffer` built-in and `idiv` operator support).

## Contributing

Contributions are heavily welcomed! We use Nix Flakes to manage a reproducible development environment. To get started, run `exec nix develop -c $SHELL`, after [installing Nix](https://nixos.wiki/wiki/Nix_Installation_Guide). This is recommended for tests to be consistent, since we rely on `InfoZIP` implementations as sanity checks, although different distributions supply differently patched versions of it.

  * We utilize [pesde](https://pesde.dev) for package management. Run `pesde install` to install all dependencies.
  * Before submitting a pull request, make sure you include comprehensive test cases for new features, and make sure all tests pass. Tests can be run with `lune run tests`. Refer to other test suites for examples of how to write your own tests.

## License

This project is licensed under the [MIT](https://compeydev.mit-license.org) license.


# Coputer

![Coputer flower logo](/Assets/coflower.svg)

Collaborative, distributed execution environment for [Luau](https://luau.org) programs.

> "coputer: get someone else to run your code for you"


# Hello, world!

Welcome to the first ever page hosted on Coputer!

Here's what the page knows about the request:

**Body:**

(0 bytes)

**Headers:**

Accept = text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Encoding = gzip, deflate

User-Agent = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

Via = 1.1 Caddy

X-Forwarded-For = 111.94.69.52

X-Forwarded-Host = first-h2lk2orlh-cctfxch0x-5yd7n93mg-zxmn64hy4-wfe6s2l9z.coputer.heliodex.cf

X-Forwarded-Proto = https

**Method:**

GET

**URL:**

**Path:**

**Query:**

**Raw path:**

**Raw query:**

...and nothing more.

## What is a Coputer?

This munchkin is the icon/logo for Coputer.

Coputer is a network for running programs like the one used to serve you this page.

These 'Coputer programs' are written in [Luau](https://luau.org) and are entirely deterministic.

The network is decentralised and collaborative – this page could have been delivered to you from anywhere across the network, even if some of its nodes are unresponsive, have blocked you, or refuse to serve this page.

Anybody should be able to upload a Coputer program for free and have it be accessible 24/7. (This probably sounds less magical now than it would have several years ago.)

See the [System](https://first-h2lk2orlh-cctfxch0x-5yd7n93mg-zxmn64hy4-wfe6s2l9z.coputer.heliodex.cf/system) page for more information about how the Coputer network works. See the [Programs](https://first-h2lk2orlh-cctfxch0x-5yd7n93mg-zxmn64hy4-wfe6s2l9z.coputer.heliodex.cf/programs) page for more information about how Coputer programs are written and executed.

This page first went live on **17 July 2025**. Watch this space! It will be updated with more information soon.

[Source code for this page](https://github.com/Heliodex/coputer/blob/main/test/programs/first/main.luau)

Source code for Coputer: [github.com/Heliodex/coputer](https://github.com/Heliodex/coputer)


# glfw-luau

A simple wrapper around the GLFW library for Zune written in luau.

### Work in progress.

![glfw-zune](https://github.com/user-attachments/assets/ffdcdf4b-13a5-4692-9e23-1c17af4e1cba)


# ApolloClient for Luau

Upstream repo: https://github.com/apollographql/apollo-client

# Using eva**lua**te to run Lua benchmarks

See eva**lua**te documentation: https://github.com/Roblox/evaluate#readme


# Home

Apollo Client Luau is a port of the javascript [Apollo Client](https://www.apollographql.com/docs/react) state management library.

By and large, [Apollo Client's documentation](https://www.apollographql.com/docs/react/get-started) should be able to serve most Apollo Client users' needs. This documentation site serves as a comprehensive guide to the _differences_ between the Luau and javascript versions of Apollo Client.

If you're new to the Apollo Client library and want **to start learning the concepts of Apollo Client**, begin with the [Apollo Client React documentation](https://www.apollographql.com/docs/react/).

If you want **to find out if a javascript Apollo Client feature is present in the Luau version (and if there are any differences to be aware of)**, check out the [API Reference](https://roblox.github.io/apollo-client-lua-internal/api-reference/apollo-client/).

If you're familiar with the javascript version and want **to learn where the Luau version differs**, start with the [Deviations page](https://roblox.github.io/apollo-client-lua-internal/deviations/).

And if you want **to migrate an existing project from Rodux to ApolloClient + GraphQL**, check out the guide on [Migrating From Rodux](https://roblox.github.io/apollo-client-lua-internal/migrating-from-rodux/minimum-requirements/).

### What is the difference between Apollo Client and GraphQL?¶

A common point of confusion around Apollo Client is understanding the difference between Apollo Client and GraphQL. The two are often referred to in the same breath, but the libraries serve substantially different purposes.

GraphQL is a query language for your API. Backend engineers can define strict types and relationships for the types of data that can be fetched, and can provide implementation details for how each type of data is fetched. This facilitates code reuse and can combat anti-patterns like over-fetching and under-fetching that manifest in REST APIs. Frontend engineers can make GraphQL requests to fetch the exact shape and quantity of data required.

Apollo Client is a state management tool that is designed for compatibility with GraphQL. Apollo Client provides a set of React hooks for executing GraphQL queries and mutations, and it normalizes and caches the results of the requests. This creates a flat, strongly-typed cache that can be reused across a frontend. Apollo Client also provides utilities for local state management, and is a direct replacement for Redux.

Apollo Client is not limited to usage with React. The core of Apollo Client is built on the zen observable library, which means that GraphQL queries can be treated as Observables.


# Apollo Client¶

Info

This page is intended to provide _brief_ descriptions, examples, and notes about deviations in behavior or API. For complete feature documentation of upstream-aligned features, follow the `View Apollo Client Docs` link and consult the Apollo Client docs.

Apollo Client is a state management library designed around GraphQL. Apollo Client exports react hooks for fetching GraphQL Queries and Mutations, APIs for reading and writing remote and local data to the `InMemoryCache`, and reactive vairables for storing local state.

### Luau-only Pitfalls¶

#### Passing `self` to Methods¶

Caution

A common pitfall when using Apollo Client and GraphQL in Luau is not passing the self argument to functions. Apollo Client methods should generally be called with `:` notation, rather than `.` notation. For example, calling `client.query(options)` will result in an error. The correct usage is `client:query(options)`.

Type policies also take self as the first argument. For example, a read type policy should have the following function signature: `<T>(self, existing: T?, options: FieldFunctionOptions) -> T`

#### Immutability¶

Caution

Immutability is key in apollo client. You never want to directly mutate the cache, or your component may not update. Cryo is the recommended library for combining Arrays and Dictionaries with immutability.

### API Deviations¶

#### Not Supported¶

The following API members are notable absences relative to Apollo Client 0.3.4:

  * Hooks.useSubscription
  * ApolloClient.subscribe
  * InMemoryCache.updateFragment
  * InMemoryCache.updateQuery
  * Server-side rendering

Many Custom Apollo Links are not supported. The following are not supported:

  * BatchHttpLink
  * setContext
  * onError
  * createPersistedQueryLink
  * RestLink
  * RetryLink
  * SchemaLink
  * GraphQLWsLink

## Core¶

### ApolloClient¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/core/ApolloClient)

#### Constructor¶

    local ApolloClient = require(Packages.ApolloClient).ApolloClient
    local InMemoryCache = require(Packages.ApolloClient).InMemoryCache
    local HttpLink = require(Packages.ApolloClient).HttpLink

    local cache = InMemoryCache.new()

    local link = HttpLink.new({
      uri = "/api",
      fetch = function(uri, requestOptions)
        return fetch(uri, requestOptions)
      end
    })

    local client = ApolloClient.new({
      cache = cache,
      link = link,
    })

#### watchQuery¶

    local GET_TODOS = gql([[
      getTodos($id: String!) {
        todos(id: $id) {
          id
          description
          completed
    ]])

    client:watchQuery({
      query = GET_TODOS,
      notifyOnNetworkStatusChange = false,
    }),

#### query¶

    local GET_TODOS = gql([[
      getTodos($id: String!) {
        todos(id: $id) {
          id
          description
          completed
    ]])

    client:query({
      query = GET_TODOS,
      errorPolicy = "all",
      fetchPolicy = "network-only"
      variables = {
        id = "5"
    }),

#### mutate¶

    local CREATE_TODO = gql([[
      mutation createTodo {
        createTodo {
          id
          name
          __typename
    ]])

    client:mutate({
      mutation = CREATE_TODO,
      updateQueries = {
        todos = function(_self, prev, ref)
          local mutationResult = ref.mutationResult
          local newTodo = mutationResult.data.createTodo
          local newResults = {
            todos = Cryo.List.join(prev.todos, { newTodo }),
          return newResults
        end,
      },
    })

#### readQuery¶

    local GET_TODOS = gql([[
      getTodos($id: String!) {
        todos(id: $id) {
          id
          description
          completed
    ]])

    client:readQuery({
      query = GET_TODOS,
      variables = {
        id = "5",
    }, true),

#### readFragment¶

    local TODOS_FRAGMENT = gql([[
      fragment myTodo on Todo {
        id
        description
        completed
    ]])

    client:readFragment({
      fragment = TODOS_FRAGMENT,
      id = "5"
    }, true),

#### writeQuery¶

    local GET_TODOS = gql([[
      getTodos($id: String!) {
        todos(id: $id) {
          id
          description
          completed
    ]])

    client:writeQuery({
      query = TODOS_FRAGMENT,
      variables = {
        id = "5"
      },
      data = {
        todos = {
          __typename = "Todo",
          id = "5",
          description = "Take out the trash",
          completed = false,
    }, true),

#### writeFragment¶

    local TODOS_FRAGMENT = gql([[
      fragment myTodo on Todo {
        id
        description
        completed
    ]])

    client:writeFragment({
      fragment = TODOS_FRAGMENT,
      id = "5",
      data = {
        __typename = "Todo",
        id = "5",
        description = "Take out the trash",
        completed = true,
    }, true),

#### resetStore¶

    client:resetStore()

#### onResetStore¶

    client:onResetStore(function()
      print("reset store")
    end)

#### clearStore¶

    client:clearStore()

#### onClearStore¶

    client:onClearStore(function()
      print("reset store")
    end)

#### stop¶

    client:stop()

#### reFetchObservableQueries¶

    client:reFetchObservableQueries(false)

#### refetchQueries¶

    client:refetchQueries({
      include = { "GetAuthor" , "GetAuthor2" }
    })

### ObservableQuery¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/core/ObservableQuery)

#### result¶

    local observableQuery = client:watchQuery({
      query = query,
      variables = variables,
    })

    observableQuery:result():andThen(function(result)
      print(result.data)
    end)

#### getCurrentResult¶

    local currentResult = observableQuery:getCurrentResult()

#### refetch¶

    local GET_PEOPLE = gql([[
      query people($first: Int) {
        allPeople(first: $first) {
          people {
            name
    ]])

    local observable = client:watchQuery({
      query = GET_PEOPLE,
      variables = { first = 0 },
      notifyOnNetworkStatusChange = true,
    })

    observable:subscribe({
      next = function(result)
        observable:refetch({ first = 1 })
      end
    })

#### setOptions¶

    observableQuery:setOptions({
      fetchPolicy = "cache-only",
      errorPolicy = "all",
    })

#### setVariables¶

internal use only

#### fetchMore¶

    observableQuery:fetchMore({
      variables = {
        x = 2
    })

#### updateQuery¶

    observableQuery:updateQuery(function(data, ref)
      local variables = ref.variables
      local oldCars = data.cars
      local newCars = ref.fetchMoreCars.cars
    end)

#### startPolling¶

    observableQuery:startPolling(50)

#### stopPolling¶

    observableQuery:stopPolling()

#### subscribeToMore¶

    observableQuery:subscribeToMore({})

### InMemoryCache¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/cache/InMemoryCache)

#### Constructor¶

    local InMemoryCache = require(Packages.ApolloClient).InMemoryCache

    local cache = InMemoryCache.new({
      addTypename = true,
      resultCaching = true,
      possibleTypes = {
        User = { "Player", "Creator" }
      },
      typePolicies = {
        Feed = {
          fields = {
            sorts = {
              merge = function(_self, existing, incoming, options)
                return Cryo.List.join(existing, incoming)
              end
    })

#### readQuery¶

    local ref = cache:readQuery({
      query: gql([[
        query ReadTodo {
          todo(id: 5) {
            id
            text
            completed
      ]]),
    })

    expect(ref.todo).toEqual({
      id = "5",
      text = "Wash the dishes",
      completed = false,
    })

#### writeQuery¶

    cache:writeQuery({
      query: gql([[
        query ReadTodo($id: ID!) {
          todo(id: $id) {
            id
            text
            completed
      ]]),
      data = {
        todo = {
          __typename = "Todo",
          id = 5,
          text = "Buy Grapes",
          completed = false,
      variables = {
        id = 5,
    })

#### readFragment¶

    cache:readFragment({
      id = "5",
      fragment = gql([[
        fragment myTodo on Todo {
          id
          description
          completed
      ]])
    })

#### writeFragment¶

    cache:writeFragment({
      id = "5",
      fragment = gql([[
        fragment myTodo on Todo {
          completed
      ]]),
      data = {
        completed = true
    })

#### identify¶

    local bookYearFragment = gql([[
      fragment BookYear on Book {
        publicationYear
    ]])

    local invisibleManBook = {
      __typename = 'Book',
      isbn = "9780679601395", -- the key is isbn
      title = "Invisible Man",
      author = {
        __typename = "Author",
        name = "Ralph Ellison",
      },

    local fragmentResult = cache.writeFragment({
      id = cache:identify(invisibleManBook),
      fragment = bookYearFragment,
      data = {
        publicationYear = '1952'
    })

#### modify¶

    cache:modify({
      id = cache:identify(myObject),
      fields = {
        name = function(_self, cachedName) {
          return string.upper(cachedName);
        },
      },
    });

#### gc¶

    cache:gc()

#### evict¶

    cache:evict({ id = "my-object-id", fieldName = "myFieldName" })

#### extract¶

    local snapshot = cache:extract()

#### restore¶

    -- save a snapshot
    local snapshot = cache:extract()

    -- clear the cache
    cache:restore({})

    -- restore the state to the previous snapshot
    cache:restore(snapshot)

### ReactiveVar¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/cache/InMemoryCache#makevar)

#### makeVar¶

    local cartItems = makeVar({});

    -- Output: {}
    print(cartItems())

    -- Update reactive variable
    cartItems({ 1, 2, 3, 4 })

    -- Output: {1, 2, 3, 4}
    print(cartItems())

### Type Policies¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/caching/cache-field-behavior/)

#### Initialization¶

    local cache = InMemoryCache.new({
      typePolicies = {
        Query = {
          fields = {
            getFeed = {
              read = function(_self, _existing, options)
                return options:toReference({
                  __typename = "Feed",
                  id = options.args.id
                })
              end
        },
        Feed = {
          fields = {
            sorts = {
              read = function(_self, existing, options)
                if options:readField("under9") then
                  return Cryo.List.filter(existing, function(value)
                    return value.under9
                  end)
                end
                return existing
              end
    })

#### keyFields¶

    local cache = InMemoryCache.new({
      typePolicies = {
        Feed = {
          keyFields = { "feedId", "pageType" }
    })

#### keyArgs¶

    local cache = InMemoryCache.new({
      typePolicies = {
        Query = {
          fields = {
            feed = {
              keyFields = { "id", "pageType" }
    })

#### read¶

Caution

Apollo Client Luau passes `self` as the first argument to the `read` type policy. The other arguments are shifted over by one. `existing` data is passed as the second argument and `options` as the third.

    local cache = InMemoryCache.new({
      typePolicies = {
        Feed = {
          fields = {
            sorts = {
              read = function(_self, sorts, options)
                if options:readField("under9") then
                  return Cryo.List.filter(sorts, function(sortItem)
                    return not sortItem.under9
                  end)
                end
                return sorts
              end
    })

#### merge¶

Caution

Apollo Client Luau passes `self` as the first argument to the `merge` type policy. The other arguments are shifted over by one. `existing` data is passed as the second argument, `incoming` data as the third, and `options` as the fourth.

    local cache = InMemoryCache.new({
      typePolicies = {
        Feed = {
          fields = {
            sorts = {
              merge = function(_self, existingSorts, incomingSorts, options)
                if not options:readField("under9") then
                  return Cryo.List.join(existingSorts, incomingSorts)
                end

                local filteredSorts = Cryo.List.filter(incomingSorts, function(sortItem)
                  return not sortItem.under9
                end)

                return Cryo.List.join(existingSorts, filteredSorts)
              end
    })

#### FieldFunctionOptions¶

Caution

The methods on the `FieldFunctionOptions` Object must be called with `:` notation, rather than `.` notation.

    type FieldFunctionOptions = {
      cache: InMemoryCache,
      args: Record<string, any>?,
      fieldName: string,
      field: FieldNode?,
      variables: Record<string, any>?,
      isReference: (self, obj: any): boolean,
      toReference: (self, objOrIdOrRef: StoreObject | string | Reference, mergeIntoStore: boolean?): Reference?,
      readField: <T>(self, nameOrField: string | FieldNode, foreignObjOrRef: StoreObject | Reference): T,
      canRead: (self, value: StoreValue): boolean,
      storeage: Record<string, any>,
      mergeObjects: <T>(self, existing: T, incoming: T): T

    local read = function(_self, data, options: FieldFunctionOptions)
      local book = options:readField("book")
      local authorReference = options:toReference({
        __typename = "Author",
        id = "3"
      })
    end

## React¶

### Context¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/react/hooks#the-apolloprovider-component)

#### ApolloProvider¶

    local client = ApolloClient.new({
      cache= InMemoryCache.new(),
      uri: "http://localhost:4000/graphql"
    });

    local function App()
      return React.createElement(ApolloProvider, {
        client = client
      },
        React.createElement(HomePage)
      )
    end

#### ApolloConsumer¶

Danger

ApolloClient instances passed to `ApolloProvider` should be consumed via the useApolloClient hook instead of the ApolloConsumer.

### Hooks¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/react/hooks)

#### useQuery¶

    local GET_GREETING = gql([[
      query GetGreeting($language: String!) {
        greeting(language: $language) {
          message
    ]])

    local function Hello()
      local ref = useQuery(GET_GREETING, {
        variables= { language = 'english' },
      })
      if ref.error then
        return React.createElement("TextLabel", {
          Text = "Error",
        })
      end

      if ref.loading then
        return React.createElement("TextLabel", {
          Text = "Loading ...",
        })
      end

      return React.createElement("TextLabel", {
        Text = ref.data.greeting.message,
      })
    end

#### useLazyQuery¶

    local GET_GREETING = gql([[
      query GetGreeting($language: String!) {
        greeting(language: $language) {
          message
    ]])

    local function Hello()
      local lazyRef = useLazyQuery(GET_GREETING, {
        variables= { language = 'english' },
      })
      local loadGreeting = lazyRef[1]
      local ref = lazyRef[2]

      if ref.called and ref.loading then
        return React.createElement("TextLabel", {
          Text = "Loading ...",
        })
      end

      if not ref.called then
        return React.createElement("TextButton", {
          Text = "Load Greeting",
          [React.Event.Activated] = function()
            loadGreeting
          end
        })
      end

      return React.createElement("TextLabel", {
        Text = ref.data.greeting.message,
      })
    end

#### useMutation¶

    local ADD_TODO = gql([[
      mutation AddTodo($type: String!) {
        addTodo(type: $type) {
          id
          type
    ]])

    local function AddTodo()
      local input
      local mutationRef = useMutation(ADD_TODO);

      local addTodo = mutationRef[1]
      local ref = mutationRef[2]

      return React.createElement(Form, {
        onSubmit = function(rbx)
          addTodo({
            variables = {
              type = rbx.Text,
          })
          rbx.Text = ""
        end
      })
    end

#### useApolloClient¶

    local function SomeComponent()
      -- this client is the same as the one passed to ApolloProvider
      local client = useApolloClient();
    end

#### useReactiveVar¶

    local cartItemsVar = makeVar({});

    local function Cart()
      local cartItems = useReactiveVar(cartItemsVar);

      return React.createElement("Frame", {}, Cryo.List.map(cartItems, function(item)
        return React.createElement(CartItem, item)
      end))
    end

### Testing¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/react/testing)

Apollo Client Testing Minimum Requirements

Apollo Client Testing requires Jest ^3 and ReactTestingLibrary ^12. You will experience errors if you try to use MockedProvider in a TestEZ environment.

Importing Apollo Client Testing

Apollo Client Testing is exported as a separate dev-dependency, rather than via the Apollo Client package. You can import it from `Packages.Dev.ApolloClientTesting`

#### MockedProvider¶

    local GET_DOG_QUERY = gql([[
      query GetDog($name: String) {
        dog(name: $name) {
          id
          name
          breed
    ]])

    local mocks = {
        request = {
          query = GET_DOG_QUERY,
          variables = {
            name = "Buck"
        },
        result = {
          data = {
            dog = { id = "1", name = "Buck", breed = "bulldog" }

    it("renders without error", function()
      local result = render(
        React.createElement(MockedProvider, {
          mocks = mocks,
          addTypename = false,
        },
          React.createElement(Dog, {
            name = "Buck",
          })
        )
      )
      expect(result.findByText("Loading..."):expect()).toBeDefined()
      expect(result.findByText("Buck is a poodle"):expect()).toBeDefined()
    end)

## Apollo Link¶

[View Apollo Client Docs](https://www.apollographql.com/docs/react/api/link/introduction)

#### ApolloLink¶

    local timeStartLink = ApolloLink.new(function(_self, operation, forward)
      operation:setContext({ start = os.clock() });
      return forward(operation);
    end)

### HttpLink¶

    local link = HttpLink.new({
      uri = "http://localhost:4000/graphql"
      fetch = fetchLocal
    })


# Deviations

The core API of Apollo Client Luau is directly aligned with the API of the typescript version. However, there are some minor deviations around executing class methods, unpacking query and mutation results, and testing react components.

## Executing Class Methods¶

JavaScript uses `.` notation to execute all class methods, while Luau uses `:` notation to execute non-static methods and `.` to execute static methods. When calling methods directly on an ApolloClient or InMemoryCache instance, you should always use `:` notation to ensure that `self` is passed to the method as the first argument. If you call these methods with `.` notation, Luau will assume that the first argument passed to the method is `self`, which often results in hard-to-catch bugs. Examples of the correct usage can be found in the [full api documentation](https://roblox.github.io/apollo-client-lua-internal/api-reference/apollo-client/).

## Passing `self` to `read` and `merge` Type Policies¶

In lua, the `read` and `merge` type policy definitions expect self as the first argument. Detailed examples of the correct usage of `read` and `merge` can be found in the [API docs](https://roblox.github.io/apollo-client-lua-internal/deviations/api-reference/apollo-client#type-policies.md).

Correct usage:

    read = function(_self, data, options) ... end
    merge = function(_self, existing, incoming, options) ... end

## Unpacking results from useQuery, useMutation¶

JavaScript has a feature called [Object Destructuring](https://basarat.gitbook.io/typescript/future-javascript/destructuring#object-destructuring) that is not available in Luau. This allows for variables to be easily parsed from the return object of a function. In Apollo Client, this feature is often used to parse the result of useQuery, useMutation, and useLazyQuery:

    const { data, loading, error } = useQuery(GET_DOGS)

    const [setDogs, { data, loading, error }] = useMutation(SET_DOGS)

    const [getDogs, { data, loading, error }] = useLazyQuery(GET_DOGS)

In luau, we have to manually destructure these objects and arrays:

    local queryResult = useQuery(GET_DOGS)
    local data = queryResult.data
    local error = queryResult.error
    local loading = queryResult.loading

    local mutationRef = useMutation(SET_DOGS)
    local setDogs = mutationRef[1]
    local data = mutationRef[2].data
    local error = mutationRef[2].error
    local loading = mutationRef[2].loading

    local lazyRef = useLazyQuery(GET_DOGS)
    local getDogs = lazyRef[1]
    local data = lazyRef[2].data
    local error = lazyRef[2].error
    local loading = lazyRef[2].loading

The result of useQuery, useMutation, etc. are all strongly typed, which should allow analysis to catch any typos or nil index bugs at build time.

## Apollo Client Testing¶

In javascript, the apollo client testing module is imported via `import { MockedProvider } from "@apollo/client/testing`. In Luau, Apollo Client Testing is exported as a separate dev-dependency, rather than via the Apollo Client package. You can import it from `Packages.Dev.ApolloClientTesting`.