Skip to content

[v3] Built-in service standardisation and enhancement - #4067

Merged
leaanthony merged 11 commits into
wailsapp:v3-alphafrom
fbbdev:v3-alpha-feature/service-api
Mar 1, 2025
Merged

leaanthony merged 11 commits into
wailsapp:v3-alphafrom
fbbdev:v3-alpha-feature/service-api

Conversation

@fbbdev

@fbbdev fbbdev commented Feb 13, 2025 •

Copy link
Copy Markdown
Contributor

Description

This PR proposes some changes meant to standardise and enhance the API of built-in services (fileserver, kvstore, log, sqlite), partly along the lines of the discussions that took place on Discord here and here.

The new API is mostly compatible with the existing one, but has some minor breaking changes.

All services now adhere to the following conventions:

  • The service type is called Service for consistent JS imports (fileserver and sqlite used this convention already).
  • New() function initialises the service with a sane default configuration (breaking).
  • NewWithConfig(config *Config) allows consumers to provide a custom initial configuration.
  • Service.Configure(config *Config) allows late configuration and dynamic reconfiguration.
  • Service.Configure is never exposed to the frontend, because it could allow arbitrary accesses to the filesystem.
  • All methods must be callable concurrently without issue.
  • The API must make sense from the Go side as well as from the JS side.

fileserver

  • Mostly unchanged
  • The default configuration (nil config => no root specified) responds to all requests with 503 Service Unavailable.

kvstore

  • Default configuration (nil config => no filename specified) provides an in-memory store. Load and Save methods have no effect.
  • New method Service.Load() replaces the current content of the store with those of the associated file.
  • Method Service.Save() writes the current content of the store to the associated file.
  • New method Service.Clear() empties the store.

On-disk stores are always loaded at startup and saved at shutdown. A clarifying comment has been added to the AutoSave option.

What if one wishes to load from disk, but then keep in-memory only or write to another file? There you go:

// Associate with <file1>
store := kvstore.NewWithConfig(&kvstore.Config{Filename: "<file1>"})

// Load from <file1>
err := store.Load() // handle error

// Reattach to <file2>, this time with autosave
store.Configure(&kvstore.Config{
	Filename: "<file2>",
	AutoSave: true,
})

// Save to <file2>
err := store.Save() // handle error

// Detach from <file2>, becomes in-memory store, loads and saves have no effect
store.Configure(nil)

Notice how Service.Configure changes the configuration but does not alter the state otherwise, i.e. all data is kept, no automatic loads or saves are performed.

log

  • Default configuration (nil) writes to application.DefaultLogger(slog.LevelInfo)
  • New level type log.Level publishes Debug/Info/Warning/Error constants to JS
  • New method Service.Log(ctx context.Context, level Level, message string, args ...any) logs with dynamic level. When called from JS, it receives the binding call context that links to the source window. Custom handlers may access this value.
  • New methods Service.DebugContext, Service.InfoContext etc., matching the slog API, accept a context; they are exported to js as Debug, Info etc and receive the binding call context.

The logger implements the slog.Leveler interface to provide dynamic level configuration to custom handlers. See doc comment of the Service.Level method for an example.

sqlite

  • Default configuration (nil) creates an in-memory database.
  • The database is opened automatically at startup and closed at shutdown.
  • Service.Configure method changes the configuration but does not close or reopen the DB.
  • Service.Open() opens (if closed) or reopens the DB, applying the latest configuration.
  • Service.Close() closes the DB if open; has no effect if closed.
  • Service.Select(...) renamed to Service.Query(...) for consistency with sql.DB.
  • New methods Service.ExecContext(...) and Service.QueryContext(...) accept a context for cancellation support. These are exported to JS as Exec and Query, hence cancellation is always supported from JS.
  • New method Service.Prepare(query string) (cancelable version Service.PrepareContext) creates prepared statements.

Normally statements must be closed manually, but they are closed automatically upon calling Service.Close(). Unlike regular sql.Stmts, their Close method is idempotent.

Advanced binding generator features are used to bring prepared statements to the JS side!

import { Service as sqlite } from "<bindings>/github.com/wailsapp/wails/v3/pkg/services/sqlite";

let stmt = await sqlite.Prepare("SELECT * FROM table WHERE id=?");
let result1 = await stmt.Query("id1");
let result2 = await stmt.Query("id2");
let promise = stmt.Query("id3");
promise.cancel(); // We're having second thoughts.
await promise;
await stmt.Close();

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Ran the services example and verified everything's working fine.

  • Windows
  • macOS
  • Linux

Test Configuration

 Wails (v3.0.0-dev)  Wails Doctor

