Skip to content

Added encodeEntity to encode an entity without a Lens - #181

Merged
karelklima merged 5 commits into
karelklima:mainfrom
jspthesixth:feat/export-encode
Sep 28, 2026
Merged

karelklima merged 5 commits into
karelklima:mainfrom
jspthesixth:feat/export-encode

Conversation

@jspthesixth

@jspthesixth jspthesixth commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Adds encodeEntity(schema, entity), a typed wrapper in encoder.ts that expands the schema and runs the internal encoder, exported from the root module. It returns the quads Lens.insert writes, with every literal carrying the datatype the schema declares, without needing a data source.

import { encodeEntity } from "ldkit";

const quads = encodeEntity(PersonSchema, entity);

Options resolve the way createLens resolves them, so a global language applies.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Add tests covering both root exports and calling encode without options.

Review effort: Lite
Findings: None

What changed in this PR

Exports reusable schema expansion and RDF encoding APIs from the root module, with optional encoder options.

Changes:

  • Exports encode and expandSchema.
  • Adds JSDoc and explicit return types.
  • Allows encode options to be omitted.
File Summary
mod.ts Adds root-level exports for encode and expandSchema; public API coverage is still needed.
library/​schema/​utils.ts Documents and types expandSchema.
library/​encoder.ts Documents encode and defaults options; the two-argument public contract needs coverage.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@karelklima

Copy link
Copy Markdown
Owner

Hi, thanks for the PR! The idea is good, I just need to think about what would be the best interface for this. I don't think that the encode function should be exposed like this as it is, it would be better to have a wrapper that accepts Schema (not ExpandedSchema - or perhaps both). Alternatively this wrapper may be placed to the Lens component. I need to think about it a bit and do some testing.

@jspthesixth

Copy link
Copy Markdown
Contributor Author

Let me share the idea behind it, maybe it helps with the interface.

Our backend takes entity writes as JSON-LD. At the moment every consumer of our SDK does the JSON-LD serialization manually, and that's where we saw an issue, someone forgetting to produce the typed literals for dates for example and sending those over as plain strings.

LDkit's encoder is what we would use in order to stop doing it manually. The encoder walks the entity next to the schema and produces quads, with every literal carrying the datatype the schema declares:

{ $id: "ex:Campaign_1", label: "Summer stays", startTime: new Date("2026-09-22T23:00:00Z") }
<ex:Campaign_1>  rdf:type      <ex:Campaign> .
<ex:Campaign_1>  rdfs:label    "Summer stays" .
<ex:Campaign_1>  ex:startTime  "2026-09-22T23:00:00Z"^^xsd:dateTime .

As far as my understanding goes, the only public thing that runs the encoder is Lens.insert, and insert exists to write to a store. It turns the quads into a SPARQL update and hands it to the engine:

INSERT DATA {
  <ex:Campaign_1> <rdf:type> <ex:Campaign> .
  <ex:Campaign_1> <rdfs:label> "Summer stays" .
  <ex:Campaign_1> <ex:startTime> "2026-09-22T23:00:00Z"^^<xsd:dateTime> .
}

We don't write to a store at that point, so today we run insert against a capturing engine just to get the SPARQL update out, after that extract the triple block from it, parse it back into quads ourselves, and serialize those quads to JSON-LD:

{
  "@id": "ex:Campaign_1",
  "@type": "ex:Campaign",
  "rdfs:label": "Summer stays",
  "ex:startTime": { "@value": "2026-09-22T23:00:00Z", "@type": "xsd:dateTime" }
}

So we go through Turtle and back only to end up with the quads the encoder had in the first place, because there is no way to reach it without a Lens. It works, the consumer writes a typed entity and the datatypes come from the schema, but the round trip shouldn't be needed.

What we need is one call that takes a schema and an entity and returns the quads insert would write. encode(entity, expandSchema(schema)), a wrapper that accepts a Schema, or a method on the Lens are all fine by us. The one thing we'd like to avoid is having to construct a Lens with a data source we don't have, just to encode.

@karelklima

Copy link
Copy Markdown
Owner

Thanks for the background. I understand the use case, it will be a good addition to the library. I agree that constructing the whole Lens object just to get things encoded makes no sense.

Let's go with the wrapper as you suggested - typed function encodeEntity(schema, entity) in the encoder.ts file.

The wrapper would expand the schema and then call the internal encode function. I think that is the best compromise that provides the feature you need without exposing anything extra (expanded schema).

I might decide to expose more settings for the encoder in the future to facilitate more advanced use cases, but in that case I will come up with a standelone constructor, i.e. createEncoder, which would be similar to createLens.

@jspthesixth jspthesixth changed the title Exported encode and expandSchema from the root module Added encodeEntity to encode an entity without a Lens Sep 28, 2026
@jspthesixth

Copy link
Copy Markdown
Contributor Author

Done, encodeEntity(schema, entity) is in encoder.ts and is the only new root export. encode and expandSchema are internal again.

One thing to flag. The wrapper resolves options the way createLens does, so a language set with setGlobalOptions applies and the quads match what insert writes. If you want it schema-only until createEncoder, I'll pass {} instead.

deno doc --lint reports encodeEntity referencing the private Entity and Quad types, the same two Lens.insert and Lens.find already report. I left them as they are since fixing them means exporting those types from the root.

@karelklima
karelklima merged commit 94d626f into karelklima:main Sep 28, 2026
1 check passed
@karelklima

Copy link
Copy Markdown
Owner

Thanks for the updated PR! The change is merged and released in https://www.npmjs.com/package/ldkit/v/2.9.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants