[ ](https://github.com/biggaboy212/Cascade/edit/main/docs/Components/TitleStack.md "Edit this page") [ ](https://github.com/biggaboy212/Cascade/raw/main/docs/Components/TitleStack.md "View source of this page")

# TitleStack

A `TitleStack` is a vertical stack with title and subtitle label's that people can read, but not edit.

## Summary

### Properties

Property | Type | Description
---|---|---
`Title` | `string?` | The text content of the title label
`Subtitle` | `string?` | The text content of the subtitle label. Subtitle becomes non visible if left nil.

[View all inherited from `BaseComponent`](https://biggaboy212.github.io/Cascade/Components/#properties)

[View all inherited from `Frame`](https://create.roblox.com/docs/reference/engine/classes/Frame#summary-properties)

### Methods

[View all inherited from `Frame`](https://create.roblox.com/docs/reference/engine/classes/Frame#summary-methods)

### Events

[View all inherited from `Frame`](https://create.roblox.com/docs/reference/engine/classes/Frame#summary-events)

## Types

    type TitleStackProperties = Frame & {
        Title: string?,
        Subtitle: string?,

    type TitleStack = BaseComponent & Components & TitleStackProperties

### Function Signature

    function(self, properties: TitleStackProperties?): TitleStack

## Example

    local titleStack = row:Left():TitleStack({
        Title = "Toggle (Off)",
        Subtitle = "Lets people choose between a pair of opposing states, like on and off, using a different appearance to indicate each state.",
    })

    print(titleStack:IsA("Frame")) --> true
    print(titleStack.ClassName) --> "Frame"
    print(titleStack.Type) --> "TitleStack"

Back to top


[ ](https://github.com/biggaboy212/Cascade/edit/main/docs/Getting Started/The API/standalone.md "Edit this page") [ ](https://github.com/biggaboy212/Cascade/raw/main/docs/Getting Started/The API/standalone.md "View source of this page")

# The Standalone API

The Standalone API lets you create individual Cascade components without a full [App](https://biggaboy212.github.io/Cascade/Getting%20Started/The%20API/app/). This gives you access to the component primitives without the overhead of an app.

## Summary

### Properties

Property | Type | Description
---|---|---
Theme | `Theme?` | Light or Dark mode. See [Themes](https://biggaboy212.github.io/Cascade/Getting%20Started/The%20API/themes/). Defaults to Light.
Accent | `Accent?` | Accent color palette. See [Accents](https://biggaboy212.github.io/Cascade/Getting%20Started/The%20API/accents/).
Parent | `Instance?` | Default parent for components created on this context.

## Types

    type ComponentProperties = {
        Theme: Theme?,
        Accent: Accent?,
        Parent: Instance?,

    type ComponentContext = ComponentProperties & Components

### Function Signature

    function(properties: ComponentProperties?): ComponentContext

## Examples

### Creating standalone components

    local ctx = cascade.Component({
        Theme = cascade.Themes.Dark,
        Accent = cascade.Accents.Blue,
    })

    -- Returns our wrapped object and the raw Roblox instance
    local object, instance = ctx:Toggle({
        Parent = someFrame,
        Value = true,
        ValueChanged = function(self, value: boolean)
            print("Value changed:", value)
        end,
    })

    print(object.__instance)

### Shared parent

If most of your components share the same parent, you can set `Parent` on the context itself:

    local ctx = cascade.Component({
        Theme = cascade.Themes.Dark,
        Parent = someFrame,
    })

    -- Both components are automatically parented to someFrame
    local toggle = ctx:Toggle({ Value = false })
    local slider = ctx:Slider({ Value = 0.5, Minimum = 0, Maximum = 1 })

Note

You can still override `Parent` for a shared component group.

### Different contexts

    -- This one will use the `Light` theme with the `Red` accent.
    do
        local ctx = cascade.Component({
            Theme = cascade.Themes.Light,
            Accent = cascade.Accents.Red,
        })

        local toggle = ctx:Toggle({ Value = false })
    end

    -- This one will use the `Dark` theme with the `Blue` accent.
    do
        local ctx = cascade.Component({
            Theme = cascade.Themes.Dark,
            Accent = cascade.Accents.Blue,
        })

        local toggle = ctx:Toggle({ Value = true })
    end

Back to top


[ ](https://github.com/biggaboy212/Cascade/edit/main/docs/Getting Started/The API/app.md "Edit this page") [ ](https://github.com/biggaboy212/Cascade/raw/main/docs/Getting Started/The API/app.md "View source of this page")

# App

You can create a new app by calling the `New` method from the Cascade API. This returns a custom object merged with a `ScreenGui`.

The returned object exposes all built-in [Components](https://biggaboy212.github.io/Cascade/Components/) such as [Window](https://biggaboy212.github.io/Cascade/Components/Window/), [Tab](https://biggaboy212.github.io/Cascade/Components/Tab/), and others for UI composition.

## Summary

### Properties

Property | Type | Description
---|---|---
WindowPill | `boolean?` | Whether or not the window minimize/restore pill should be visible.
Theme | `Theme?` | Light or Dark mode. See [Themes](https://biggaboy212.github.io/Cascade/Getting%20Started/The%20API/themes/)
Accent | `Accent?` | Accent color palette. See [Accents](https://biggaboy212.github.io/Cascade/Getting%20Started/The%20API/accents/)

[View all inherited from ScreenGui](https://create.roblox.com/docs/reference/engine/classes/ScreenGui#summary-properties)

### Methods

[View all inherited from ScreenGui](https://create.roblox.com/docs/reference/engine/classes/ScreenGui#summary-methods)

### Events

[View all inherited from ScreenGui](https://create.roblox.com/docs/reference/engine/classes/ScreenGui#summary-events)

## Types

    type AppProperties = ScreenGui & {
        WindowPill: boolean?,
        Theme: Theme?,
        Accent: Accent?,

    type App = AppProperties & Components

### Function Signature

    function(self, properties: AppProperties): App

## Example

    local app = cascade.New({
        WindowPill = true,
        Theme = cascade.Themes.Light,
        Accent = cascade.Accents.Blue,
    })

Back to top


[ ](https://github.com/biggaboy212/Cascade/edit/main/docs/Components/Section.md "Edit this page") [ ](https://github.com/biggaboy212/Cascade/raw/main/docs/Components/Section.md "View source of this page")

# Section

A `Section` organizes sidebar content by grouping related tabs together with optional disclosure.

## Summary

### Properties

Property | Type | Description
---|---|---
`Title` | `string?` | The displayed section title.
`Disclosure` | `boolean?` | Whether the section's content should be hideable. Defaults to false.
`Expanded` | `boolean?` | Whether the section should be expanded by default. Defaults to true.

[View all inherited from `BaseComponent`](https://biggaboy212.github.io/Cascade/Components/#properties)

[View all inherited from `Frame`](https://create.roblox.com/docs/reference/engine/classes/Frame#summary-properties)

### Methods

[View all inherited from `Frame`](https://create.roblox.com/docs/reference/engine/classes/Frame#summary-methods)

### Events

[View all inherited from `Frame`](https://create.roblox.com/docs/reference/engine/classes/Frame#summary-events)

## Types

    type SectionProperties = Frame & {
        Title: string?,
        Disclosure: boolean?,
        Expanded: boolean?,

    type Section = BaseComponent & Components & SectionProperties

### Function Signature

    function(self, properties: SectionProperties?): Section

## Example

    local section = window:Section({
        Disclosure = true,
        Title = "Settings",
    })

    print(section:IsA("Frame")) --> true
    print(section.ClassName) --> "Frame"
    print(section.Type) --> "Section"

Back to top


# picomatch

Status: :heavy_check_mark: Ported

Source: https://github.com/micromatch/picomatch/tree/2.3.1

Version: v2.3.1

### :pencil2: Notes

### :x: Excluded

### :package: [Dependencies](https://github.com/micromatch/picomatch/blob/2.3.1/package.json)

| Package | Version | Status | Notes |
| ------- | ------- | ------ | ----- |


# Fast Minkowski Shapecast

## Overview
Fast Minkowski Shapecast is a fast linear convex shapecasting algorithm written in LuaU

https://github.com/user-attachments/assets/313bd7fc-8829-4695-80e3-bc6b95493fe2

## Usage

```luau
local ReplicatedStorage = game:GetService 'ReplicatedStorage'

local FMShapecast = require(ReplicatedStorage:WaitForChild 'FMShapecast')
local World = FMShapecast() --// Creates a new geometry world

local sphereShape = World.createShape 'Ball' --// Creates a sphere shape
sphereShape.Transform = CFrame.new(0, 50, 0)
sphereShape.Radius = 1
sphereShape.Margin = .01

local shapecastParams: FMShapecast.ShapecastParams = {}
shapecastParams.FilterDescendantsInstances = {}
shapecastParams.FilterType = Enum.RaycastFilterType.Include
shapecastParams.MaxParts = 10
shapecastParams.RespectCanCollide = true
shapecastParams.Tolerance = .01
shapecastParams.CollisionGroup = 'Default'
shapecastParams.DepthContact = false

local shapecastResult = World:Shapecast(sphereShape, Vector3.new(0, -999, 0), shapecastParams)
if shapecastResult then
    print('Distance', shapecastResult.Distance)
    print('Instance', shapecastResult.Instance)
    print('Material', shapecastResult.Material)
    print('Position', shapecastResult.Position)
    print('Normal', shapecastResult.Normal)
end

## Limitations
- Doesn't work with meshes or union operations
- Doesn't work with terrain

## Example Place
[Bouncy Spheres](https://www.roblox.com/games/140565669830337/FMShapecast-Bouncy-Spheres)


# chrome.luau

This is an implementation of the Chrome DevTools Protocol written in Luau for Lune, based on [rust-headless-chrome](https://github.com/rust-headless-chrome/rust-headless-chrome).

It allows you to control a Chromium browser with Luau code thanks to Lune's networking features, which is useful for web-crawling, automating tasks, authentication and basically everything else you can do with a browser.

Below you will find a quick snippet showcasing its usage. You can also take a peek inside the `examples` folder.

## Quick Start

```luau
local fs = require("@lune/fs")
local chrome = require("./lune_packages/chrome")

local browser = chrome.browser.create()
local tab = browser:create_tab()

-- Navigate to wikipedia
tab:navigate_to("https://www.wikipedia.org"):wait_until_navigated()

-- Wait for network/javascript/dom to make the search-box available
-- and click it.
tab:wait_for_element("input#searchInput"):click()

-- Type in a query and press `Enter`
tab:type_text("WebKit"):press_key("Enter"):wait_until_navigated()

-- We should end up on the WebKit page once navigated
local element = tab:wait_for_element("#firstHeading")
assert(string.sub(tab:get_url(), -6) == "WebKit")

-- Take a screenshot of the entire browser window
local jpeg = tab:capture_screenshot({ format = "jpeg", from_surface = true })
-- Save the screenshot to disk
fs.writeFile("screenshot.jpeg", jpeg)

-- Take a screenshot of just the WebKit infobox
local png = tab:wait_for_element("#mw-content-text > div > table.infobox.vevent"):capture_screenshot({ format = "png" })
-- Save the screenshot to disk
fs.writeFile("screenshot.png", png)

-- Run JavaScript in the page
local result = element:call_js_fn([[
    function getIdTwice() {
        // `this` is always the element that you called `call_js_fn` on
        const id = this.id;
        return id + id;
]])

assert(result.value == "firstHeadingfirstHeading")

## Installation

As of now, chrome.luau only supports pesde as a package manager.
To install the package, just execute the following command.

```sh
pesde add synpixel/chrome

## Features

- [x] Type safety
- [x] Launch a browser instance
- [x] Connect to an existing browser instance
- [x] Headless browsing (default)
- [x] DOM manipulation
- [x] Dispatch user input
- [x] Access and modify storage
- [x] Intercept and monitor network traffic
- [ ] (To-Do) Auto-install a browser


# **Roblox Data Stores Batch Processor CLI: Technical Guide**

**⚠ Use At Your Own Risk**

This CLI is a powerful tool to help creators better manage data in data stores. It directly interacts with your live experience data at bulk.

Batch data processing is a very complex process, and many things can go wrong. **Improper use, incorrect configurations, or errors in custom callback functions can result in permanent, irreversible data loss.** It is strongly recommended to test thoroughly on a test experience first.

**Please read this entire guide carefully before using this tool.** Please be especially cautious if you are doing a data migration while your experience is still reading / writing those keys. You will need to implement handling for this — this is called a live-path migration and is covered later in **Section 10**.

By using this tool, you acknowledge and accept all risks.

Welcome to the official guide for the Roblox Data Stores Batch Processor Command-Line Interface (CLI). This document provides technical guidance for installing, configuring, and operating the tool.

This tool is open source under the MIT License! Feel welcome to fork the repository and stage a pull request with your contributions. Please see the **[LICENSE](./LICENSE)** for details. This tool
uses third-party
modules; their licenses are listed in **[ATTRIBUTIONS.md](./ATTRIBUTIONS.md)**.

## **1\. Overview**

The Batch Processor CLI is a powerful command-line tool built on the Lune runtime. It performs large-scale, custom operations on your experience's data stores by leveraging memory stores, data stores, and `LuauExecutionSessionTasks` Open Cloud APIs.

The CLI orchestrates the entire workflow, from task creation to execution, while providing you with the necessary tools to actively manage and monitor your running batch processes.

### **1.1. Use Cases**

The tool allows you to run custom Luau logic across all keys within a data store or across all data stores within an experience. This enables a variety of powerful data operations, including:

- **Schema Migrations:** Updating the data structure for thousands or millions of user profiles after an update.
- **Bulk Data Deletion:** Removing specific data fields from all user accounts for data cleanup or compliance, or clearing obsolete data stores.

If you are a DataStore2 or Berezaa Method user, this tool will be especially useful for data migration and cleanup\!

### **1.2. How It Works**

At a high level, the tool operates by creating and managing `LuauExecutionSessionTasks` to execute your custom Luau scripts. The batch process is broken down into two stages:

- **Stage 1: Scan and Queue**
  The first stage scans your experience to identify all target data stores or keys based on your provided criteria. It then populates a memory store queue with these items, which are considered "jobs" for the next stage. Stage 1 also accumulates progress as jobs are completed, updating overall batch process state.
- **Stage 2: Dequeue and Process**
  The second stage dequeues the jobs created in Stage 1 and executes your custom Luau logic for each item within a job. This is where your data manipulation, migration, or analysis takes place.

If the batch process runs to completion, at-least-once processing is guaranteed.

## **2\. Getting Started**

Follow these steps to set up the CLI and prepare your environment.

### **2.1. Prerequisites**

1. **Unzip Module:** Unzip the provided Batch Processing module and save it to your local machine.
2. **Install Lune:** [Install the Lune runtime](https://lune-org.github.io/docs/getting-started/1-installation) on your machine. This is required to execute the tool.
3. **Configure Open Cloud API Key:** The tool requires an Open Cloud API Key with specific permissions.
   - Navigate to the [Creator Hub API Extensions](https://create.roblox.com/dashboard/credentials).
   - Create a new API Key or edit an existing one.
   - Grant the key the necessary permissions for your target experience, as detailed in **Appendix A: API Key Permissions**.
   - Save the generated API key to a secure location.

### **2.2. Running a Batch Process**

1. **Set API Key Environment Variable:** Before running a command, set your API Key as an environment variable. You can do this directly in your terminal, or see **Appendix F: Tips and Tricks** for
   further ways of securing your API Key.
   - **macOS/Linux (sh, bash, zsh):**
     `export API_KEY="<Your-API-Key>"`
   - **Windows (PowerShell):**
     `$env:API_KEY="<Your-API-Key>"`
2. **Create a Callback Function:** Write your custom processing logic in a Luau file. See **Appendix C: Custom Callback Function** for requirements and examples. This callback function will be called on **every key / data store scanned by the batch processor**. Each item will be processed independently.
3. **Execute Command:** Navigate to the top-level directory of the unzipped module and run one of the available commands (process-keys or process-data-stores). For example, call
   - `lune run batch-process process-keys mybatchprocess -c myconfig.json`

### **2.3 Foreground Process**

The batch process runs actively in the foreground, and you must **keep the terminal session open** for the entire duration of the job.

This is because the CLI performs a critical "keep-alive" function. Today Session Tasks have a finite lifetime (a 5-minute time limit) and can terminate for various reasons. The CLI actively monitors
these sessions and automatically restarts any that die to ensure that long-running batch processes can continue until completion. Closing the terminal will terminate this orchestration process. Batch
process state is stored in memory stores, so if the terminal closes, you can **resume** a previously-running batch process.

Note that if you exit out of the foreground process, the batch process will continue to execute until all Session Tasks terminate organically.

### **2.4 Cleaning up a Batch Process**

After a batch process finishes or fails, the process name continues to be reserved until the process is cleaned up. This allows you to review and keep a record of previously completed batch processes. To cleanup a batch process, you can call the `cleanup` command like so:

- `lune run batch-process cleanup mybatchprocess -c myconfig.json`

“Cleaning up” a batch process implies deleting the data stores / memory stores resources used for execution – see **Appendix D** for information on these resources.

## **3\. Best Practices**

Before executing a large-scale batch process on your production data, it is crucial to follow these testing and verification steps to prevent unintended consequences.

- **Test on a Non-Production Experience First**
  Always run your batch process against a test data store with a smaller, representative dataset. This allows you to validate your callback logic and tune configurations in a safe environment without impacting live user data. Verify that the batch process is running using the [**Data Stores Observability Dashboard**](https://create.roblox.com/docs/cloud-services/data-stores/observability), and verify any changes to your data stores manually in [**Data Stores Manager**](https://create.roblox.com/docs/cloud-services/data-stores/data-stores-manager). Also ensure that your memory stores usage and error rate from the batch process is at acceptable levels with the [**Memory Stores Observability Dashboard**](https://create.roblox.com/docs/cloud-services/memory-stores/observability).
- **Perform a Scope Test**
  Perform a “scope test” by running a batch process with an empty callback function on your keys / data stores. Given your `--key-prefix` or `--data-store-prefix`, observe how many items get captured by the batch process and ensure it coincides with your expected count.
- **Perform a Spot Test**
  Once you are confident in your script and configurations, perform a limited "spot test" on your production environment. Use the `--key-prefix` or `--data-store-prefix` options to target a single, specific data point (e.g., your own test account’s key). Verify that the operation completes successfully and has the intended effect on that single item before running it on the entire dataset.
- **Verify All Configurations**
  Before initiating a full run, double-check all provided configurations—either in your config file or as command-line options. **We have picked default configurations that work for most use cases, but they may not work for yours\!** Ensure all values are within reasonable limits for your use case and that you understand the performance implications of all configurations.
- **Monitor Batch Process Runs**
  After starting a batch process, monitor the run for several minutes before walking away. Follow the same steps as discussed above in **Test on a Non-Production Experience First**.

## **4\. Command Reference**

### **4.1. process-keys**

Starts a batch process that iterates over key names within a single standard data store.

**Usage Examples:**

```sh
# Using direct command-line options
lune run batch-process process-keys  --data-store-name <name> [other-options...]

# Using a configuration file
lune run batch-process process-keys  --config <filepath>

**Arguments:**

- **``**: The unique name of the new batch process. The process name can be
  at most 18 characters long.

**Command-Specific Options:**

- **`--data-store-name / -d` :** The name of the data store to scan.
- **`--data-store-scope / -s` :** The scope of the data store.
- **`--key-prefix / -P` :** A prefix to filter which keys are processed.
- **`--exclude-deleted-keys` :** A flag to exclude keys that have been previously deleted.

**Shared Options:**
This command also accepts all required options (`--universe-id`, etc.) and all optional processing configurations. **See Appendix B: Configuration Glossary** for the full list.

### **4.2. process-data-stores**

Starts a batch process that iterates over standard data store names within an experience.

**Usage Examples:**

```sh
# Using direct command-line options
lune run batch-process process-data-stores  --data-store-prefix  [other-options...]

# Using a configuration file
lune run batch-process process-data-stores  --config <filepath>

**Arguments:**

- **``** : The unique name of the new batch process. The process name can be
  at most 18 characters long.

**Command-Specific Options:**

- **`--data-store-prefix / -P`** : A prefix to filter which data stores are processed.

**Shared Options:**
This command also accepts all required options (`--universe-id`, etc.) and all optional processing configurations. See **Appendix B: Configuration Glossary** for the full list.

### **4.3. resume**

Resumes a previously running batch process that has not completed. A batch process is considered incomplete if it fails, or if the terminal is closed and the session tasks time out before all items can be processed.

**Usage:**

```sh
# Using direct command-line options
lune run batch-process resume  --universe-id

# Using a configuration file
lune run batch-process resume  --config <filepath>

**Arguments:**

- **``** : The unique name of the previously run batch process to resume.

**Option:**

- **`--universe-id / -u`** : The universe to load / resume the batch process on.

**Shared Options:**
When resuming a process, all original configurations are loaded from the saved state. You can optionally override any of the following configurations:

- `numProcessingInstances`
- `outputDirectory`
- `errorLogMaxLength`
- `jobQueueMaxSize`
- `maxTotalFailedItems`
- `memoryStoresExpiration`
- `memoryStoresStorageLimit`
- `numRetries`
- `retryTimeoutBase`
- `retryExponentialBackoff`
- `processItemRateLimit`
- `progressRefreshTimeout`

**Important Notes**:

- Resuming will also **reload the callback function from the filepath**. Please ensure that the callback function and filepath are still correct before resuming a batch process.
- Resuming will **reset the output directory**. If you would like to save the previous output, then please change the outputDirectory configuration while resuming.
- When resuming a previously failed batch process, it is **required** that you set the `maxTotalFailedItems` to a value greater than the current failed item count.
- The new configurations are only applied to **new session tasks**. So, if you resume a batch process that still has active Session Tasks running, the process will continue with the old configurations until these sessions complete.

### **4.4. cleanup**

Cleans up an old batch process.

**Usage:**

```sh
# Using direct command-line options
lune run batch-process cleanup  --universe-id

# Using a configuration file
lune run batch-process cleanup  --config <filepath>

**Command-Specific Arguments:**

- **``** : The unique name of the previously run batch process to resume.

**Option:**

- **`--universe-id / -u`** : The universe to load / resume the batch process on.

### **4.5. list**

Lists all batch processes.

**Usage:**

```sh
# Using direct command-line options
lune run batch-process list --universe-id

# Using a configuration file
lune run batch-process list --config <filepath>

**Option:**

- **`--universe-id / -u`** : The universe to load / resume the batch process on.

## **5\. Configuration**

You can configure a batch process using a combination of a configuration file, command-line options, and interactive prompts. The tool uses a clear order of precedence to determine the final configurations for a run.

### **Order of Precedence**

Configurations are applied in the following order, with later methods overriding earlier ones:

1. **Default Values:** Built-in defaults for optional configurations.
2. **Configuration File:** Configurations loaded from a JSON file specified with the `-c` / `--config` flag.
3. **Command-Line Options:** Flags passed directly in the command (e.g., `--num-retries 5`). These will always override any values set in a config file.
4. **Interactive Prompts:** For any _required_ configurations not provided by the methods above, the CLI will prompt for input.

### **Recommended Workflow**

A powerful way to use the CLI is to combine a configuration file with command-line overrides. This allows you to maintain consistent base configurations while retaining flexibility.

1. **Create a base config.json file** with your common configurations (like universeId, placeId, and default processing parameters).
2. **Use command-line options** to override specific configurations for a particular run.

**_Example:_**
Imagine `my_config.json` sets `numRetries` to `3`. You can override it for a single run:

```sh
# This run will use numRetries = 5, overriding the value from the file.
# All other settings will be loaded from my_config.json.
lune run batch-process process-keys my_process -c my_config.json --num-retries 5

See **Appendix B: Configuration Glossary** for a full list of available options.

## **6\. Observability and Debugging**

### **6.1. Output Files**

The CLI generates an output directory for each batch process, providing tools for monitoring and debugging. To inspect session details and logs, you must call the Open Cloud endpoints with your API key included in the **x-api-key** header.

- **`failed_items.output`**
  Contains a line-separated record for each item that failed processing.
  **_Format:_** `|<error_time>||<session_path>`
  Metadata for failed items (such as error time, logs, and session path) is never guaranteed, and will always be lost upon resuming a batch process. For persistent error tracking, please ensure this information is saved to a separate location before resuming a batch process.
- **`process.output`**
  Displays the current status of the process along with its active configurations.
- **`stage1_session_paths_active.output & stage2_session_paths_active.output`**
  Lists the session paths for currently active Stage 1 (scanning) and Stage 2 (processing) tasks. Note that logs are only available after a session completes.
  Example Request: `GET https://apis.roblox.com/cloud/v2/<session_path>`
- **`stage1_session_paths_complete.output & stage2_session_paths_complete.output`**
  An archive of session paths that have completed execution.
- **`session_logs directory`**
  Contains a file for each completed sessions’ task logs. Each log file’s name is `<task ID>.output`, where the task ID is the final component of the session path.
- **`cli.output`**
  A list of warnings and other debugging messages coming directly from the CLI. You will likely not need to use this, but in cases of unexpected behavior it may contain insights into the issue.

Given a session path from `failed_items.output` or `stage[1/2]_session_paths_[active/complete].output`, you can find its logs in the `session_logs` directory.

Alternatively, you can manually query the session task status or logs by requesting the following Open Cloud endpoints. Include your API key in the `x-api-key` header before making the requests:

- **Query Task Status:**
  `GET https://apis.roblox.com/cloud/v2/<session_path>`
- **Query Logs:**
  `GET https://apis.roblox.com/cloud/v2/<session_path>/logs`

### **6.2. Troubleshooting**

If a batch process appears to be stalled or not working as expected, follow these diagnostic steps. Some circumstances can cause it to fail or to _appear_ unresponsive while it is still running in the background.

1. **Check Memory Stores Dashboard:** The first step is to verify that the process is actually running. The CLI's orchestration logic heavily uses memory stores.
   - Navigate to your **Memory Stores Dashboard** on the Creator Hub.
   - Check for read and write activity. A running process will generate continuous activity. This is the primary indicator that the tool is operational.
   - Ensure that your experience has **available memory stores quota**. If your experience is out of available storage or Request Units, the batch process may stall.
2. **Inspect Session Logs for Completed Tasks:** If memory stores are active but you see no progress, a session may have completed or errored out.
   - Wait for a session path to appear in one of the ...\_complete.output files in your output directory.
   - Once a path appears, check for its logs in the session_logs directory, or query the Open Cloud endpoint directly.
   - Reading these logs can help diagnose issues within the Luau callback script or reveal problems like exceeding memory store storage limits, which might prevent the process from updating its state.
   - Note that there may be warning messages implying that the process has hit its configured `memoryStoresStorageLimit`. These are not indicative of your experience being fully out of memory stores
     quota -- to be certain, we recommend checking your memory stores dashboard.
3. **Review the cli.output File:** In rare cases, the CLI itself may encounter an unrecoverable error.
   - Check the cli.output file in your output directory.
   - This file contains critical errors related to the CLI's internal operations. An error in this file indicates an issue with the tool itself, rather than with the Luau scripts it is orchestrating. The CLI has built-in retry logic for all essential operations; if it exhausts its retries, it will terminate and log the final error here.

## **7\. Performance Tuning and Resource Management**

This section provides guidance on how to configure the CLI to balance processing speed with resource consumption.

### **7.1. Understanding and Managing Memory Stores Usage**

The CLI uses memory stores for job orchestration. Before starting a process, the tool will provide you with an **estimated upper bound for memory stores usage** (both for access in Request Units/min and for storage in KB) based on your configurations. This allows you to assess the potential impact on your experience, especially if it has low concurrent users (CCU) or already uses memory stores heavily.

You can further control resource consumption with the `--memory-stores-storage-limit` option. This sets a safety cap (in kilobytes) for the batch process. If the tool detects that storage usage is approaching this limit, it will automatically slow down or pause the Stage 1 scanning process to allow the Stage 2 workers to clear the job queue. This reduces memory pressure and prevents the process from failing due to storage limits.

Be especially wary of your storage quota. If the storage limit is ever reached, the batch processor may freeze indefinitely, and you may need to flush your memory stores or cleanup your batch process.

### **7.2. Optimizing for Processing Speed vs. Resource Usage**

Tuning your batch process involves a trade-off between speed and the consumption of memory store resources. The configurations with the largest impact are `numProcessingInstances`, `maxItemsPerJob`, and `jobQueueMaxSize`.

#### **To Maximize Processing Speed:**

- **Do:** Increase `numProcessingInstances`. This is the primary lever for speed, as it increases the number of concurrent workers processing items.
- **Do:** Increase `maxItemsPerJob`. This allows the Stage 1 scanner to enqueue jobs more quickly by fetching more items per List call, which helps keep your processing instances fed with work.
- **Be Aware:** This strategy will significantly increase both memory store access (RU/min) and storage usage. It is best suited for experiences with high CCU and a large memory store quota.

#### **To Minimize Memory Stores Storage / Access Impact:**

This is recommended for experiences with low CCU or those that already use memory stores heavily for live game features.

- **Do:** Use a lower `numProcessingInstances` (e.g., 1-3). This is the most effective way to reduce memory store access usage.
- **Do:** Keep `jobQueueMaxSize` at a low value (e.g., the default of 20). A large queue is unnecessary if the processing rate is slower and only serves to consume storage.
- **Do:** Keep `maxItemsPerJob` at a moderate value. A smaller job size reduces the amount of data stored in the queue at any given time.
- **Be Aware:** This will result in a slower overall batch process but ensures the tool has a minimal footprint on your experience's resources.

#### **What Not to Do:**

- **Don't** set `jobQueueMaxSize` to a very high number unless you have a specific reason. The queue is unlikely to fill up in most scenarios, and a large value primarily consumes unnecessary storage.
- **Don't** set `maxItemsPerJob` so high that a single job cannot be completed within the 5-minute `LuauExecutionSessionTask` lifetime. A safe upper bound is `maxItemsPerJob < processItemRateLimit * 4`.

The default optional configurations are tuned to suffice for most batch processing scenarios. However, it is ultimately up to you to tune the configurations to your particular use case.

## **8\. Example Use Case: Data Deletion**

Deleting all data in a single, obsolete data store can be done using the Delete Data Store API directly from the Data Stores Manager on Creator Hub. However, this may take up to 30 days to reduce your storage footprint. To delete data faster, you may use the batch processor. Please take caution that you are only deleting data from a data store that you are **no longer using**.

1. **Create a Deletion Callback:** Use the following callback with optional logging:

local DataStoreService = game:GetService("DataStoreService")
local dataStoreToDelete = DataStoreService:GetDataStore("", "")

return function(item)
    ...
    ...
    dataStoreToDelete:RemoveAsync(item)
end

2. **Create the Configuration File** (or provide arguments on the command line)
3. **Run the `process-keys` command to kick off the deletion:** e.g.
   `lune run batch-process process-keys DS_Deletion -c <config filepath>.json`

On a related note, bulk Data Store deletion can be achieved by integrating the Deletion API with the batch processor. Please take caution that you are only deleting data stores that you are no longer
using.

1. **Create a Deletion Callback:** Use the following callback with optional logging:

local DataStoreService = game:GetService("DataStoreService")
local dataStoreToDelete = DataStoreService:GetDataStore("", "")

local DataStoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")

return function(item)
    ...
    ...
    local encodedDataStoreName = HttpService:UrlEncode(dataStoreName)
    local apiKey = HttpService:GetSecret()
    local response = HttpService:RequestAsync({
        Url = "https://apis.roblox.com/cloud/v2/universes/" .. UNIVERSE_ID
.. "/data-stores/" .. encodedDataStoreName,
        Method = "DELETE",
        Headers = {
            ["x-api-key"] = apiKey,
            ["Content-Type"] = "application/json
        },
    })
    ...
    ...
end

2. **Create the Configuration File** (or provide arguments on the command line)
3. **Run the `process-data-stores` command to kick off the deletion:** e.g.
   `lune run batch-process process-data-stores DS_Deletion -c <config filepath>.json`

## **9\. Example Use Case: Data Migrations**

### **9.1. General Migration Strategy**

When performing a large-scale data migration, it is critical to handle cases where users might join your experience mid-migration. This requires a robust strategy to ensure data integrity.

1. **Implement a Live-Path Migration:** Before starting the batch process, implement and deploy live-server code that handles migration for any user who joins the experience. This "live path" ensures that active users are migrated on-the-fly. This logic must use a **migration marker** to indicate that data has been migrated.
2. **Use a Migration Marker:** A migration marker is a piece of data or any signifier that indicates a specific key has already been migrated. Your callback function for the batch process **must** check for this marker before attempting to migrate data. This prevents the batch process from overwriting data that was already migrated by the live-path logic, making the process idempotent (safe to run multiple times).
3. **Recommended Marker Patterns:**
   - **Writing to a New Data Store (Recommended):** The safest approach is to read data from the old data store and write the migrated data to a _new_ data store. The migration marker is simply the existence of the key in the new data store. Your callback logic would be: if the key exists in the new store, do nothing; otherwise, perform the migration.
   - **Adding a Marker Property (In-Place Migration):** If you are overwriting data in the _same_ data store, you must add a "marker" property to the data object itself, or to the data store metadata (e.g., `isMigrated = true` or `schemaVersion = 2`). Your callback logic must first check if this property exists and has the correct value before proceeding.

By combining a live-path migration with a marker check in your batch callback, you create an idempotent process that is safe to run on live experiences.

### **9.2. Specific Example: DataStore2 Migration \+ Deletion**

The tool can be used to perform data migration and cleanup for the **DataStore2 module**, which uses the **"Berezaa Method"** of storing data. An example configuration and callback files are provided
to meet this exact use case in the `examples` folder in the tool.

**Provided Files in `examples/`**

- **`ds2-config.json`**: A set of configurations specifically tuned for a DataStore2 migration and deletion batch process.
- **`ds2-migrate.luau`**: A callback script that migrates the latest version from the DataStore2 data store to the migrated data store
- **`ds2-migrate-delete.luau`**: A callback script that **1\)** migrates the latest version from the DataStore2 data store to the migrated data store, and **2\)** deletes **all but the latest version** from the DataStore2 data store. We recommend using this script, since it will enable rollbacks in emergencies.
- **`ds2-migrate-delete-all.luau`**: A callback script that **1\)** migrates the latest version from the DataStore2 data store to the migrated data store, and **2\)** deletes **all data** from the
  DataStore2 data store, including the Data Store resource. Use this script with caution.

**Prerequisites:**

1. Integrate the **DataStore2 Migration Tool** into your experience.
2. Ensure the "Saving Method" is `MigrateOrderedBackups` (and set it if not).
3. Follow the testing instructions provided in the [**Data Stores Migration Packages Dev Forum Post**](https://devforum.roblox.com/t/datastores-migration-packages-for-the-berezaa-method-and-datastore2-module-public-beta/3631514).
4. Publish the experience with these changes.

**Execution Steps:**

1. Complete the setup in the **Getting Started** section.
2. If you are using the `ds2-migrate-delete-all.luau` callback, see **Appendix E: Configuring Data Store Deletion Callback** for more specific setup instructions.
3. Update the provided `ds2-config.json` file with your preferred callback (`ds2-migrate[-delete[-all]].luau`), `universeId`, `placeId`, and the DataStore2 prefix. The DataStore2 prefix is the name of the key / master key of your data store, followed by a ‘/’. For example, if you have a master key called `DATA`, then the prefix will be `DATA/`. We recommend verifying you have the correct prefix by looking for data stores with the pattern `<user id>` in Data Stores Manager.
4. (Optional) If you are using a custom migration scope in the [**DataStore2 Migration Tool**](https://create.roblox.com/store/asset/82521207271039/BETA-DataStore2-Migration-Tool?keyword=datastore2&pageNumber=1&pagePosition=0), edit the chosen callback script and add the scope as the value of `MIGRATED_DS_SCOPE`.
5. Run the `process-data-stores` command, e.g.
   `lune run batch-process process-data-stores DS2_Migration -c examples/ds2-config.json`

### **9.3. Specific Example: Generic Berezaa Method Migration**

If you have implemented the “Berezaa Method” with a custom implementation, it is still viable to use this tool for data migration. Utilize the functions available in the [**Berezaa Method Data Migration Tool**](https://create.roblox.com/store/asset/100741523916104/BETA-Berezaa-Method-Data-Migration-Tool) and adjust them to your use case.

## **Appendix**

### **Appendix A: API Key Permissions**

| API System                  | Experience Operations                                                                                |
| :-------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- |
| **luau-execution-sessions** | write                                                                                |
| **memory-stores**           | memory-store.sorted-map readmemory-store.sorted-map write                                                                                |
| **ordered-data-stores**     | universe.ordered-data-store.scope.entry:read                                                                                |
| **universe-datastores**     | universe-datastores.objects:listuniverse-datastores.objects:readuniverse-datastores.objects:createuniverse-datastores.control:create |

### **Appendix B: Configuration Glossary**

_Table of all command-line options, grouped by category._

#### **Tool Behavior Options**

| Config Name | Option           | Description                                                                                |
| :---------- | :--------------- | :---------------------------------------------------------------------------------------------------------------- |
| config      | `-c`, `--config` | Filepath to a JSON configuration file. Configurations in this file are overridden by direct command-line options. |

#### **Shared (Required) Configurations**

_These configurations are required for all commands except resume (`universeId` only). For resume, `numProcessingInstances` and `outputDirectory` can both be changed, but are no longer considered required._

| Config Name            | Option                             | Description                                                                                |
| :--------------------- | :--------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| callback               | `-C`, `--callback`                 | Filepath to the Luau callback function to execute for each item.                                                                                |
| numProcessingInstances | `-i`, `--num-processing-instances` | Number of Stage 2 processing sessions to run concurrently. Please note that the batch process will only spin up as many concurrent sessions as are available. So, if no sessions are running, the effective range of this configuration is 1 \- 9\. |
| outputDirectory        | `-o`, `--output-directory`         | Directory where output files will be created.                                                                                |
| placeId                | `-p`, `--place-id`                 | The Place ID of the place to execute the batch process on.                                                                                |
| universeId             | `-u`, `--universe-id`              | The Universe ID of the experience to execute the batch process on.                                                                                |

#### **Shared (Optional) Configurations**

_These configurations can be used with both process-keys and process-data-stores. They are optional to provide._

_The default values are suitable for most simple migration or deletion tasks. For more complex or performance-intensive operations, you should tune these values. Either way, it is recommended to understand what each configuration does before running a batch process._

| Config Name              | Option                          | Default | Description                                                                                |
| :----------------------- | :------------------------------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------- |
| errorLogMaxLength        | `--error-log-max-length`        | 50      | Maximum character length of error logs to persist from failed items.                                                                 |
| jobQueueMaxSize          | `--job-queue-max-size`          | 20      | Maximum number of jobs to hold in the queue at one time.                                                                             |
| maxItemsPerJob           | `--max-items-per-job`           | 50      | Maximum number of items to process in one job.                                                                                |
| maxTotalFailedItems      | `--max-total-failed-items`      | 100     | Number of failed items that will cause the entire batch process to fail.                                                             |
| memoryStoresExpiration   | `--memory-stores-expiration`    | 3888000 | Expiration time in seconds for all memory store entries related to the process.                                                      |
| memoryStoresStorageLimit | `--memory-stores-storage-limit` | \-1     | Storage limit for memory stores (in kilobytes) (-1 for no limit).                                                                    |
| numRetries               | `--num-retries`                 | 4       | Number of times to retry processing a single item before marking it as failed.                                                       |
| processItemRateLimit     | `--process-item-rate-limit`     | 50      | Maximum number of items a single processing instance can process per minute.                                                         |
| progressRefreshTimeout   | `--progress-refresh-timeout`    | 5       | Interval in seconds for the Stage 1 session to timeout between updating the overall process state and listing jobs to the job queue. |
| retryExponentialBackoff  | `--retry-exponential-backoff`   | 2       | The multiplier for exponential backoff on retry timeouts. 1 for no backoff.                                                          |
| retryTimeoutBase         | `--retry-timeout-base`          | 0.5     | The base timeout in seconds for retries. `Total wait = base * (backoff ^ retry_attempt)`.                                            |

#### **`process-keys` Specific Configurations**

| Config Name        | Option                                                      | Description                                                                                |
| :----------------- | :---------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| allScopes          | `--all-scopes` (flag)                                       | Flag to scan all data stores scopes. If this flag is provided, the `dataStoreScope` configuration is discarded. |
| dataStoreName      | `-d`, `--data-store-name`                                   | Name of the data store to scan.                                                                                |
| dataStoreScope     | `-s`, `--data-store-scope`                                  | Scope of the keys to scan. If not provided, defaults to global scope.                                           |
| keyPrefix          | `-P`, `--key-prefix`                                        | Prefix of keys to include in the scan. If not provided, all keys are scanned.                                   |
| excludeDeletedKeys | `--exclude-deleted-keys` / `--include-deleted-keys` (flags) | Flag to include or exclude deleted keys from the scan. Defaults to false (deleted keys are included).           |

#### **`process-data-stores` Specific Configurations**

| Config Name     | Option                      | Description                                   |
| :-------------- | :-------------------------- | :-------------------------------------------- |
| dataStorePrefix | `-P`, `--data-store-prefix` | Prefix of data stores to include in the scan. |

### **Appendix C: Custom Callback Function**

To execute a batch process, you must provide a filepath to a Luau file containing your custom callback function. This file must meet the following requirements:

1. It must contain valid, compilable Luau code.
2. It must return a single function that accepts one parameter: the item name (which will be a key name or a data store name, depending on the command).

We recommend the following format for your callback function:

```luau
-- Define Services + Other Global Variables
local DataStoreService = game:GetService("DataStoreService")
local MY_CONSTANT = 100
...

-- Define helper functions
local function migrateHelper(item)
    ...
end
...

----------- EITHER -----------

return function(item)
    ...
    migrateHelper(item)
    ...
end

------------- OR -------------

local function callback(item)
    ...
    migrateHelper(item)
    ...
end

return callback

The callback will be validated for syntax errors on the command line prior to starting your batch process. It is ultimately up to you to ensure the callback operates as intended.

This callback runs inside a `LuauExecutionSessionTask` within your experience's context, allowing you to use `require()` on ModuleScript instances from your place’s DataModel.

**Example 1: Basic Migration**

```luau
-- example-migrate-callback.luau
local DataStoreService = game:GetService("DataStoreService")
local sourceDataStore = DataStoreService:GetDataStore("source")
local destinationDataStore = DataStoreService:GetDataStore("destination")

local function migrateValue(value)
    local migratedValue = value
    ...
    ...
    return migratedValue
end

return function(item)
    local value = sourceDataStore:GetAsync(item)
    local migratedValue = migrateValue(value)
    destinationDataStore:SetAsync(item, migratedValue)
end

**Example 2: Using a ModuleScript from your Place**

```luau
-- example-migrate-callback-with-module-script.luau
local MigrationScript = require(game.ServerScriptService.MigrationScript)

return function(item)
    MigrationScript.MigrateItem(item)
end

**Example 3: Basic Deletion**

```luau
-- example-delete-callback.luau
local DataStoreService = game:GetService("DataStoreService")
local dataStoreToDelete = DataStoreService:GetDataStore("", "")

return function(item)
    dataStoreToDelete:RemoveAsync(item)
end

**Example 4: Invalid Callback Function (Bad Luau Code)**

```luau
-- example-bad-luau-code.luau
local DataStoreService = game:GetService("DataStoreService")
local sourceDataStore = DataStoreService:GetDataStore("source")
local destinationDataStore = DataStoreService:GetDataStore("destination")

local function migrateValue(value)
    local migratedValue = value
    ...
    ...
    return migratedValue
end

return function(item)
    local value = sourceDataStore:GetAsync(item)
    local migratedValue = migrateValue(value)
    destinationDataStore:SetAsync(item, migratedValue)

-- syntax error - function is missing 'end' to close

**Example 5: Invalid Callback Function (No Function Returned)**

```luau
--example-no-function-returned.luau
local DataStoreService = game:GetService("DataStoreService")
local sourceDataStore = DataStoreService:GetDataStore("source")
local destinationDataStore = DataStoreService:GetDataStore("destination")

local function migrateValue(value)
    local migratedValue = value
    ...
    ...
    return migratedValue
end

local function callback(item)
    local value = sourceDataStore:GetAsync(item)
    local migratedValue = migrateValue(value)
    destinationDataStore:SetAsync(item, migratedValue)
end

-- 'callback' is never returned - 'return callback' would make this valid

The callback function will have the following predictable logging behavior:

- Any `print()` statements in your callback will appear in the session logs.
- When an error is thrown within the callback, it will trigger the retry logic. If all retries are exhausted:
  - The truncated error will attempt to be logged to `failed_items.output` file for quick reference.
  - The complete error will be logged to the full session logs.

Remember that your callback will be called on each item individually, and runs independently of other items in the same batch or different batches.

### **Appendix D: Reserved Memory Stores / Data Stores Names**

The batch processor will utilize the following memory stores / data stores resources:

| Resource Type            | Resource Names                                                                                |
| :----------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **MemoryStoreSortedMap** | `_RBX_batch-processes``_RBX_batch-process-storage-pool``_RBX_batch-process-task-status``_RBX_batch-process-guid-to-session-paths``_RBX_batch-process-api-key-validation``_RBX_batch-process-pages-``_RBX_batch-process-failed-item-logs-` |
| **MemoryStoreQueue**     | `_RBX_batch-process-job-queue-`                                                                                |
| **OrderedDataStore**     | `_RBX_batch-process-failed-items-`                                                                                |
| **DataStore**            | `_RBX_batch-processes``_RBX_batch-process-api-key-validation`                                                                                |

**Please do not write to these resources from outside of the batch processor**. Doing so may break the batch processor.

### **Appendix E: Configuring Data Store Deletion Callback**

To use the `ds2-migrate-delete-all.luau` callback, we must configure the Universe ID within the callback, and store our API key in a secret.

1. In the `ds2-migrate-delete-all.luau` file, manually add your Universe ID as the value of `UNIVERSE_ID` on **Line 11**.
2. Create a new Open Cloud API Key, and add the permission `universe-datastores.control:delete` to it for your experience.
3. Add a secret called `DS_DELETION_API_KEY` to [Secrets Store](https://create.roblox.com/docs/cloud-services/secrets), with this API Key as your value.

### **Appendix F: Tips and Tricks**

- **Use a High-Uptime Environment**: Because the CLI must remain running for the entire duration of a batch process, it is highly recommended to run the tool on a machine with a stable internet connection and high uptime, such as a Virtual Private Server (VPS), rather than a personal laptop that may go to sleep or lose connectivity.
- **Securely Manage API Keys**: Your Open Cloud API Key is a powerful secret that provides direct access to your experience's data. It is critical to protect it.
  - We strongly advise using a dedicated secret management solution, such as the [1Password CLI](https://developer.1password.com/docs/cli/get-started/), to inject your API key into the environment.
    This prevents the key from being saved in plain text.
  - If you do set the key manually (e.g., `export API_KEY=...`), be aware that this command may be saved to your shell's history file (like `.bash_history` or `.zsh_history`). To prevent accidental leaks, we recommend clearing your shell history after your session is complete.
  - When debugging session logs by calling Open Cloud endpoints, you must include your API key in the request header. To avoid pasting your key directly into browser-based API tools, consider using a browser extension (like ModHeader) to securely inject the `x-api-key` header into your requests.

### **Appendix G: Resources**

- [DataStore2 Module](https://kampfkarren.github.io/Roblox/)

- [Data Stores Documentation](https://create.roblox.com/docs/cloud-services/data-stores)

  - [Data Stores Limits](https://create.roblox.com/docs/cloud-services/data-stores/error-codes-and-limits#limits)
  - [Data Stores Manager](https://create.roblox.com/docs/cloud-services/data-stores/data-stores-manager)
  - [Data Stores Observability Dashboard](https://create.roblox.com/docs/cloud-services/data-stores/observability)

- [Data Stores Migration Packages \- Dev Forum](https://devforum.roblox.com/t/datastores-migration-packages-for-the-berezaa-method-and-datastore2-module-public-beta/3631514)

  - [\[BETA\] DataStore2 Migration Tool](https://create.roblox.com/store/asset/82521207271039/BETA-DataStore2-Migration-Tool?keyword=datastore2&pageNumber=1&pagePosition=0)
  - [\[BETA\] Berezaa Method Data Migration Tool](https://create.roblox.com/store/asset/100741523916104/BETA-Berezaa-Method-Data-Migration-Tool)

- [Lune Documentation](https://lune-org.github.io/docs)

  - [Installation](https://lune-org.github.io/docs/getting-started/1-installation)

- [Memory Stores Documentation](https://create.roblox.com/docs/cloud-services/memory-stores)

  - [Memory Stores Limits](https://create.roblox.com/docs/cloud-services/memory-stores#limits-and-quotas)
  - [Memory Stores Observability Dashboard](https://create.roblox.com/docs/cloud-services/memory-stores/observability)

- [Open Cloud Engine API for Luau Execution \- Dev Forum](https://devforum.roblox.com/t/beta-open-cloud-engine-api-for-executing-luau/3172185)

- [Roblox Creator Hub \- API Keys](https://create.roblox.com/dashboard/credentials?id=6680476435&activeTab=ApiKeysTab)

- [Roblox Open Cloud API Documentation](https://create.roblox.com/docs/cloud)

  - [LuauExecutionSessionTasks](https://create.roblox.com/docs/cloud/reference/LuauExecutionSessionTask)

- [Secrets Store](https://create.roblox.com/docs/cloud-services/secrets)


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/index.md "Edit this page")

# Home

DataStore2 is a simple to use data store system that caches, leading to significantly faster performance over traditional data stores.

This documentation will show you how to use DataStore2 and configure it to your liking.

If you find anything missing, file [an issue on GitHub](https://github.com/Kampfkarren/Roblox/issues).

## Games that use DataStore2

  * [Dungeon Quest](https://www.roblox.com/games/2414851778/Wave-Defence-Dungeon-Quest) \- 600M+ visits
  * [Baldi's Basics](https://www.roblox.com/games/1823470443/Baldis-Basics-Beta) \- 140M+ visits
  * [Fairy World](https://www.roblox.com/games/1817078882/Fairy-World-FULL-BLOOM) \- 16.6M+ visits
  * Do you want your game on here? [File an issue on GitHub for it](https://github.com/Kampfkarren/Roblox/issues)!


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/advanced/backups.md "Edit this page")

# Backups

Info

This page is not to be confused with the [ordered backups](https://kampfkarren.github.io/Roblox/advanced/saving_methods/) method of saving. This page is relevant no matter what saving method you choose.

DataStore2 in the end always uses Roblox's data stores and servers. Not infrequently, these servers will go down through no fault of you. You need to be prepared for when this happens. Luckily, DataStore2 makes it easy.

Let's say we have the following code:

    local coinsStore = DataStore2("coins", player)
    print(coinsStore:Get(0))

By default, if data stores are down, DataStore2 will keep retrying until it gets a result. However, we may not want it to do that! If you write:

    local coinsStore = DataStore2("coins", player)
    coinsStore:SetBackup(5)
    print(coinsStore:Get(0))

...then DataStore2 will try to get the data five times before giving up.

When DataStore2 "gives up", it'll pretend the player doesn't have any data at all. Thus, no matter how many coins the player has, since there's no way to recover that data it'll simply print they have 0 coins.

However, don't worry! DataStore2 will mark this data store as a "backup data store". That means that **it will never save**. You wouldn't want it to save, it would override the player's actual data!

If it's necessary, you can check if a data store is a backup data store with the `:IsBackup()` method.

    local coinsStore = DataStore2("coins", player)
    coinsStore:SetBackup(5)
    print(coinsStore:IsBackup()) -- will print "true" if DataStore2 couldn't successfully get the data

You can also clear a backup data store so that the next time you :Get(), DataStore2 will try to recover the data again. You do not need to reset the retry amount.

    coinsStore:ClearBackup()


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/guide/installation.md "Edit this page")

# Installation

There are a few recommended ways to get DataStore2 into your game.

It is recommended that however way you get it, you put it in `ServerScriptService`, as you can only use DataStore2 on the server anyway.

### Method 1: Model File (Roblox Studio)

  * Download the `rbxmx` model file attached to the latest release from the [GitHub releases page](https://github.com/Kampfkarren/Roblox/releases).
  * Insert the model into Studio into `ServerScriptService`.

### Method 2: Filesystem

  * Copy the `DataStore2` directory into your codebase
  * Use [Rojo](https://rojo.space/) to sync the files into your place


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/guide/gotchas.md "Edit this page")

# Gotchas

  * You need to set your data stores _as they change_ , not on PlayerRemoving.

The normal way of doing data stores is to keep a cache somewhere of a player's data (such as in a folder or using leaderstats), then saving when the player leaves. **It is wrong, however, to use DataStore2 this way**. DataStore2 is built to be used whenever your data actually changes. You shouldn't invoke DataStore2 in PlayerRemoving _at all_. Don't worry, `:Set` will never error or do API calls, it's all cached. DataStore2 will save the player's data before they leave.

  * Because of the throttles on OrderedDataStores, DataStore2 (with the default saving method) is only guaranteed to not throttle on :Get() if you use less than 2 unique keys.

In normal data stores, you'd save all your data into one giant player data table to minimize data loss/throttling. In DataStore2, this is a built in feature! Simply use `DataStore2.Combine("DATA", "any", "keys", "here")`, or call it multiple times (`DataStore2.Combine("DATA", "coins"); DataStore2.Combine("DATA", "guns")`).

These are called "combined data stores", they save all your data into one big table but without the cost of ergonomics (you don't have to get the entire data just to manipulate one part of it).

The "DATA" is what the data will combine under internally. If you are migrating an existing game (and have made sure to set your [saving method](https://kampfkarren.github.io/Roblox/advanced/saving_methods/) to Standard), this will be whatever your large data table is already called.

In the future, combined data stores will become the default, but for now you must explicitly define every key you use, or be at risk of heavy throttling.


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/advanced/serde.md "Edit this page")

# Serialization

Bug

Deserialization is known to cause issues with combined data stores. If you can reproduce these issues, please [file an issue on GitHub](https://github.com/Kampfkarren/Roblox/issues)!

Data stores have a limit on how much they can save. While usually it doesn't matter, sometimes you're expecting to be saving a LOT of data, and you're going to want to save as little as possible. However, it's rare that the most optimal way to store data will be the most readable. Luckily, DataStore2 can help make this process invisible, so you save the most optimal form of your data while using the most readable.

The process of saving data in an optimal, compressed format is called "serialization". Likewise, the process of taking compressed data and turning it into a format for humans is called "deserialization".

### Methods

DataStore2 lets you easily serialize/deserialize data with `BeforeInitialGet` and `BeforeSave`. Simply put, `BeforeInitialGet` defines your deserializer, while `BeforeSave` defines your serializer. Here's how it works.

Let's say we have an inventory system where a player can only have one of any item. Let's also suppose their data is saved like this:

        ["Cool Sword"] = true,
        ["Doge Head"] = true,
        -- etc...

However, we run into an issue. The more items we add, the more data we have to save, and we might have some pretty long names in the future!

    ["The Totally Awesome Sword of Justice, Sponsored By Builderman"] = true,

Ouch. Let's use serializers and deserializers to fix this. The best way to store our data is like this:

    { 1, 6, 8 }

Huh? Numbers? Yep, but those numbers are going to correlate to items. We're going to create a dictionary of all the items in our game under the name "ItemsDictionary". It'll map **item IDs** to **names**.

    return {
        "Cool Sword",
        "Crazy Sword",
        "Doge Head",
        -- etc

Now, we can write our serializer and deserializer. We're going to save as numbers, but come back the same place we were before.

    local itemsStore = DataStore2("items", player)

    itemStore:BeforeInitialGet(function(serialized)
        -- BeforeInitialGet is called the first time :Get() is, known as the "initial get".
        -- This is the only time DataStore2 will call the DataStoreService get method.
        -- The "serialized" argument is the value that was actually in the store.
        -- That means that right now, it's something like { 1, 6, 8 }
        -- What we return is what DataStore2 is going to give `:Get()`.
        local deserialized = {}

        for _, id in pairs(serialized) do
            local itemName = ItemsDictionary[id]
            deserialized[itemName] = true
        end

        return deserialized
    end)

    itemStore:BeforeSave(function(deserialized)
        -- BeforeSave is called, well, before we save!
        -- The return is what DataStore2 will actually save, so we want it in the serialized form.
        local serialized = {}

        for itemName in pairs(deserialized) do
            for itemId, name in pairs(ItemsDictionary) do
                if name == itemName then
                    table.insert(serialized, itemId)
                end
            end
        end

        return serialized
    end)


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/api.md "Edit this page")

# API

## Global Methods

### DataStore2

    DataStore2(dataStoreName, player) -> DataStore

Will create a DataStore instance for the player with that specific name. If one already exists, will retrieve that one.

Warning

Do not use the master key that you use in combined data stores, this behavior is not defined!

### DataStore2.Combine

    DataStore2.Combine(masterKey, ...keysToCombine)

Combines all the keys under `keysToCombine` under the `masterKey`. Internally, will save all data under those keys into the `masterKey` as one large dictionary. You can learn more about combined data stores and why you should use them in the [gotchas page](https://kampfkarren.github.io/Roblox/guide/gotchas/). Can be called multiple times without overriding previously combined keys.

Warning

You should never use data stores without combining them or at the very least, replicating the behavior by creating one large dictionary yourself! Combined data stores will [soon be the default way to use DataStore2](https://github.com/Kampfkarren/Roblox/issues/38).

### DataStore2.ClearCache

    DataStore2.ClearCache()

Clears the DataStore2 cache, so using `DataStore2` again will give you fresh data stores. This is mostly for internal use or for unit testing.

### DataStore2.PatchGlobalSettings

    DataStore2.PatchGlobalSettings({
        SavingMethod?: "Standard" | "OrderedBackups",
    })

Will override the global settings by patching it with ones you provide. This means if you do not specify a setting, it will not be changed.

#### Current Settings

  * SavingMethod (Default: "OrderedBackups")
    * Controls how the data should be saved. Read more in the [saving methods page](https://kampfkarren.github.io/Roblox/advanced/saving_methods).

### DataStore2.SaveAll

    DataStore2.SaveAll(player)

Will save all the data stores of the player. This is the recommended way to save combined data stores.

## DataStore API

### DataStore:Get

    Get(defaultValue?: any, dontAttemptGet?: bool) -> any

Will return the value cached in the data store, if it exists. If it does not exist, will then attempt to get the value from Roblox data stores. This function will only yield if there is no value in the data store.

When a `defaultValue` is passed in, that value will be used if the player has no data.

When `dontAttemptGet` is `true`, will return the cached value and will not attempt to get it from Roblox if it does not exist. Ignores the value of `defaultValue`.

Note

:Get() returns a deep copy of whatever the data is, thus if the value is a table, then `dataStore:Get() ~= dataStore:Get()`. This may be lifted in the future for "pure" data stores.

### DataStore:Set

    Set(newValue: any)

Will set the cached value in the data store to `newValue`. Does not make any data store calls, and so will never yield.

### DataStore:Save

    Save()

Saves the data in the current data store to Roblox. This function yields.

Warning

Currently, Save() does not attempt to retry if it fails the first time. Save() _can_ error if data stores are down or your data is invalid.

### DataStore:Update

    Update(updateCallback: (any) => any)

Will set the data store value to the return of `updateCallback` when passed with the current value.

Bug

`Update` currently does not attempt to get the value from the Roblox data store. [This will be fixed in a future update.](https://github.com/Kampfkarren/Roblox/issues/57)

Notice

You may see people talk about how `UpdateAsync` is more reliable than `SetAsync` in normal Roblox data stores. In DataStore2, this doesn't matter since neither actually call Roblox data store methods, so use `:Set` when you don't need the old value.

### DataStore:GetTable

    GetTable(default: Dictionary<any, any>) -> Dictionary<any, any>

Will get the value (either from the cache or Roblox data stores), and patch it with the default value if it doesn't have the keys. For example, if you have the data:

        coins = 0,
        swords = {},

...and want to add a `gems` field, just appending on that to the default value for `Get` won't work the way you might want it too--it won't add the key!

    -- Oops! The player already has data, so it doesn't try to use the default value!
    dataStore:Get({
        coins = 0,
        gems = 0,
        swords = {},
    })

You can, however, use `GetTable`:

    -- Much better! It'll check if they have each key rather than just if they have data.
    dataStore:GetTable({
        coins = 0,
        gems = 0,
        swords = {},
    })

Note

This is not necessary to use tables with DataStore2. You can save/retrieve tables just like any other piece of data.

### DataStore:Increment

    Increment(add: number, defaultValue?: number)

Will increment the current value (cached or from Roblox data stores) with the value provided in `add`. If a value does not exist, will use `defaultValue`, then add.

### DataStore:OnUpdate

    OnUpdate(callback: (value: any) => void)

Will call the callback provided whenever the cached value is updated. Is _not_ called on the initial get.

### DataStore:SetBackup

    SetBackup(retries: number, alternativeDefaultValue?: any)

Will set the number of retries for `:Get()` to attempt to retrieve a Roblox data store value before giving up and marking the data store as a backup. If `alternativeDefaultValue` is provided, then that value will be given to `:Get()`, otherwise normal rules apply while assuming the player actually doesn't have any data. Learn more on the [backups page](https://kampfkarren.github.io/Roblox/advanced/backups/).

### DataStore:IsBackup

    IsBackup() -> bool

Returns whether the current data store is a backup data store or not. Learn more on the [backups page](https://kampfkarren.github.io/Roblox/advanced/backups/).

Tip

You don't need to know if a data store is a backup when saving. Backup data stores will never save.

### DataStore:ClearBackup

    ClearBackup()

Unmarks the current data store as a backup data store. The next time `Get()` is called, it'll attempt to get the value inside Roblox data stores again. Learn more on the [backups page](https://kampfkarren.github.io/Roblox/advanced/backups/).

### DataStore:BeforeInitialGet

    BeforeInitialGet(modifier: (dataValue: any) => any)

Called after a value is received from Roblox data stores. The value returned is what `:Get()` will receive. Primarily used for deserialization. Learn more on the [serialization page](https://kampfkarren.github.io/Roblox/advanced/serde/).

Bug

BeforeInitialGet is known to cause issues with combined data stores. If you can reproduce these issues, please [file an issue on GitHub](https://github.com/Kampfkarren/Roblox/issues)!

### DataStore:BeforeSave

    BeforeSave(modifier: (dataValue: any) => any)

Called before a value is saved into Roblox data stores. The value returned is what will be saved. Primarily used for serialization. Learn more on the [serialization page](https://kampfkarren.github.io/Roblox/advanced/serde/).

### DataStore:AfterSave

    AfterSave(callback: (savedValue: any) => void)

Will call the callback after data is successfully saved into Roblox data stores.

### DataStore:GetAsync

    GetAsync(defaultValue?: any, dontAttemptGet?: bool) -> Promise<any>

Same as `Get`, but will instead return a [`Promise`](https://eryn.io/roblox-lua-promise/) instead of yielding.

### DataStore:SaveAsync

    SaveAsync() -> Promise<bool, any>

Same as `Save`, but will instead return a [`Promise`](https://eryn.io/roblox-lua-promise/) instead of yielding. On rejection, will return the error that caused the reject. On resolution, will return whether the data _actually_ saved. This is false in the case of being in studio, data not being updated or the data store being a backup store.

Tip

If you only want to be notified when the data is truly saved, use `AfterSave`.

### DataStore:GetTableAsync

    GetTableAsync(default: Dictionary<any, any>) -> Promise<Dictionary<any, any>>

Same as `GetTable`, but will instead return a [`Promise`](https://eryn.io/roblox-lua-promise/) instead of yielding.

### DataStore:IncrementAsync

    IncrementAsync(add: number, defaultValue?: number) -> Promise<void>

Same as `Increment`, but will instead return a [`Promise`](https://eryn.io/roblox-lua-promise/) instead of yielding.


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/guide/basic_usage.md "Edit this page")

# Basic Usage

DataStore2 is meant to be easy to use. The basic usage can be seen from the following example code.

The code below does the following:

  * Fires a remote event called "CoinAmount" to players when they join and when their amount of coins updates.
  * Listens for a "BuyProduct" remote to let players buy products.
  * Buying products reduces their amount of coins, which will then fire the remote event.

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Players = game:GetService("Players")
    local ServerScriptService = game:GetService("ServerScriptService")

    local DataStore2 = require(ServerScriptService.DataStore2)

    -- Always "combine" any key you use! To understand why, read the "Gotchas" page.
    DataStore2.Combine("DATA", "coins")

    Players.PlayerAdded:Connect(function(player)
        local coinStore = DataStore2("coins", player)

        local function callRemote(value)
            ReplicatedStorage.CoinAmount:FireClient(player, value)
        end

        -- Fire a remote event to the player telling them how many coins they have.
        -- If this is their first time playing the game, they'll start out with 100.
        callRemote(coinStore:Get(100))

        -- Everytime the coin store updates, we'll send the RemoteEvent again.
        coinStore:OnUpdate(callRemote)
    end)

    -- This is a RemoteEvent where a player can purchase a product by its name.
    local Products = require(ReplicatedStorage.Products)

    ReplicatedStorage.BuyProduct.OnServerEvent:connect(function(player, productName)
        if not Products[productName] then return end -- Make sure the player is buying a real product

        local coinStore = DataStore2("coins", player)
        local productPrice = Products[productName].price

        if coinStore:Get(100) >= productPrice then
            print("Buying product", productName)
            coinStore:Increment(-productPrice)
        end
    end)


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/advanced/saving_methods.md "Edit this page")

# Saving Methods

DataStore2 supports two ways of storing data. You can tell DataStore2 which one to use by either editing the `Settings` module inside DataStore2, or by running the following command before creating any data stores:

    DataStore2.PatchGlobalSettings({
        SavingMethod = "SavingMethodGoesHere",
    })

## "OrderedBackups"

This is the default setting for DataStore2, and is partly responsible for its popularity. With this as the saving method, DataStore2 will use _multiple data stores_ instead of just one. This ensures no data is lost, but is extremely redundant and uses a lot of storage on Roblox's side. This method was initially conceptualized by berezaa, which is why you'll sometimes hear this coloquially called the "berezaa method".

## "Standard"

While this is the saving method, DataStore2 will instead just use normal data stores without any funny business. This is the recommended saving method if you are migrating an existing game to DataStore2.

For example, if you use the key "coins" (assuming it's uncombined), DataStore2 will get your data simply with:

    DataStoreService:GetDataStore("coins"):GetAsync(player.UserId)


[](https://github.com/Kampfkarren/Roblox/edit/master/docs/guide/simulator.md "Edit this page")

# Basic Example: Simulator

In this example, we're going to create a basic "simulator" game. If you do not have DataStore2 installed, [read the installation guide](https://kampfkarren.github.io/Roblox/guide/installation).

In our game, we're going to have a brick that when a player clicks on it, they'll get points.

After installing DataStore2, create a "Part" in the workspace, then put a "ClickDetector" inside of it. Name the part something like PointsPart. Your workspace should look like this:

Next, we're going to create a leaderstats board. Create a script in ServerScriptService with the following code.

    local Players = game:GetService("Players")
    local ServerScriptService = game:GetService("ServerScriptService")
    local Workspace = game:GetService("Workspace")

    local DataStore2 = require(ServerScriptService.DataStore2)

    -- Combine every key you use. This will eventually be the default, but for now read the "Gotchas" section to understand why we need this.
    DataStore2.Combine("DATA", "points")

    Players.PlayerAdded:Connect(function(player)
        local pointsStore = DataStore2("points", player)

        local leaderstats = Instance.new("Folder")
        leaderstats.Name = "leaderstats"

        local points = Instance.new("NumberValue")
        points.Name = "Points"
        points.Value = pointsStore:Get(0) -- The "0" means that by default, they'll have 0 points
        points.Parent = leaderstats

        pointsStore:OnUpdate(function(newPoints)
            -- This function runs every time the value inside the data store changes.
            points.Value = newPoints
        end)

        leaderstats.Parent = player
    end)

Click "Play", and look at the top right of your screen. You should see this:

Now, we have to let the player actually get points. Write the following code after the connection:

    Workspace.PointsPart.ClickDetector.MouseClick:Connect(function(player)
        local pointsStore = DataStore2("points", player)
        pointsStore:Increment(1) -- Give them 1 point
    end)

Go in game, and click the brick to your heart's content. You should be getting points! If you play your game on roblox.com, you'll be able to get points, leave, come back, and the points you had last time should save!

However, you'll notice that if you play the game in _studio_ , your points _won't_ save. This is because **DataStore2 does not save in studio by default**. Usually, you don't want to save in studio because you don't want broken data while you're developing, and you don't want to have to wait for your data to save every time. However, you can change this yourself.

Create a "BoolValue" in ServerStorage named "SaveInStudio". It should look like this:

Now, make sure the checkbox is checked.

You'll now be able to play over and over again as much as you want, and your data will save!


# Luau.tmLanguage

Repository of TextMate language files for the [Luau](https://luau-lang.org/) programming language.

This repository is forked from https://github.com/microsoft/TypeScript-TmLanguage, licensed under MIT

Only the YAML files need to be modified, the rest of the files are autogenerated

npm install
npm run build:grammar

npm test
npm run diff
npm run accept


Log Rust prints and panics to Luau for easy debugging.

## Install

cargo add autput

## Brief

When embedding Rust in Luau via Wasynth, there's no good default error handler
or printing functionality. Rust panics cause mysterious error messages and
printed messages go nowhere.

Autput aims to be a minimal useful product for sending panics and prints to Luau
from Rust. You need only initialise Autput, and all the relevant panic handling
and printing features are turned on for you automatically, so you can get to
work right away.

```Rust
fn main() {
    autput::connect();
    info!("This is an info message.");
    warn!("This is a warning message.");
    error!("This is an error message.");
    panic!("This is a panic.");

- Compatible with the `log` crate
- Send to `print()`, `warn()` (Roblox only), or your own Luau log functions
- Rust panics are redirected to Luau's `error()`

## A note about `println!`

It isn't possible to redirect where standard output goes in Rust. As a result,
Autput can't restore `println!()` functionality, or other similar hardcoded
printing procedures.

Instead, you should use the `log` crate and its macros: `info!()`, `warn!()`
etc.

## License

Licensed the same way as all of my open source projects: BSD 3-Clause + Security Disclaimer.

As with all other projects, you accept responsibility for choosing and using this project.

See [LICENSE](./LICENSE) or [the license summary](https://github.com/dphfox/licence) for details.


# ZenObservable for Luau

Upstream Repo: https://github.com/zenparsing/zen-observable


# luau-raylib

Luau bindings for [raylib](https://www.raylib.com/). Powered by [zune](https://github.com/Scythe-Technology/zune)


+-OVERVIEW----------------------------------+-LICENSE-(MIT)---------------------------------------------------+
| quantum computing simulation lib written  | MIT License                                                     |
| in luau                                   |                                                                 |
+-USAGE-------------------------------------+ Copyright (c) 2024 veires                                       |
| quantum.luau is the main file, require    |                                                                 |
| it.                                       | Permission is hereby granted, free of charge, to any person     |
+-STYLE-------------------------------------+ obtaining a copy of this software and associated documentation  |
| GENERAL:                                  | files (the "Software"), to deal in the Software without         |
| - use tabs, spaces are janky to work with | restriction, including without limitation the right to use,     |
| - when you want to make a sub-table in a  | copy, modify, merge, publish, distribute, sublicense, and/or    |
| parent table, do this:                    | sell copies of the Software, and to permit persons to whom      |
| local parent = {}                         | the Software is furnished to do so, subject to the following    |
| local child = {}                          | conditions:                                                     |
| parent.child = child                      |                                                                 |
|                                           | The above copyright notice and this permission notice shall be  |
| NAMING SCHEME:                            | included in all copies or substantial portions of the Software. |
|               + GENERALS +                |                                                                 |
| - use snake case                          | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|                                           | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
|                + PREFIX +                 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND        |
| - m: supports multiple qubits             | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT     |
| - t: supports topological based           | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,    |
|                                           | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    |
|                + SUFFIX +                 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR   |
| - _ex: extra/expanded version of function | OTHER DEALINGS IN THE SOFTWARE.                                 |
+-CREDITS-----------------------------------+-----------------------------------------------------------------+
| made by: veires     > @veires              used: - greg hewgill's picomath                         d8888b.  |
|          jiface     > @ssynical                                                                 db VP  `8D  |
|          plusgiant5 > @plusgiant5                                                               VP   oooY'  |
|                                                                                ~~~b.  |
|                                                                                db db   8D  |
| hi guys please contribute to this please i'm too lazy we're too lazy so please                  VP Y8888P'  |
+-------------------------------------------------------------------------------------------------------------+


# folly
A text editor made with Zig and Luau.

Ideology: move fast and fix things later


# semver-luau

A Luau library to parse and compare [Semantic Versioning] compatible version numbers.

Semantic Versioning (Semver) is a versioning scheme for software that uses a three-part version number format, `MAJOR.MINOR.PATCH`.
Additionally, Semver defines rules on when to increment each of these parts:

- `MAJOR` version increments indicate incompatible API changes.
- `MINOR` version increments indicate the addition of functionality in a backward-compatible manner.
- `PATCH` version increments indicate backward-compatible bug fixes.

Semver also allows for optional pre-release and build metadata tags.

For a detailed set of guidelines that Semver version numbers follow, visit the previously linked website containing the full
Semver specification.

## Usage

To improve error / nil handling ergnomics, this library makes use of [util.luau]'s [`Option`] and [`Result`] implementations.
Any fallible methods return a [`Result`] and potentially null values are represented as [`Option`]s.

Invalid versions should return a [`Result`] with the respective error. If not, this is considered a bug, and please consider
filing an issue [here](https://github.com/0x5eal/semver-luau/issues).

```luau
local Semver = require("semver")

-- Parse version strings
local v1 = Semver.parse("1.2.3"):unwrap() --[[
  major = 1,
  minor = 2,
  patch = 3,
  buildMetadata = Option::None,
  prerelease = Option::None
local v2 = Semver.parse("5.12.0+build.1731248766"):unwrap() --[[
  major = 5,
  minor = 12,
  patch = 0,
  buildMetadata = Option::Some("build.1731248766"),
  prerelease = Option::None

-- Compare versions
print(v1 < v2)  -- true
print(v1 > v2)  -- false
print(v1 == v2) -- false

## MSLV (Minimum Supported Luau Version)

This library requires at least requires at least Luau [0.629](https://github.com/luau-lang/luau/releases/tag/0.629),
which corresponds to Lune [0.8.7](https://github.com/lune-org/lune/releases/tag/v0.8.7) (for leading `|` support).

## License

This project is licensed under the [MIT] license.

[Semantic Versioning]: https://semver.org
[util.luau]: https://github.com/lukadev-0/util.luau
[`Result`]: https://lukadev-0.github.io/util.luau/docs/error-handling
[`Option`]: https://lukadev-0.github.io/util.luau/docs/optional-values
[MIT]: https://compeydev.mit-license.org


# Bright

A scriptable tool for transforming and transpiling Luau code

## Non-goals
- Drop-in replacement for Darklua - Bright is designed to be a much more complex transformer and will support more
  complex rule definitions, but in exchange this means that it won't act as a simple drop-in
- Simplicity - since Bright is scriptable, it will support a wide variety of transformations that you can apply to code,
  which means that writing a transformer will not be simple; clear documentation should be prioritised over simplicity
- Producing new ASTs - transformer scripts are for transforming existing ASTs, not making new ones


[ ](https://github.com/brightluau/bright/edit/master/docs/badges.md "Edit this page") [ ](https://github.com/brightluau/bright/raw/master/docs/badges.md "View source of this page")

# Badges¶

This section explains badges that are used in this documentation.

##  Version¶

The tag symbol with a version number indicates when this feature or behavior was added, or the minimum version of Bright required to run a specific rule.

Back to top


[ ](https://github.com/brightluau/bright/edit/master/docs/developer-guide/index.md "Edit this page") [ ](https://github.com/brightluau/bright/raw/master/docs/developer-guide/index.md "View source of this page")

# Development and You¶

This part of the documentation is currently under construction.

So you've installed Bright, tinkered with a few things, decided it wasn't enough and thought "I've got too much time to kill"? Well, you're in the right place, and I wish you good health from this point forward.

## Transformer Development¶

Transformers are the meat and potatoes of Bright, and are the part of the tool that is the most flexible. They let you manipulate your code in a variety of ways, from stripping comments to transforming statements as if they're macros, and are extremely powerful.

### Before you begin¶

Before you start writing your own transformers, consider the following:

  1. **Transformer development sucks.** This isn't because the API is bad or inherently hard to learn, but more the fact that developing transformers sucks the living soul out of you when debugging why they don't work.
  2. **It might already exist.** A lot of people publish their transformers for others to use, and existing transformers are quite configurable. Why not shop around first so that you don't reinvent the wheel?
  3. **You might not need one.** If you are writing a transformer for a specific purpose, make sure that it's not just an inherent fault in the way you've written your code. Transformers aren't for fixing mistakes or bad practices (though you definitely can use them to do that).

### Pointers¶

Now that you've read the above and _still_ decided to go through with writing one, here's some pointers to documentation pieces that you should read.

  * [**Poke and You**](https://brightluau.dev/developer-guide/poke/)

Bright is powered by Poke, so you should learn Poke's API before writing transformers. It's a real page turner.

  * [**Transformonomicon**](https://brightluau.dev/developer-guide/transformonomicon/)

The transformonomicon contains all of the information for writing transformers, from how Bright calls your code to how to make your transformers customizable.

## Bright Development¶

If you're one of the few people that actually wants to use Bright's API or even contribute to the CLI, then first off, wow, and second, you're also in the right place. Head over to the [Bright](https://brightluau.dev/developer-guide/bright/) part of the developer guide to find out more.

Back to top


[ ](https://github.com/brightluau/bright/edit/master/docs/transformers/community.md "Edit this page") [ ](https://github.com/brightluau/bright/raw/master/docs/transformers/community.md "View source of this page")

# Community Transformers¶

This part of the documentation is currently under construction.

## Some rule¶

[](https://brightluau.dev/badges/#version "Minimum version")[1.2.3](https://brightluau.dev/changelog/#1.2.3)

Back to top


[ ](https://github.com/brightluau/bright/edit/master/docs/api/std/NoConfig.md "Edit this page") [ ](https://github.com/brightluau/bright/raw/master/docs/api/std/NoConfig.md "View source of this page")

# `type NoConfig`¶

[](https://brightluau.dev/badges/#version "Minimum version")[dev](https://brightluau.dev/changelog/#dev)

    1

    type NoConfig = Config<{}>

A blank configuration.

Back to top


[ ](https://github.com/brightluau/bright/edit/master/docs/transformers/index.md "Edit this page") [ ](https://github.com/brightluau/bright/raw/master/docs/transformers/index.md "View source of this page")

# Transformers¶

This part of the documentation is currently under construction.

Rules for me, and thee!

Back to top


[ ](https://github.com/brightluau/bright/edit/master/docs/user-guide/initializing-a-project.md "Edit this page") [ ](https://github.com/brightluau/bright/raw/master/docs/user-guide/initializing-a-project.md "View source of this page")

# Initializing a Project¶

This part of the documentation is currently under construction.

To get Bright set up on a project, run `bright init`. This will set up a `bright/` directory and add a `bright.toml` file with a basic configuration. Notably, you will have no rules at this point, which is where you have to pick a path.

## Preset Rules¶

This is the path most people will take, and the path recommended for simplicity's sake. Check out the [transformers](https://brightluau.dev/transformers/) page for some common rules you can grab, as well as some community-provided ones.

## Custom Rules¶

If you want to write your own transformer rules, you should get started with the [developer guide](https://brightluau.dev/developer-guide/#transformer-development). It's going to be a long ride, friend.

Back to top