# System

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| Name          | MacOS                                                                                                          |
| Version       | 12.6.6                                                                                                         |
| ID            | 21G646                                                                                                         |
| Branding      | Monterey                                                                                                       |
| Platform      | darwin                                                                                                         |
| Architecture  | amd64                                                                                                          |
| Apple Silicon | unknown                                                                                                        |
| CPU           | Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz                                                                       |
| CPU           | Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz                                                                       |
| GPU           |  cores, Metal Family: Supported, Metal GPUFamily macOS 2                                                       |
|       Metal Family: Supported, Metal GPUFamily macOS 2                                                                         |
| Memory        | 32 GB                                                                                                          |
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

# Build Environment

┌─────────────────────────────────────────────────────────┐
| Wails CLI    | v3.0.0-dev                               |
| Go Version   | go1.23.5                                 |
| Revision     | b88d57cbd960c985bd57bab3e13ead10d1618da9 |
| Modified     | false                                    |
| -buildmode   | exe                                      |
| -compiler    | gc                                       |
| CGO_CFLAGS   |                                          |
| CGO_CPPFLAGS |                                          |
| CGO_CXXFLAGS |                                          |
| CGO_ENABLED  | 1                                        |
| CGO_LDFLAGS  |                                          |
| GOAMD64      | v1                                       |
| GOARCH       | amd64                                    |
| GOOS         | darwin                                   |
| vcs          | git                                      |
| vcs.modified | false                                    |
| vcs.revision | b88d57cbd960c985bd57bab3e13ead10d1618da9 |
| vcs.time     | 2025-02-06T14:05:25Z                     |
└─────────────────────────────────────────────────────────┘

# Dependencies

┌───────────────────────────┐
| Xcode cli tools | 2395    |
| npm             | 10.8.2  |
| *NSIS           | v3.10   |
└─ * - Optional Dependency ─┘

# Checking for issues

 SUCCESS  No issues found

# Diagnosis

 SUCCESS  Your system is ready for Wails development!

Checklist:

  • I have updated docs/src/content/docs/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features

    • Introduced support for aarch64 AppImage builds and improved macOS application menu exclusivity.
    • Added dynamic configuration capabilities for services, including a new Configure method.
    • Enhanced logging service with dynamic log-level specification and improved error handling.
    • Implemented a new Stmt class for managing prepared statements in the SQLite service.
    • Added methods for managing key-value store operations, including Load, Clear, and Delete.
    • Updated method names for consistency across services, such as renaming Select to Query.
  • Bug Fixes

    • Improved panic handling and response management for unconfigured services.
  • Documentation

    • Updated changelog and tutorials to reflect new features, breaking changes, and service usage improvements.
  • Refactor

    • Streamlined service initialization methods and consolidated service functionality for better clarity and maintainability.

@coderabbitai

coderabbitai Bot commented Feb 13, 2025 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

Walkthrough

This pull request updates the documentation and service implementations across the codebase. It revises the changelog to document new features, breaking changes, and method renames. Several services—including fileserver, kvstore, sqlite, and log—have been refactored to adopt a consistent configuration approach using methods like NewWithConfig and Configure. The changes include updates to naming conventions (e.g., renaming GenerateQRCode to Generate, Select to Query), redefining exported entities, adding support for prepared statements in SQLite, and enhancing thread safety and error handling across multiple modules.

Changes

