Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/memory/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ See [README.md](README.md) for the format and routing rules.

## Project (durable context & rationale)

*(no entries yet)*
- [ts-codegen-via-buf](project/ts-codegen-via-buf.md) — TS codegen will likely use Buf (protobuf-es); keep proto option docs implementation-neutral.

## Reference (external systems)

Expand Down
24 changes: 24 additions & 0 deletions .agents/memory/project/ts-codegen-via-buf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
name: ts-codegen-via-buf
description: TypeScript codegen will likely use Buf (protobuf-es), not google-protobuf protoc — keep proto option docs implementation-neutral.
metadata:
type: project
since: 2026-08-11
---

TypeScript code generation for the Spine SDK is likely to be built on
Buf (`protobuf-es`) rather than the `google-protobuf` runtime used by
the legacy JS stack (`web/client-js`, `mc-js`).

**Why:** Stated by the project lead while reviewing the `ts_type`
property docs of `IsOption`/`EveryIsOption` (branch
`more-language-types`, 2026-08-11): the docs must not restrict
implementation details such as "extends the `Message` type from the
`google-protobuf` module", because the TS toolchain is not settled on
`google-protobuf`.

**How to apply:** When documenting or implementing TypeScript-facing
options in `options.proto` (or elsewhere in the SDK), stay neutral
about the generation toolchain and runtime base types until the TS
codegen actually lands. Do not infer TypeScript contracts from what
`web/client-js` currently depends on.
50 changes: 50 additions & 0 deletions .agents/tasks/add-ts-type-option.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
slug: add-ts-type-option
branch: more-language-types
owner: claude
status: in-review
started: 2026-08-11
---

## Goal

`IsOption` and `EveryIsOption` in `base/src/main/proto/spine/options.proto`
gain a `ts_type` property naming the TypeScript interface for the annotated
message type(s), mirroring the existing `java_type` contract.

## Context

- Requested by the user on the `more-language-types` branch, which is
already version-bumped (`2.0.0-SNAPSHOT.440`).
- No consumer of `ts_type` exists yet anywhere in the SpineEventEngine
organisation; this repo introduces the property first. Codegen support
arrives later in the compiler/model-compiler repos.
- Spine's JS/TS client stack (`web/client-js`) is built on the
`google-protobuf` runtime, so a generated marker interface extends
`Message` from that module — the parallel of `com.google.protobuf.Message`
in the `java_type` docs.

## Plan

- [x] Add `ts_type = 2` to `IsOption` with docs mirroring `java_type`.
- [x] Add `ts_type = 3` to `EveryIsOption`, including the `generate`
interplay paragraphs.
- [x] Extend the `(is)` and `(every_is)` extension-field docs with
"When targeting TypeScript…" sentences.
- [x] Mention `ts_type` in the message-level docs of both option types.
- [x] Build (`proto` change → `clean build` per `running-builds.md`).

## Log