File Path(s) Change Summary
docs/src/content/docs/changelog.mdx Updated changelog with new features, breaking changes, method renames, and relocations; added Configure on services.
docs/src/content/docs/learn/services.mdx, docs/src/content/docs/tutorials/01-creating-a-service.mdx Revised documentation for service configuration; renamed constructor (New → NewWithConfig), updated QR service methods (GenerateQRCode → Generate, added URL and ServiceStartup), and removed obsolete startup methods.
v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/* Changed kvstore binding imports/exports; removed keyvaluestore.js; added service.js with key-value functions (Clear, Delete, Get, Load, Save, Set).
v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/* Replaced LoggerService with Service in bindings; exported new Level constants from models.js; removed loggerservice.js; added a new logging service implementation in service.js.
v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/* Added Row and Rows type definitions; updated service.js with methods like ExecContext, QueryContext, and Prepare functions; introduced new stmt.js for prepared statements.
v3/examples/services/assets/index.html Updated import statements and method calls for sqlite, kvstore, logger, and hash services (e.g., changed Select to Query, updated logging call syntax).
v3/examples/services/main.go Modified service instantiation to use NewWithConfig methods for sqlite, kvstore, and fileserver services.
v3/pkg/application/logger_dev.go, v3/pkg/application/logger_dev_windows.go, v3/pkg/application/logger_prod.go Updated DefaultLogger function signature to accept slog.Leveler instead of slog.Level.
v3/pkg/services/fileserver/fileserver.go Refactored fileserver service: replaced configuration field with an atomic pointer, introduced NewWithConfig and Configure, removed lifecycle methods (ServiceStartup, ServiceShutdown), added dummyHandler for unconfigured state.
v3/pkg/services/kvstore/kvstore.go Refactored KeyValueStore into Service; updated methods (e.g., Load, Save, Get, Set, Delete), and added Clear.
v3/pkg/services/log/log.go Redesigned logging service: defined Level type (with constants for Debug, Info, Warning, Error), introduced a new Service structure with dynamic logging methods.
v3/pkg/services/sqlite/sqlite.go Enhanced SQLite service: replaced DBFile with DBSource, added configuration handling, concurrency control, prepared statement support, and context-aware methods.
v3/pkg/services/sqlite/stmt.js Introduced a new Stmt class for managing prepared SQL statements with methods Close, Exec, and Query.
v3/examples/services/assets/bindings/log/slog/index.js Removed deprecated logging type definitions.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant FS as Fileserver Service
    Client->>FS: Call Configure(config)
    FS->>FS: Set atomic HTTP handler (or dummyHandler if config is nil)
    Client->>FS: Send HTTP Request
    FS->>Client: Serve request using configured handler (or return 503)
Loading
sequenceDiagram
    participant App as Application
    participant SQLite as SQLite Service
    App->>SQLite: Call PrepareContext("SQL Query")
    SQLite->>SQLite: Create prepared statement (Stmt)
    App->>SQLite: Call ExecContext/QueryContext with prepared Stmt and args
    SQLite->>App: Return execution result or query result
Loading

Suggested reviewers

  • leaanthony

Poem

Hop along, my coding friend,
New methods and changes on the bend,
Constructors now shine with a "WithConfig" glow,
Bugs hop away as improvements flow,
I nibble through code with carrot-like glee,
In a field of updates, I’m proud to be free!
🐇🥕


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82ed0e6 and f87dce1.

📒 Files selected for processing (3)
  • docs/src/content/docs/changelog.mdx (2 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js (0 hunks)
  • v3/examples/services/assets/index.html (3 hunks)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
docs/src/content/docs/changelog.mdx (1)

40-40: ⚠️ Potential issue

Remove duplicate entry.

The "New Menu guide" entry appears twice.

Apply this diff to remove the duplicate:

 - New Menu guide by [@leaanthony](https://github.com/leaanthony)
 - Better panic handling by [@leaanthony](https://github.com/leaanthony)
-  - New Menu guide by [@leaanthony](https://github.com/leaanthony)

Also applies to: 42-42

🧹 Nitpick comments (18)
v3/pkg/services/fileserver/fileserver.go (3)

22-26: Consider validating config in the constructor
While it’s fine to delegate all logic to Configure, adding basic validations (e.g., checking for invalid RootPath) can help catch errors early.


40-45: Handler reconfiguration design
Storing the http.FileServer or dummyHandler via an atomic pointer is concurrency-safe. This approach is consistent with the new design, but consider logging or returning an error if RootPath is invalid.


52-54: Clear fallback response
Returning 503 is a clear indication that the service is not configured, but consider providing more descriptive instructions to the client on how to configure it.

v3/pkg/services/log/log.go (2)

49-54: Helper constructor
Consider validating or cloning the config as soon as it's passed in, to ensure external modifications do not affect the Service.


67-83: Thread-safe configuration
Cloning the config to avoid external mutations is a good practice. However, consider logging if invalid levels or other anomalies are detected.

v3/pkg/services/kvstore/kvstore.go (4)

17-20: AutoSave mechanism
The auto-save explanation is helpful. Ensure that frequent writes are carefully managed for performance.


38-47: Initialize store with configuration
Creating an empty map upfront avoids nil checks later. Good approach.
You might want to log or handle missing file paths if config is provided but no filename is set.


93-125: File loading logic
Handling a missing file gracefully is user-friendly, but consider logging that the file doesn’t exist so users know why the data is empty.


128-136: On-disk persistence
Using JSON is a straightforward approach. Ensure large datasets do not cause performance issues or memory overhead.

v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js (2)

9-12: Consider removing @ts-ignore
You have an import of $models that is used below, so you may not need the @ts-ignore directive. If $models is definitely used, you can safely remove the Unused imports directive.


35-38: Add null guard
To fully support the doc statement about idempotency, you could add a null guard to handle null or already closed statements gracefully.

v3/pkg/services/sqlite/sqlite.go (4)

59-59: Added concurrency fields
The lock sync.RWMutex and stmts map[uint64]struct{} ensure safe concurrent usage. Keep an eye on read locks to prevent potential writer starvation.

Also applies to: 62-62


125-159: Open method
Ensures a DBSource is set, closes any existing connection, and opens a new one. The flow is correct. Consider logging any errors for better observability.


276-326: PrepareContext
Generates a new statement ID using nextId. The approach is robust, but watch out for potential ID exhaustion in extremely long-lived services.


428-437: Global concurrency structures
stmts sync.Map and nextId are shared globally. Revisit the design if memory usage grows over time or if multiple service instances are used concurrently.

v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/service.js (1)

66-69: Consider renaming the Set function to avoid shadowing.

The Set function shadows the global Set object, which could lead to confusion. Consider using a more specific name like SetValue or SetKey.

-export function Set(key, value) {
+export function SetValue(key, value) {
    let $resultPromise = /** @type {any} */($Call.ByID(2491766752, key, value));
    return $resultPromise;
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 66-66: Do not shadow the global "Set" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

docs/src/content/docs/learn/services.mdx (1)

471-471: Fix grammatical issues in the documentation.

Replace "an hardcoded" with "a hardcoded" in the following lines:

  • Line 471: "we used an hardcoded route"
  • Line 580: "in place of an hardcoded path"

Also applies to: 580-580

docs/src/content/docs/tutorials/01-creating-a-service.mdx (1)

212-212: Fix grammatical issue in the tutorial.

Add the missing pronoun "you" in the sentence:

  • Line 212: "If have a vanilla frontend" should be "If you have a vanilla frontend"
🧰 Tools
🪛 LanguageTool

[grammar] ~212-~212: It seems that a pronoun is missing.
Context: ...lable when using frontend bundlers. If have a vanilla frontend that does not e...

(IF_VB)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c85b10 and fe38cd9.

📒 Files selected for processing (25)
  • docs/src/content/docs/changelog.mdx (2 hunks)
  • docs/src/content/docs/learn/services.mdx (1 hunks)
  • docs/src/content/docs/tutorials/01-creating-a-service.mdx (12 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/index.js (1 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js (0 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/service.js (1 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js (1 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js (0 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/models.js (1 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/service.js (1 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js (1 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/models.js (1 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js (2 hunks)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/stmt.js (1 hunks)
  • v3/examples/services/assets/bindings/log/slog/index.js (0 hunks)
  • v3/examples/services/assets/index.html (3 hunks)
  • v3/examples/services/main.go (2 hunks)
  • v3/pkg/application/logger_dev.go (1 hunks)
  • v3/pkg/application/logger_dev_windows.go (1 hunks)
  • v3/pkg/application/logger_prod.go (1 hunks)
  • v3/pkg/services/fileserver/fileserver.go (2 hunks)
  • v3/pkg/services/kvstore/kvstore.go (2 hunks)
  • v3/pkg/services/log/log.go (1 hunks)
  • v3/pkg/services/sqlite/sqlite.go (3 hunks)
  • v3/pkg/services/sqlite/stmt.js (1 hunks)
💤 Files with no reviewable changes (3)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js
  • v3/examples/services/assets/bindings/log/slog/index.js
✅ Files skipped from review due to trivial changes (2)
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/index.js
  • v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/models.js
🧰 Additional context used
🪛 LanguageTool
docs/src/content/docs/tutorials/01-creating-a-service.mdx

[grammar] ~212-~212: It seems that a pronoun is missing.
Context: ...lable when using frontend bundlers. If have a vanilla frontend that does not e...

(IF_VB)


[misspelling] ~471-~471: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ...tions In the example above we used an hardcoded route /qrservice. If yo...

(EN_A_VS_AN)


[misspelling] ~580-~580: Use “a” instead of ‘an’ if the following word doesn’t start with a vowel sound, e.g. ‘a sentence’, ‘a university’.
Context: ...jsto use theURL` method in place of an hardcoded path: ```js title="front...

(EN_A_VS_AN)

🪛 Biome (1.9.4)
v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/service.js

[error] 66-66: Do not shadow the global "Set" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.23)
  • GitHub Check: Run Go Tests (macos-latest, 1.23)
  • GitHub Check: Run Go Tests (windows-latest, 1.23)
🔇 Additional comments (96)
v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/index.js (2)

5-8: LGTM! Service standardization looks good.

The changes align with the PR objectives of standardizing built-in services by introducing a consistent Service type.


10-12: LGTM! Level type export looks good.

The addition of the Level type export aligns with the PR objectives of enhancing the logging service with a new logging level type.

v3/pkg/services/fileserver/fileserver.go (6)

5-5: No immediate concerns about concurrency usage
The import of sync/atomic is appropriate for safe concurrent updates of the handler pointer.


9-9: Doc comment looks good
The added doc line clarifies how RootPath is used.


14-14: Ensure pointer defaults are always set
Storing a dummy handler is a good fallback and helps avoid nil pointer dereferences.


17-19: Chaining constructor approach
Returning NewWithConfig(nil) from New() is a neat approach for DRY.


35-39: Comprehensive documentation
The doc comments here clearly explain the fallback behavior if config is nil.


49-49: Safe atomic load usage
Using (*s.fs.Load()).ServeHTTP(w, r) ensures concurrency correctness. Good job.

v3/pkg/services/log/log.go (11)

7-7: Synchronized configuration pointer
Importing "sync/atomic" is consistent with the approach of using atomic.Pointer[Config] for concurrency.


12-23: Clear definition of log levels
Mapping Level to slog.Level ensures easy integration with built-in severity levels. Nice approach.


33-41: Wails injection approach
Exporting the context-bound methods as top-level is a neat design for bridging.


44-47: New service function
Chaining to NewWithConfig(nil) keeps constructor logic centralized.


56-60: Simple service naming
Returning a consistent plugin name is straightforward and aligns with the typical pattern. No concerns.


62-66: Comprehensive reconfiguration doc
The doc comments precisely explain the fallback when config is nil. Good clarity.


85-111: Extensive documentation
The doc block is very thorough. It’s helpful in explaining the dynamic log level usage.


113-118: Convenient alias
Exposing LogLevel() as an alias for the dynamic setting is user-friendly.


120-123: Runtime log level updates
Being able to adjust the log level at runtime is a powerful feature.


125-139: Flexible logging
Feeding the call context into the logger may be beneficial for structured logging. No issues found.


141-195: Convenience logging methods
Providing typed methods for each log level is a best practice and improves code readability.

v3/pkg/services/kvstore/kvstore.go (11)

14-14: Documentation for on-disk store
Clarity on the filename usage is good. This helps users understand how the store is persisted.


24-31: Service struct refactor
Lock-based concurrency combined with an unsaved flag is a straightforward design.
Consider an atomic.Bool for unsaved if you want more granular updates.


33-36: Default in-memory store
By returning NewWithConfig(nil), you ensure consistent initialization.


51-51: Explicit plugin name
No concerns with the updated service name.


55-59: Service startup loading
Wrapping the error from Load() ensures clarity on any failing condition. Good error-handling approach.


61-65: Service shutdown saving
Similarly wrapping the Save() error clarifies if there’s an issue persisting data at shutdown.


67-91: Dynamic reconfiguration
Preserving existing store data while reconfiguring is sensible. Note that if the store was unsaved, changes might be lost unless Save() was called.

Do you intend to automatically save before reconfiguring, or is the manual approach enough?


152-164: Retrieve operation
Returning the entire store when key is empty is a convenient feature. Just ensure there’s no privacy or security risk.


165-184: Set with autosave
Encapsulating everything in a closure ensures consistent locking. Well done.


186-206: Delete with autosave
Same pattern as Set. Straightforward and consistent.


208-229: Clear with autosave
Resetting data to a new map is simpler than manually deleting each key. Looks good.

v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js (15)

14-20: Documentation clarity
The doc block for Close is comprehensive. Consider explicitly mentioning concurrency behavior if Close is called multiple times concurrently.


28-34: Validate 'stmt' argument
The doc states ClosePrepared is idempotent, but there's no explicit check for null in the function. Consider verifying if stmt is non-null to avoid potential errors during $Call.ByID.


40-46: Doc coverage
The documentation for ExecContext is consistent and clear on early-cancellation behavior.


47-50: Implementation clarity
The usage of spread syntax for args is straightforward, and there is no apparent concurrency issue.


52-60: Doc completeness
ExecPrepared doc clearly outlines usage and cancellation. Good job.


61-64: Implementation matches doc
The function logically aligns with the published doc.


66-79: Concurrent calls
This doc for Open() is thorough. However, ensure to handle repeated calls from multiple threads, to preserve data integrity.


81-92: Doc presentation
PrepareContext doc has sufficient detail on concurrency, closure, and usage patterns.


93-96: Function usage
PrepareContext here is standard. No concerns.


98-108: QueryContext doc
Explicitly stating partial result returns on cancellation is valuable.


109-113: Typed results
Returning $models.Rows is consistent with other methods. No issues.


115-131: Thorough explanation
QueryPrepared doc is well-structured and clarifies concurrency.


137-140: Naming consistency
Re-exporting ExecContext as Execute and QueryContext as Query might confuse new devs but presumably maintains backward compatibility. Consider adding a comment clarifying.


142-143: Imported Stmt
Importing Stmt from ./stmt.js is consistent with your approach to prepared statements.


144-167: Wrapper design
Prepare function elegantly wraps PrepareContext. Overall design is cohesive.

v3/pkg/services/sqlite/sqlite.go (24)

5-5: Import usage
The new imports (bytes, fmt, sync, atomic, errors) are used appropriately for concurrency control and error wrapping.

Also applies to: 8-10, 12-12


18-22: DBSource doc
Clear explanation of how to use :memory: and parameterization with modernc.org/sqlite.


25-48: Generated code
These wails:inject lines appear to be auto-generated bindings.


65-68: New function
Initializing the service with default config is a user-friendly approach.


70-81: NewWithConfig function
Falling back to an in-memory database is a sensible default.


89-93: ServiceStartup
Automatically calling Open() is consistent with the design. Consider robust error handling if Open() fails.


95-99: ServiceShutdown
Calls Close() for cleanup. Make sure the application handles any returned errors gracefully.


101-123: Configure method
Preserves open connections while changing config. The doc clarifies the reconnection step. Implementation is neat.


161-167: Close method
Leverages closeImpl() under a lock. The concurrency approach is sound.


174-200: closeImpl
Iterates over s.stmts and closes them. The approach to skip calling Stmt.Close() directly to avoid deadlocks is wise.


202-208: Execute
Simple wrapper for ExecContext. Implementation is consistent with typical designs.


209-229: ExecContext
Cancels gracefully if ctx is canceled. Good use of read lock for concurrency.


230-236: Query
Wrapper for QueryContext. Straightforward approach.


238-262: QueryContext
Partial result return on context cancellation is user-friendly. Ensures no open connection error is returned.


264-274: Prepare
Uses PrepareContext behind the scenes. Clean code.


328-336: ClosePrepared
Delegates to stmt.Close(). Straight to the point.


338-357: ExecPrepared
Checks for null statements, gracefully handles context cancellation. Good design.


359-382: QueryPrepared
Follows the same pattern as ExecPrepared. The final partial result approach is consistent.


384-391: Row and Rows
Using map[string]any for each row is a flexible approach.


394-426: parseRows
Implementation scans each row into a map. The context check inside Next() loop is a nice touch for partial results.


438-440: init()
Initializes nextId to 1. Straightforward.


442-454: Stmt struct
Associates an id for concurrency tracking. The design is well-structured for prepared statements.


456-474: Stmt.Close
Properly removes the statement from global sync.Map and local map. Watch for potential repeated calls if a context is canceled mid-statement.


476-500: MarshalText, UnmarshalText
Enables statements to be transferred or stored by ID. This is helpful for advanced usage. Implementation seems correct.

v3/pkg/application/logger_prod.go (1)

10-10: Flexibility in logging level
Switching from slog.Level to slog.Leveler broadens the range of accepted log levels. Good improvement.

v3/pkg/application/logger_dev.go (1)

14-14: Great improvement in flexibility!

The change from slog.Level to slog.Leveler interface type makes the function more flexible while maintaining backward compatibility, as it allows for both built-in and custom log levels.

v3/pkg/application/logger_dev_windows.go (1)

15-15: LGTM!

The changes are consistent with logger_dev.go, making the function more flexible by accepting the slog.Leveler interface.

v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/index.js (1)

10-22:

Details

✅ Verification successful

LGTM! Verify auto-generation.

The added type definitions enhance type safety for SQLite query results. Since this is an auto-generated file, ensure that the generation process is working correctly.

Run the following script to verify the auto-generation process:


🏁 Script executed:

#!/bin/bash
# Description: Verify that the file is auto-generated correctly.

# Test: Check if there are any other auto-generated files with similar patterns
fd -e js -x grep -l "This file is automatically generated" {}

Length of output: 66233


Auto-generation process confirmed – no changes required.

The file under review has been verified against the auto-generation signature across the codebase. The executed script found this file among other auto-generated files, confirming that the generation process is functioning correctly. The added type definitions also enhance type safety as expected.

v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/models.js (2)

9-15: Great improvement in type safety!

The change from any to number type enhances type safety for log levels.


17-26:

Details

✅ Verification successful

LGTM! Verify auto-generation.

The predefined log level constants follow a consistent pattern with 4-unit increments, aligning with standard logging practices where higher values indicate more severe levels.

Run the following script to verify the auto-generation process:


🏁 Script executed:

#!/bin/bash
# Description: Verify that the file is auto-generated correctly and check for similar patterns in other service models.

# Test: Check if there are any other auto-generated model files with similar patterns
fd -e js -x grep -l "This file is automatically generated" {} | grep "models.js"

Length of output: 17377


LGTM. Auto-generation Verified

The predefined log level constants (Debug: -4, Info: 0, Warning: 4, Error: 8) follow the expected 4-unit increment pattern, consistent with standard logging practices. The verification script confirms that this file is auto-generated—as seen by its presence alongside many similar auto-generated model files—ensuring consistency across the codebase.

  • File: v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/models.js
  • Verification: Auto-generation markers are detected in this file, matching the pattern used in other service model files.
v3/examples/services/main.go (2)

5-8: LGTM!

The added imports are necessary for the enhanced functionality: log/slog for logging, os for system operations, path/filepath for path manipulation, and runtime for runtime information.


39-41: LGTM! Service initialization changes align with standardization goals.

The changes consistently use NewWithConfig across all services, improving API consistency. The configuration updates (e.g., DBFile → DBSource) reflect the enhanced service APIs.

Also applies to: 42-45, 47-51

v3/pkg/services/sqlite/stmt.js (4)

1-8: LGTM! Well-structured TypeScript setup.

The TypeScript configuration and symbol declarations provide proper type safety and encapsulation.


10-17: LGTM! Clear and comprehensive documentation.

The class documentation clearly explains the purpose, concurrency support, and resource management requirements.


25-46: LGTM! Well-implemented constructor with proper encapsulation.

The constructor properly initializes private members using symbols and includes comprehensive type annotations.


53-78: LGTM! Robust method implementations.

The methods are well-implemented with:

  • Support for early cancellation
  • Proper type annotations
  • Clear documentation
  • Consistent error handling
v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/stmt.js (1)

1-79: Duplicate file - no additional review needed.

This file is identical to v3/pkg/services/sqlite/stmt.js and appears to be auto-generated bindings.

v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/service.js (1)

13-16: LGTM! Well-implemented service operations.

The functions are well-implemented with:

  • Clear documentation
  • Proper type annotations
  • Support for cancellation
  • Consistent error handling

Also applies to: 23-26, 33-36, 45-48, 55-58

v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/service.js (7)

19-22: LGTM!

The function is well-documented with JSDoc comments and correctly implements the Debug level logging functionality.


30-33: LGTM!

The function is well-documented with JSDoc comments and correctly implements the Error level logging functionality.


41-44: LGTM!

The function is well-documented with JSDoc comments and correctly implements the Info level logging functionality.


64-67: LGTM!

The function is well-documented with JSDoc comments and correctly implements the dynamic level logging functionality.


75-78: LGTM!

The function is well-documented with JSDoc comments and correctly implements the log level retrieval functionality.


85-88: LGTM!

The function is well-documented with JSDoc comments and correctly implements the log level modification functionality.


96-99: LGTM!

The function is well-documented with JSDoc comments and correctly implements the Warning level logging functionality.

v3/examples/services/assets/index.html (2)

9-12: LGTM!

The import statements have been correctly updated to use the standardized Service export from each service module.


44-44: LGTM!

The method calls have been correctly updated to use the new standardized method names and service interfaces.

Also applies to: 149-159

docs/src/content/docs/changelog.mdx (4)

30-32: LGTM! Breaking changes are well documented.

The breaking changes section clearly documents the service method renames and relocations, which align with the PR objectives of standardizing the API.


55-65: LGTM! Service enhancements are well documented.

The additions section effectively documents the new service features:

  • Configure method for dynamic reconfiguration
  • Default behaviors for unconfigured services
  • New service methods and their purposes

67-91: LGTM! Bug fixes are well documented.

The fixes section comprehensively documents improvements to binding generation and model file handling, which are crucial for service reliability.

🧰 Tools
🪛 LanguageTool

[grammar] ~70-~70: The correct preposition appears to be “on”.
Context: ...2) - Updated the minimum system version in macOS .plist files from 10.13.0 to 10.1...

(IN_WINDOWS)


104-106: LGTM! Service standardization changes are well documented.

The changes section effectively documents the standardization efforts:

  • Consistent Service type naming
  • Standardized NewWithConfig function naming
  • Improved method naming for better API clarity

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
docs/src/content/docs/tutorials/01-creating-a-service.mdx (3)

492-513: Add input validation to prevent invalid QR code generation attempts.

The QRService struct and its constructor could benefit from input validation to ensure proper usage.

Consider adding size validation in the constructor and methods:

 // QRService handles QR code generation
 type QRService struct {
     route string
+    minSize int
+    maxSize int
 }

 // NewQRService creates a new QR service
 func NewQRService() *QRService {
-    return &QRService{}
+    return &QRService{
+        minSize: 128,  // Minimum QR code size
+        maxSize: 1024, // Maximum QR code size
+    }
 }

582-605: Enhance error handling and user feedback.

The frontend implementation could benefit from better error handling and loading state management.

Consider these improvements:

 async function generateQR() {
     const text = document.getElementById('text').value;
     if (!text) {
         alert('Please enter some text');
         return;
     }

     const img = document.getElementById('qrcode');
+    const button = document.getElementById('generateButton');
+    
+    // Disable button and show loading state
+    button.disabled = true;
+    img.src = 'loading-spinner.gif';  // Add a loading spinner image
+    
     try {
         // Invoke the URL method to obtain an URL for the given text.
         img.src = await QRService.URL(text, 256);
     } catch (error) {
+        // Show user-friendly error message
+        img.src = '';
+        alert('Failed to generate QR code. Please try again with different text.');
+        console.error('QR generation failed:', error);
     } finally {
+        button.disabled = false;
     }
 }

611-616: Add more details about error handling best practices.

While the note about error handling is helpful, it could be expanded to cover more scenarios and best practices.

Consider adding examples of:

  • Different types of errors that might occur
  • How to handle them gracefully in the frontend
  • How to provide meaningful error messages to users
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe38cd9 and 8f815e9.

📒 Files selected for processing (1)
  • docs/src/content/docs/tutorials/01-creating-a-service.mdx (12 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.23)
  • GitHub Check: Run Go Tests (windows-latest, 1.23)
🔇 Additional comments (1)
docs/src/content/docs/tutorials/01-creating-a-service.mdx (1)

469-489: LGTM! Clear explanation of dynamic configuration support.

The documentation effectively explains the dynamic configuration approach, which aligns well with the PR's standardization goals.

Comment thread docs/src/content/docs/tutorials/01-creating-a-service.mdx
# Conflicts:
#	docs/src/content/docs/changelog.mdx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
docs/src/content/docs/learn/services.mdx (2)

162-164: New Config Structure Added
The new Config struct with the RootPath field is clear and concise. However, consider documenting or validating the case when RootPath is empty since the PR summary mentions that the service should return a 503 response when unconfigured.


170-174: Renamed Constructor: NewWithConfig
The NewWithConfig function correctly uses the provided configuration to initialize the file server. As mentioned earlier, consider adding error handling (or at least a note) for cases where config.RootPath might be empty so that a proper 503 response can be returned in such scenarios.

docs/src/content/docs/changelog.mdx (1)

63-73: Detailed New Feature Additions
The added changelog items from lines 63 to 73 comprehensively document new features and improvements including:

  • The addition of the Configure method for dynamic reconfiguration.
  • The fileserver service now returning a 503 when unconfigured.
  • The updates to the kvstore (in-memory default, Load and Clear methods), log service (Level and Log), and sqlite service (in-memory default, Close, cancellation support, and prepared statement support).

These entries are clear; however, consider double-checking that the descriptions fully capture the runtime behavior changes (for instance, the handling of empty configurations in the fileserver) and that the wording consistently aligns with usage in the implementation.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f815e9 and 82ed0e6.

📒 Files selected for processing (2)
  • docs/src/content/docs/changelog.mdx (2 hunks)
  • docs/src/content/docs/learn/services.mdx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Run Go Tests (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests (macos-latest, 1.24)
  • GitHub Check: Run Go Tests (windows-latest, 1.24)
🔇 Additional comments (4)
docs/src/content/docs/learn/services.mdx (3)

166-168: Service Struct Simplification
The updated Service struct now only holds the fs http.Handler, which simplifies its design. Ensure that any additional context (such as default handling when no root is provided) is covered elsewhere in the documentation.


176-178: ServiceName Method
Returning a fully qualified service name here helps with clear identification and logging. This implementation is straightforward and aligns well with the overall API standardization.


180-182: ServeHTTP Delegation
The ServeHTTP method simply delegates HTTP requests to the internal file server handler. This is a clean and effective design.

docs/src/content/docs/changelog.mdx (1)

123-125: Standardizing Naming Conventions
The renaming of built-in service types and constructor functions – changing to Service and NewWithConfig respectively, as well as renaming the Select method to Query on the sqlite service – is clearly documented here. This aligns with the objective to standardize the API and should help avoid confusion.

@fbbdev fbbdev mentioned this pull request Feb 25, 2025
15 tasks done
leaanthony
leaanthony previously approved these changes Mar 1, 2025
# Conflicts:
#	docs/src/content/docs/changelog.mdx
#	v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/kvstore/keyvaluestore.js
#	v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/log/loggerservice.js
#	v3/examples/services/assets/bindings/github.com/wailsapp/wails/v3/pkg/services/sqlite/service.js
@leaanthony
leaanthony merged commit 96fa0d2 into wailsapp:v3-alpha Mar 1, 2025
@fbbdev
fbbdev deleted the v3-alpha-feature/service-api branch March 1, 2025 09:32
Grantmartin2002 pushed a commit to Grantmartin2002/wails that referenced this pull request Apr 29, 2026
…-api

[v3] Built-in service standardisation and enhancement
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.

2 participants