- 2026-08-11 — drafted; executing.
- 2026-08-11 — `./gradlew clean build` green; `review-docs` approved the
diff (nits only). Discussing the "not nested into a namespace" wording
with the user; docs may still be adjusted before commit.
- 2026-08-11 — per user: dropped the "generated interface extends
`Message` from `google-protobuf`" paragraph — TS codegen will likely
use Buf, so docs stay implementation-neutral (see team memory
`ts-codegen-via-buf`). Rebuilt: green.
- 2026-08-11 — pre-pr PASS (build green; kotlin-engineer APPROVE,
review-docs APPROVE, spine-code-review APPROVE WITH CHANGES — noted
the pre-existing +10 bump vs additive change; surfaced in PR body).
PR: https://github.com/SpineEventEngine/base-libraries/pull/956
36 changes: 36 additions & 0 deletions base/src/main/proto/spine/options.proto
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,9 @@ extend google.protobuf.MessageOptions {
// When targeting Java, specify the name of a Java interface to be implemented by this
// message via `(is).java_type`.
//
// When targeting TypeScript, specify the name of a TypeScript interface to be
// implemented by this message via `(is).ts_type`.
//
IsOption is = 73915;

// Reserved 73916 to 73921 for future API annotation options.
Expand Down Expand Up @@ -724,6 +727,9 @@ extend google.protobuf.FileOptions {
// When targeting Java, specify the name of a Java interface to be implemented by these
// message types via `(every_is).java_type`.
//
// When targeting TypeScript, specify the name of a TypeScript interface to be
// implemented by these message types via `(every_is).ts_type`.
//
EveryIsOption every_is = 73946;

// Reserved 73947 to 73970 for future use.
Expand Down Expand Up @@ -1235,6 +1241,7 @@ message EntityOption {
// The nature of the type depends on the target programming language.
// For example, the `java_type` property defines a name of the Java interface common
// to all message classes generated for the proto file having this option.
// Similarly, the `ts_type` property defines a name of the TypeScript interface.
//
// The option triggers creation of the common type if the `generate` property is set to true.
// Otherwise, it is expected that the user provides the reference to an existing type.
Expand Down Expand Up @@ -1267,13 +1274,30 @@ message EveryIsOption {
// will have no declared methods.
//
string java_type = 2;

// The reference to a TypeScript top-level interface.
//
// The interface cannot be nested into a namespace or a class.
// If a nested interface is provided, the code generation should fail the build process.
//
// The value is a simple name of the interface. It is assumed that the interface
// is available in the scope of the generated message types.
//
// If the value of the `generate` field is set to `false`, the referenced interface must exist.
// Otherwise, a compilation error will occur.
//
// If the value of the `generate` field is set to `true`, the framework will
// generate the interface using the given name.
//
string ts_type = 3;
}

// Defines an additional type for a message type in which this option is declared.
//
// The nature of the type depends on the target programming language.
// For example, the `java_type` property defines a name of the Java interface that
// the generated message class will implement.
// Similarly, the `ts_type` property defines a name of the TypeScript interface.
//
message IsOption {

Expand All @@ -1290,6 +1314,18 @@ message IsOption {
// The referenced interface must exist. Otherwise, a compilation error will occur.
//
string java_type = 1;

// The reference to a TypeScript top-level interface.
//
// The interface cannot be nested into a namespace or a class.
// If a nested interface is provided, the code generation should fail the build process.
//
// The value is a simple name of the interface. It is assumed that the interface
// is available in the scope of the generated message types.
//
// The referenced interface must exist. Otherwise, a compilation error will occur.
//
string ts_type = 2;
}

// Defines the way to compare two messages of the same type to one another.
Expand Down
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/io/spine/dependency/boms/Boms.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ package io.spine.dependency.boms

import io.spine.dependency.DependencyWithBom
import io.spine.dependency.kotlinx.Coroutines
import io.spine.dependency.lib.Jackson
import io.spine.dependency.lib.JacksonV2
import io.spine.dependency.lib.Kotlin
import io.spine.dependency.lib.Grpc
import io.spine.dependency.test.JUnit
Expand Down Expand Up @@ -60,7 +60,7 @@ object Boms {
* Technology-based BOMs.
*/
object Optional {
val jackson = Jackson.bom
val jackson = JacksonV2.bom
val grpc = Grpc.bom
}
}
5 changes: 4 additions & 1 deletion buildSrc/src/main/kotlin/io/spine/dependency/lib/Jackson.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import io.spine.dependency.DependencyWithBom
* consuming the 2.x artifact, so both its coordinates and its
* `com.fasterxml.jackson.annotation` package stay unchanged.
*
* The Jackson 2.x artifacts, which some of our dependencies still consume,
* are declared by [JacksonV2].
*
* See:
* - [Jackson Releases](https://github.com/FasterXML/jackson/wiki/Jackson-Releases)
* - [Migrating to Jackson 3](https://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md)
Expand All @@ -50,7 +53,7 @@ object Jackson : DependencyWithBom() {
* The version of `jackson-annotations`, which Jackson 3.x deliberately keeps
* on the 2.x line.
*
* Must match the `jackson.version.annotations` property declared by the [bom].
* Must match the `jackson.version.annotations` property declared by the [JacksonV2.bom].
*
* See: https://github.com/FasterXML/jackson-annotations?tab=readme-ov-file#release-notes
*/
Expand Down
105 changes: 105 additions & 0 deletions buildSrc/src/main/kotlin/io/spine/dependency/lib/JacksonV2.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2026, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.dependency.lib

import io.spine.dependency.Dependency
import io.spine.dependency.DependencyWithBom

/**
* Jackson 2.x dependencies.
*
* Jackson 2.x artifacts keep the `com.fasterxml.jackson.*` group IDs, unlike
* Jackson 3.x, which moved to `tools.jackson` (JSTEP-1). We declare the 2.x line
* to align the versions of the artifacts pulled transitively by third-party
* dependencies, while our own code uses Jackson 3.x declared by [Jackson].
*
* The `jackson-annotations` artifact, although it belongs to the 2.x line, is
* declared by [Jackson.annotations] because Jackson 3.x keeps consuming it.
*
* See:
* - [Jackson Releases](https://github.com/FasterXML/jackson/wiki/Jackson-Releases)
*
* @see Jackson
*/
@Suppress("unused")
object JacksonV2 : DependencyWithBom() {
override val group = "com.fasterxml.jackson"
override val version = "2.22.1"

// https://github.com/FasterXML/jackson-bom
override val bom = "$group:jackson-bom:$version"

private val groupPrefix = group

/**
* All Jackson 2.x modules we use are declared by the nested objects,
* such as [Core] or [DataType].
*/
override val modules = emptyList<String>()

object Core : Dependency() {
override val version = JacksonV2.version
override val group = "$groupPrefix.core"

@Suppress("MemberNameEqualsClassName")
val core = "$group:jackson-core"
val databind = "$group:jackson-databind"

override val modules = listOf(core, databind)
}

object DataType : Dependency() {
override val version = JacksonV2.version
override val group = "$groupPrefix.datatype"

val jdk8 = "$group:jackson-datatype-jdk8"
val jsr310 = "$group:jackson-datatype-jsr310"
val guava = "$group:jackson-datatype-guava"

override val modules = listOf(jdk8, jsr310, guava)
}

object DataFormat : Dependency() {
override val version = JacksonV2.version
override val group = "$groupPrefix.dataformat"

val xml = "$group:jackson-dataformat-xml"
val yaml = "$group:jackson-dataformat-yaml"
val protobuf = "$group:jackson-dataformat-protobuf"

override val modules = listOf(xml, yaml, protobuf)
}

object Module : Dependency() {
override val version = JacksonV2.version
override val group = "$groupPrefix.module"

val parameterNames = "$group:jackson-module-parameter-names"

override val modules = listOf(parameterNames)
}
}
18 changes: 17 additions & 1 deletion buildSrc/src/main/kotlin/io/spine/dependency/lib/Log4j2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ package io.spine.dependency.lib
*/
@Suppress("unused", "ConstPropertyName")
object Log4j2 {
private const val version = "2.26.0"
const val version = "2.26.1"

const val core = "org.apache.logging.log4j:log4j-core:$version"

/**
* Routes the calls made through the [SLF4J API][Slf4J.lib] to the [core] backend.
*
* Add this artifact when a third-party library logs via SLF4J — such as Micronaut —
* and its output should reach the Log4j2 backend of the application.
*
* The artifact is `log4j-slf4j2-impl`, the binding for the SLF4J 2.x that [Slf4J]
* declares. Do not substitute `log4j-slf4j-impl`: it binds SLF4J 1.7 only, and
* under SLF4J 2.x it registers no provider, which leaves the logging silently
* unbound instead of failing the build.
*
* @see <a href="https://logging.apache.org/log4j/2.x/log4j-slf4j2-impl/">Log4j2 SLF4J 2.x binding</a>
*/
const val slf4j2Bridge = "org.apache.logging.log4j:log4j-slf4j2-impl:$version"
}
Loading
Loading