How can I use mjml parser for template building editors

Hi @ngarnier
We are using Grapesjs https://github.com/artf/grapesjs-mjml as a template builder which uses MJML parser to convert MJML code into HTML code. It uses v3.3.5 and recently I upgraded to "version4.0-beta" for the efficient parser functionalities. But it throws some error which I already discussed at #928.
The demo link http://grapesjs.com/demo-mjml.html
My aim is to develop responsive email using MJML with the help of this editor.
When we start to drag and drop some elements into the canvas, it will convert the MJML code to  HTML code and render into the editor. Its calling mjml2html parser for this process, where that has been bundled into client side  as a minified file https://github.com/artf/grapesjs-mjml/blob/master/dist/grapesjs-mjml.min.js (it is heavy in size and i'm bothering the load time as well in the browser)
So, The questions are

Why this code import { mjml2html } from 'mjml'; was working fine with lower version and not in higher version?
Using the same version3.3.5 - Is all converted HTML code will work fine in major email clients or not?
Do have any other approaches to achieve this without client-side library loading?
Is my aim to achieve this is possible with Grapejs editor?

Thanks in Advance

hello @anand-gopinath,

mjml2html is now the default exported function, so you don't need the brackets anymore (documented here)
Yes, the HTML produced with v3.3.5 was already working in all major email clients
You should rather use MJML server-side. You can either host your own api (quite easy to do, here is an example that can even be simplified), use a lambda function or the MJML API
I don't see why it wouldn't be, but I don't know much about the Grapejs editor so you should rather ask its creator who knows it better than anyone

Cheers!

@ngarnier Hi, just wanted to know more about the email builder? Could you point me in the right direction?

@Amandeepsinghghai you can try it here: https://www.mailjet.com/demo/. If you're interested in embedding it inside your app, you can provide some info about your use case and request a quote here: https://hello.mailjet.com/en-demo/ (note this is an enterprise feature).


Create new class PasswordResetsController.cs

@CodiumAi-agent /describe

@codiumAi-Agent /ask

@CodiumAI-Agent /describe

Title
Create new class PasswordResetsController.cs

PR Type
enhancement

Description

Added a new PasswordResetsController class to handle password reset operations.
Implemented a Put method to update user passwords, including validation for required fields and matching passwords.
Implemented a Post method to create password reset requests, generating a unique key and handling non-existent emails.
Enhanced error handling to provide detailed feedback on invalid input.

Changes walkthrough 📝
Relevant filesEnhancement

PasswordResetsController.csImplement Password Reset Functionality in New Controller

Controllers/PasswordResetsController.cs
Introduced a new PasswordResetsController class.  Added Put method for updating user passwords with validation.  Added Post method for creating password reset requests.  Implemented error handling for invalid input and non-existent users.

+91/-0

💡 PR-Agent usage: Comment /help "your question" on any pull request to receive relevant information

@CodiumAI-Agent /analyse

PR Code Suggestions ✨

CategorySuggestion                                                                                ScoreSecurity

Improve the security of the password reset key generation

Replace MD5 hashing with a more secure hashing algorithm for generating password reset keys.
Controllers/PasswordResetsController.cs [79-80]
-var md5 = MD5.Create();
-var hash = md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(passwordResetRequest.email));
+using var sha256 = SHA256.Create();
+var hash = sha256.ComputeHash(System.Text.Encoding.ASCII.GetBytes(passwordResetRequest.email));

Suggestion importance[1-10]: 9
Why: Replacing MD5 with SHA256 significantly enhances the security of the password reset key generation, addressing a critical security concern.
9

Enhance password security by enforcing strength requirements

Validate the new password against common security policies (minimum length, complexity) before updating.
Controllers/PasswordResetsController.cs [54]
-existingUser.updatePassword(passwordResetRequest.password);
+if (IsValidPassword(passwordResetRequest.password))
+{
+    existingUser.updatePassword(passwordResetRequest.password);
+}
+else
+{
+    ModelState.AddModelError("password", "Password does not meet security requirements");
+    return BadRequest(ModelState);
+}

Suggestion importance[1-10]: 7
Why: Validating password strength before updating enhances security by ensuring passwords meet defined security policies, although the exact implementation of IsValidPassword needs to be defined.
7
Possible issue

Make the password update process robust by ensuring atomicity and exception handling

Ensure that the password update operation is atomic and handle potential exceptions during the database update.
Controllers/PasswordResetsController.cs [54-56]
-existingUser.updatePassword(passwordResetRequest.password);
-_context.Users.Update(existingUser);
-_context.SaveChanges();
+using (var transaction = _context.Database.BeginTransaction())
+{
+    try
+    {
+        existingUser.updatePassword(passwordResetRequest.password);
+        _context.Users.Update(existingUser);
+        _context.SaveChanges();
+        transaction.Commit();
+    }
+    catch (Exception ex)
+    {
+        transaction.Rollback();
+        // Handle exception (e.g., log the error and return an appropriate message)
+    }
+}

Suggestion importance[1-10]: 8
Why: The suggestion to use a transaction ensures that the password update process is atomic and robust, which is crucial for maintaining data integrity and handling exceptions effectively.
8

Prevent runtime errors by ensuring user object is not null before updating

Check for potential null reference on existingUser before calling updatePassword to prevent runtime exceptions.
Controllers/PasswordResetsController.cs [54]
-existingUser.updatePassword(passwordResetRequest.password);
+if (existingUser != null)
+{
+    existingUser.updatePassword(passwordResetRequest.password);
+}
+else
+{
+    return NotFound("User not found.");
+}

Suggestion importance[1-10]: 7
Why: This suggestion correctly identifies a potential null reference issue, which is crucial for preventing runtime exceptions and ensuring the application's stability.
7

@CodiumAI-Agent /analyse


Context directory

This design is a subset of #7199 and #7432 . It intentionally keeps a narrow focus, to allow for rapid implementation, and easing user pain as soon as possible.
Problem
Dagger modules are often contextual: they are used in the context of another software projet. Almost always, these modules need to access their context directory - typically the project's source code.

Today a module cannot directly access its context directory. Instead it must be passed by the caller as an argument. This is inconvenient.
To make things worse, modules cannot filter the contents of the context directory. This is essential to avoid the prohibitive cost of uploading a huge monorepo on every task.

Solution
Give modules the ability to access their context directory at runtime.
Proposed design
Status

Design consensus: ✅
Implementation: <volunteer needed!>

Spec
To access the context directory, simply set a default value to your directory arguments. When applying the default value, the engine will interpret it as a path in the module's context directory, and pass the corresponding directory.

The context directory is the git repository containing the module. If there is not git repository, the context is simply the module.
Relative paths are relative to the current source file, scoped to the context directory for security
Absolute paths are rooted in the context directory
There is no limit to the number of directories which can have default values of this sort
Optionally, directories can be filtered with a gitignore-style annotation

In this example, we use this technique for several files and directories, with a mix of relative and absolute paths.
// Build the app
func (app *App) Build(
  // Frontend source code
  // +default="./frontend/src"
  frontend *Directory,
  // Backend source code
  // +default="./backend/app/src"
  // +ignore=["*", "!**/*.go", "!go.mod", "!go.sum"]
  backend *Directory,
  // Build tool configuration, shared across the monorepo
  // +default="/tools/builder.conf"
  buildConfig *File,
) *Directory {
  // Execute the build as usual
}

Caching considerations
We favored a design that used function arguments, rather than core function calls (dag.Foo()) because it makes it easier to protect caching. In this design, the engine always knows exactly which subset of the context directory, if any, a function needs. So it can invalidate cache if, and only if, that subset changes. This allows for massive performance optimizations in large monorepos especially.
Relationship to other proposals
This design is a subset of #7199 and #7432 . It intentionally keeps a narrow focus, to allow for rapid implementation, and easing user pain as soon as possible.

FYI @vito @sipsma @sagikazarmark @TomChv @kpenfound @rawkode
As previously discussed. This is the more narrowly scoped "phase 1" of contextual modules, that hopefully we can fast-track.

I like it. Also, good job on narrowing the scope. I suspect this will stay useful even if the concept of contextual modules change in the future.
How is this going to behave in reusable modules? What's the context directory in those cases (when the module is actually called from another module)?
I know I already asked this, but just to clarify: if I call a remote module, the context directory is going to be that module's repository, right?

I have questions about the implementation.

To access the context directory, simply set a default value to your directory arguments. When applying the default value, the engine will interpret it as a path in the module's context directory, and pass the corresponding directory.

The SDK still needs an API to communicate this to the engine. Would it be the new dag.Context().Directory(...)? For SDKs that support default values, that API would be used directly.
However, I suppose the engine needs this information during load so it would have to be reported in the argument definition itself in the TypeDef system somehow, is that right @sipsma?

The context directory is the git repository containing the module. If there is not git repository, the context is simply the module.

This matches today’s context directory, but If this is a stepping stone into https://github.com/dagger/dagger/issues/7199 where the context directory is the parent directory it will be a breaking change later. Does it make sense for this path to match that proposal instead?

Relative paths are relative to the current source file, scoped to the context directory for security

The engine doesn’t have any knowledge of source files, so the SDK needs to resolve the path in relation to what? "source" directory is what makes the most sense because that’s what the SDK’s language code knows about.
I think it would be easier to be relative from the source dir from the get go (user facing), instead of the source file. For example, assuming backend is a sibling directory of <module source>:
# <module source>/src/main/main.py
def build(
    self,
    frontend: dagger.Directory = dag.context().directory("../backend/app/src"),
) -> dagger.Directory:
    ...

Otherwise, this would be pretty common in Python because the function code isn’t in the root of the module’s sources like in Go:
# <module source>/src/main/main.py
def build(
    self,
    frontend: dagger.Directory = dag.context().directory("../../../../backend/app/src"),
) -> dagger.Directory:
    ...

Note that using dag.context().directory() doesn't make sense that the relative path is not the context directory, so this would have to be a different API method. Also, the solution may require that this be done through the TypeDef registration phase so it would look even more different (but less consistent if not using dag here).
Just making a point on why relative to source code file isn't optimal.

Absolute paths are rooted in the context directory

👍

To access the context directory, simply set a default value to your directory arguments. When applying the default value, the engine will interpret it as a path in the module's context directory, and pass the corresponding directory.

The SDK still needs an API to communicate this to the engine. Would it be the new dag.Context().Directory(...)? For SDKs that support default values, that API would be used directly.

I don't know. Ideally we would not add a new dag.Foo() call to the API to implement this, just to keep the user-facing change as simple as possible.

The context directory is the git repository containing the module. If there is not git repository, the context is simply the module.

This matches today’s context directory, but If this is a stepping stone into #7199 where the context directory is the parent directory it will be a breaking change later. Does it make sense for this path to match that proposal instead?

I think we should focus on this, and accept that it might require adapting #7199. I'm OK with that.
Note that in this proposal, term "the context directory" becomes less important to the user.

Relative paths are relative to the current source file, scoped to the context directory for security

The engine doesn’t have any knowledge of source files, so the SDK needs to resolve the path in relation to what? "source" directory is what makes the most sense because that’s what the SDK’s language code knows about.

Yeah that makes sense. It's unfortunate from a UX point of view, but I see how it might be unavoidable. @vito you suggested the relative-to-file idea, wdyt? Is relative-to-source-dir ok?

I like it. Also, good job on narrowing the scope. I suspect this will stay useful even if the concept of contextual modules change in the future.

Yes agreed! And by the way the ignore part is actually orthogonal to context: even if the caller overrides the default value for a directory, the ignores will still be applied. So it's basically a better version of views, and will be a major improvement for monorepos I believe.

How is this going to behave in reusable modules? What's the context directory in those cases (when the module is actually called from another module)?

I think the context directory is always the current module's git repo.

I know I already asked this, but just to clarify: if I call a remote module, the context directory is going to be that module's repository, right?

Yes I think so.

(Is .git. preserved for remote module calls?)

We haven't discussed it, do you have a preference? @sipsma @jedevc are both technically possible?

'.git' is often required by build tools, so my preference would be to preserve it.

One thing I don't understand from this example is how we build in isolation?
There's a few modalities, two of which are more important:

Build Everything (CI)
Build One Thing (Local)

The example covers 1, but not 2.
If I am in the frontend directory and I run a build, what happens?

One thing I don't understand from this example is how we build in isolation?
There's a few modalities, two of which are more important:

Build Everything (CI)
Build One Thing (Local)

The example covers 1, but not 2.
If I am in the frontend directory and I run a build, what happens?

The idea is that once a module can access its context, it becomes easier to create a dedicated module for each component of your monorepo. So in your case, you would probably create a dedicated dagger module under the frontend directory, and call build there.

Maybe I'm missing something. How would it work if my backend calls the frontend to get the static build and each module needs its own context?
I would need to define the front ends context directory in every caller?

@shykes

Yeah that makes sense. It's unfortunate from a UX point of view, but I see how it might be unavoidable. @vito you suggested the relative-to-file idea, wdyt? Is relative-to-source-dir ok?

That sounds like a good compromise.
I suggested relative-to-current-file('s dir) just because that's always my first guess, but the downside would be having to tweak these values as you move module code around. (../ => ../../ :nauseated_face:)
Also FWIW I'd hope relative paths are mostly used to pass files from within the source dir. Using ../ too much seems kinda weird, vs. absolute paths. But maybe there are repo layouts where it makes sense.

../ makes sense when the source dir is not the module root. For example in our repo: sdk/python/dev needs to access the source code of /sdk/python. It makes sense to use .. in that case.  Same reason I hate that go doesn't support relative imports.

RE: .git, agree it's needed often enough that we can't just always exclude it. The problems with it though are that A) it changes often in ways that unexpectedly invalidate the cache and B) can be quite large, which is a waste when it's not needed.
So we just need support for include/exclude. We can choose whether it's excluded by default and requires the user override that to explicitly include or it's just like any other dir and needs to be explicitly excluded if not desired.
I'd probably air on not treating it specially right now for conceptual simplicity, just include by default and require explicit exclude, but could still be convinced otherwise.

@sipsma, can you clarify the implementation detail on the engine, whether it needs to be hooked into some larger "process" like adding patterns to the context directory upload or if it's just a lazy dag.Host().Directory() underneath as a default value?

@helderco I think we'll want to start relatively simple. There will be some performance deficiencies for local context dirs (as opposed to git, etc.) due to how local filesync works in buildkit but we can address those over time.
Very high level outline would be:

Add support for the pragma/annotation in the SDKs and TypeDefs, to mark args as being from the context dir + params like path/include/exclude/etc.
Pick up those settings when constructing the args to module function calls, somewhere around this line or so
If context dir, load the context from the context dir (which can be plumbed into ModuleFunction as a field in that struct), set the arg to the ID of that directory

There's a lot of variations on how to approach it, that's just one, but I think they all have that general shape.
The performance deficiency around local dirs is that right now when you load the same local directory repeatedly with different filter patterns, the cache of that local dir will be updated to match those patterns by either deleting files which shouldn't exist or loading ones that don't exist in the engine's cache. So if you use different filter patterns you can end up e.g. deleting a file that was previously loaded, but then later reloading it if it's needed for a different context dir arg with different filters.

There are some ideas on how to fix that, ranging from "relatively straightforward" to "very complicated but highly efficient", but we should probably not rope all that into the first iteration unless proven utterly necessary.

Simplest thing in sdks is to use a lazy API as a default value like I showed above:

this approach SGTM!. AFAK we're currently using this approach with other types like CacheVolume.  Is that correct?

The SDK still needs an API to communicate this to the engine. Would it be the new dag.Context().Directory(...)? For SDKs that support default values, that API would be used directly.

I don't know. Ideally we would not add a new dag.Foo() call to the API to implement this, just to keep the user-facing change as simple as possible.

I understand, but while it's simpler in Go because there's pragma comments to generate default values, it's more complicated for SDKs that do support default values. Using a lazy API object is easier, more consistent with the rest of the DX, and it doesn't change the suggested implementation for Go. You'd still use the same pragma in Go as suggested (the Go SDK would produce the API object under the hood), while SDKs that support default values would use the lazy object directly.

Simplest thing in sdks is to use a lazy API as a default value like I showed above:

this approach SGTM!. AFAK we're currently using this approach with other types like CacheVolume. Is that correct?

Yes, that's right.

If not using an API call like that, how would you implement custom metadata to a function argument in TypeScript, @TomChv? See Go example in the description.

I can also do it with lazy evaluation like you proposed in Python.
build(
   frontend: Directory = dag.context().directory("../backend/app/src")
)

But I also think it's doable with decorators that would act like pragma
build(
   @context("../backend/app/src")
   frontend: Directory
)

This is a bit more tricky, because if we extends to different type of context like OS configuration, I would need to bind it to the type of the argument but it's definitely doable.
However, it creates a difference between a contextual agument and an actual default argument that would allow both with this notation, and I'm not sure we want to allow that.
For example, how do you handle this:
build(
   @context("../backend/app/src")
   frontend: Directory = dag.directory().withNewfile("xxx", ...)
)

I see the issue. But the problem with creating dag.context().directory() for SDKs that support real default arguments, is that then people will call it directly too, why would they not? That breaks the design that we agreed on, because we can't rely on arguments for caching (per @sipsma) and risk fragmentation with context as a "shadow argument" (per @vito).

Yes @helderco you are correct, ignore is its own thing. I should have mentioned that in the issue.

Yes @helderco you are correct, ignore is its own thing. I should have mentioned that in the issue.

It's obviously separate, I just rushed into the idea that they could both go into the same call 😇

But the problem with creating dag.context().directory() for SDKs that support real default arguments, is that then people will call it directly too, why would they not?

Good point.

[...] we can't rely on arguments for caching (per @sipsma)

Not sure I understand.

Simplest thing in sdks is to use a lazy API as a default value like I showed above:
def build(
    self,
    frontend: dagger.Directory = dag.context().directory("../backend/app/src"),
) -> dagger.Directory:
    ...

So Go's pragma would just use the pragmas to build the query object and set it as the default value. Then the engine would do it's thing while interpreting the default value of the argument during the load/registration phase, instead of new TypeDef fields.

@helderco This might work as is, but it's pretty complicated+tenuous and there's at least one place where I'm not sure yet if we'd need some internal engine changes.
A lot of the complication has to do with the fact that dag.context().directory(), when used with modules loaded from local dirs, results in an ID that is not just {context{directory{id}}} but instead more like {blob(digest:"somesha"){id}}. That's due to the fact that when we load a local dir, we synchronously do the load and then return an id that references the content hash of that loaded dir (otherwise you get all sorts of problems around caching+determining whose local dir you are actually referencing and want to load)
The places this is going to matter are:

The evaluation of default arg values needs to happen in the SDK itself (not engine) and during module initialization only (not every invocation). I think this is the case today, but becomes a hard requirement now.
When we add support for caching all function calls, we need to make sure that we link the caches of the context dir load with the cache of the function call.

We already have some code for this since function calls used to be cached, but I'm not 100% sure if it works with default arg values yet, this is where we may need some adjustments.
This is the call in question that does the cache linking
Otherwise we're gonna end up in situations where the engine goes looking for context dir blobs that no longer exist in the cache and get an error.

The approach I outlined avoids all of these concerns and I think in the long term would be more robust for other uses of "contextual arguments" that were being discussed. I'd be okay with whatever approach works for now, just want to make sure there's awareness of these issues that need to be dealt with and checked while implementing.

@sipsma I'm trying to connect the discussion about the internals (which I don't fully grasp) to the DX users would see in the end. In the approach you recommend, what would an example look like in Python or Typescript?

To be clear, what I suggested was not for dag.context().directory() to do the upload itself, but to just return an object such that the engine, when processing default arguments during a module load, would then create the actual dagger.Directory there.

I'm trying to connect the discussion about the internals (which I don't fully grasp) to the DX users would see in the end. In the approach you recommend, what would an example look like in Python or Typescript?

For Typescript, I was imagining something like what Tom had here with @context https://github.com/dagger/dagger/issues/7647#issuecomment-2173375044
For Python, I was imagining something similar with @ wrappers, but am now questioning whether that's actually syntactically possible. @helderco is it? Or if not would type annotations be feasible to use here? Just to explore if this is even a viable path, separate from whether we should do it this way vs. default arg values.

To be clear, what I suggested was not for dag.context().directory() to do the upload itself, but to just return an object such that the engine, when processing default arguments during a module load, would then create the actual dagger.Directory there.

I see, the thing is I would greatly prefer to avoid that being generally usable across the API, instead I'd wan tto restrict it only to be used for default arg values. Otherwise it will be passable as the value of an arg when invoking functions and is going to be incredibly confusing, due to what I mentioned around the fact that dag.context().directory() has different values for different clients. One client would pass a value of dag.context().directory() and the other client would see something completely different than the caller. It would feel like purely a source of confusion rather than utility other than the default arg value case.
But at that point, if this is restricted to only being usable as a default arg value, it feels like it may as well be just a setting on TypeDef.
So to summarize my perspective on the options:

Make this a part of TypeDef, go uses pragmas, typescript uses @context, python uses something TBD based on question at beginning of this comment
We use default arguments like Helder suggested, but implement dag.context().directory() as a real usable API call that results in a content-hashed dir loaded, to avoid confusion I mentioned above. And just be very careful to deal with the subtleties I outlined here

If 1 is feasible in a reasonable way for Python, that's my current preference. But otherwise 2 is fine too.

To be clear, what I suggested was not for dag.context().directory() to do the upload itself, but to just return an object such that the engine, when processing default arguments during a module load, would then create the actual dagger.Directory there.

I see, the thing is I would greatly prefer to avoid that being generally usable across the API, instead I'd wan't to restrict it only to be used for default arg values. Otherwise it will be passable as the value of an arg when invoking functions and is going to be incredibly confusing, due to what I mentioned around the fact that dag.context().directory() has different values for different clients. One client would pass a value of dag.context().directory() and the other client would see something completely different than the caller. It would feel like purely a source of confusion rather than utility other than the default arg value case.

Yeah, but I'm also suggesting this working only on default args, since that's where then engine would interpret and load that value (when saving the default value of a Directory argument).
If used elsewhere it would return an error like dag.Host at runtime, or when using CurrentModule outside of a module.

To be very clear, this is only relevant in the scenario where dag.context().directory() is lazy and not content hashed (making it different than other dirs that can be sourced from local sources). In the case where dag.context().directory() is content hashed with the blob stuff I mentioned above, this confusion wouldn't be an issue.

It's lazy only during registration, but during execution the engine would send the actual dagger.Directory (saved as default value for the argument while loading).

But at that point, if this is restricted to only being usable as a default arg value, it feels like it may as well be just a setting on TypeDef.
So to summarize my perspective on the options:

Make this a part of TypeDef, go uses pragmas, typescript uses @context, python uses something TBD based on question at beginning of this comment
We use default arguments like Helder suggested, but implement dag.context().directory() as a real usable API call that results in a content-hashed dir loaded, to avoid confusion I mentioned above. And just be very careful to deal with the subtleties I outlined here

If 1 is feasible in a reasonable way for Python, that's my current preference. But otherwise 2 is fine too.

Nr 2 is not what I suggested, in the sense of a "real usable API call" with those subleties. I suggested something more akin to nr 1 but using the default value support in the language for that info. That's what seems the most natural to me. The one question I have there is that the return of dag.context().directory() would need to match the type dagger.Directory while also being slightly different from it so that the engine can detect it appropriately during module load.

Just to update, I had a call with @sipsma and we have an understanding. He sees my points on using the default value but I also see the complications around caching and performance if not properly lazy. Actually warming up to not using the default value because of that as it makes an overall simpler implementation, just sacrificing on DX for SDKs that support default values, but that can be improved later.

Yes @helderco you are correct, ignore is its own thing. I should have mentioned that in the issue.

It's obviously separate, I just rushed into the idea that they could both go into the same call 😇

Wait, it's not obvious to me now. If you're passing a local dir through the CLI we can probably use these patterns to filter in dag.Host().Directory(), but would the CLI try to filter a Directory from a git reference? Or just apply to dag.Host and dag.Context?

Yes @helderco you are correct, ignore is its own thing. I should have mentioned that in the issue.

It's obviously separate, I just rushed into the idea that they could both go into the same call 😇

Wait, it's not obvious to me now. If you're passing a local dir through the CLI we can probably use these patterns to filter in dag.Host().Directory(), but would the CLI try to filter a Directory from a git reference? Or just apply to dag.Host and dag.Context?

Yes I believe the filter should always be applied to the argument, no matter where the value of the argument comes from. Conceptually the ignore annotation should work the same as if the function filtered its argument in code - just with better performance.

Yes @helderco you are correct, ignore is its own thing. I should have mentioned that in the issue.

It's obviously separate, I just rushed into the idea that they could both go into the same call 😇

Wait, it's not obvious to me now. If you're passing a local dir through the CLI we can probably use these patterns to filter in dag.Host().Directory(), but would the CLI try to filter a Directory from a git reference? Or just apply to dag.Host and dag.Context?

Yes I believe the filter should always be applied to the argument, no matter where the value of the argument comes from. Conceptually the ignore annotation should work the same as if the function filtered its argument in code - just with better performance.

Yeah, and @sipsma also clarified (in Discord) that we may already do that for git module sources and there's an issue for sparse loading git refs that would allow the use of the patterns make a performance optimization.

@helderco @TomChv I'm moving the remaining DX discussion here, rather than the PR (#7744) for durability, and to avoid fragmentation.
So far we have this:
Go
func (app *App) Build(
  // +default="./frontend/src"
  frontend *Directory,
  // +default="./backend/app/src"
  backend *Directory,
  // +default="/tools/builder.conf"
  buildConfig *File,
) *Directory {}

Typescript
@func()
build(
  @context("./frontend/src") frontend: Directory,
  @context("./backend/app/src") backend: Directory,
  @context("./tools/builder.conf") buildConfig: File,
): Directory {}

Python
Done by Helder above
def build(
  self,
  backend: Annotated[
     dagger.Directory,
     DefaultFromContext("../backend/app/src"),
      Ignore(...),
  ]
) -> dagger.Directory:
  ...

And if I understand correctly, it's not possible in typescript to do frontend: Directory =   context("./frontend/src") ?

And if I understand correctly, it's not possible in typescript to do frontend: Directory =   context("./frontend/src") ?

I can but I cannot execute it at the registration time, so it will be at the runtime

I can but I cannot execute it at the registration time, so it will be at the runtime

Ah I see. Does this have practical consequences? I guess the default value won't be introspectable in GraphQL - to GraphQL clients it will only look like a null default?

Ah I see. Does this have practical consequences? I guess the default value won't be introspectable in GraphQL - to GraphQL clients it will only look like a null default?

Yeah it will looks as optional

Yeah it will looks as optional

But that's already the case with default values on directories today in Typescript, right? Not specific to context?

But that's already the case with default values on directories today in Typescript, right? Not specific to context?

Yes exactly

And if I understand correctly, it's not possible in typescript to do frontend: Directory =   context("./frontend/src") ?

I can but I cannot execute it at the registration time, so it will be at the runtime, or I would parse the static code and generate a function from it.
Technically I can, not sure it's the nicest way though

I think @TomChv is making it harder than it is. What he's calling "registration time" I believe he's thinking about the scanning code part, but it's only a part of it. To report functions to dagger, that code already runs at runtime. To me, the registration phase ends when you send the returnValue of ModuleID.
There's other issues, like having to satisfy the type check on dagger.Directory and making sure the value isn't attempted to be used within the body of the function or by directly calling another function within the module (TS would apply the default).

@helderco would it be possible to summarize the issues and tradeoffs here, for the record?

@helderco would it be possible to summarize the issues and tradeoffs here, for the record?

Yeah, but it'll have to be tomorrow 🙂

@helderco would it be possible to summarize the issues and tradeoffs here, for the record?

Yeah, but it'll have to be tomorrow 🙂

Of course, thank you!

 What he's calling "registration time" I believe he's thinking about the scanning code part, but it's only a part of it. To report functions to dagger, that code already runs at runtime. To me, the registration phase ends when you send the returnValue of ModuleID.

What I'm talking about is the user code, it's not run during the registration part, it's only statically analyzed.
So the default value cannot be resolved.

 What he's calling "registration time" I believe he's thinking about the scanning code part, but it's only a part of it. To report functions to dagger, that code already runs at runtime. To me, the registration phase ends when you send the returnValue of ModuleID.

What I'm talking about is the user code, it's not run during the registration part, it's only statically analyzed. So the default value cannot be resolved.

In this case it doesn't necessarily matter. You're assuming it's an api call, but we're talking about an SDK function that you don't even have to execute. The idea is to do as you're doing but pick up the directory path from the default argument instead of the decorator.
Anyway, it doesn't matter since there's larger issues when using this DX.

@helderco would it be possible to summarize the issues and tradeoffs here, for the record?

TL:DR; at the end.
I’m going to use mostly Python to balance two options for setting this default:

As a parameter default value
As a parameter metadata

Go doesn’t support default values for parameters so the Dagger Go SDK implements defaults as metadata.
Example (for comparison)
// Build the app
func (app *App) Build(
  // Frontend source code
  // +default="./frontend/src"
  frontend *Directory,
  // Backend source code
  // +default="./backend/app/src"
  // +ignore=["*", "!**/*.go", "!go.mod", "!go.sum"]
  backend *Directory,
  // Build tool configuration, shared across the monorepo
  // +default="/tools/builder.conf"
  buildConfig *File,
) *Directory {
  // Execute the build as usual
}

In Python, the most natural DX here would be, at first glance, to use the language’s default value for function parameters. It’s technically possible to do it in both Python and TypeScript, but you have to jump through hoops and well… it’s a bit awkward.
As a parameter default value
@function
def build(
    self,

    frontend: Annotated[
        dagger.Directory,
        Doc("Frontend source code"),
    ] = from_context("./frontend/src"),

    backend: Annotated[
        dagger.Directory,
        Doc("Backend source code"),
        Ignore(["*", "!**/*.go", "!go.mod", "!go.sum"]),
    ] = from_context("./backend/app/src"),

    build_config: Annotated[
        dagger.File,
        Doc("Build tool configuration, shared across the monorepo"),
    ] = from_context("/tools/builder.conf"),

) -> dagger.Directory:
    """Build the app"""
    # Execute the build as usual

Note: Annotated receives a type as first argument, and a variable number of metadata that can be anything we want: Annotated[<type>, <metadata>...].
Why?

It’s a default, so it feels more natural to define as the parameter’s default rather than metadata.
Don’t have to worry about the user also setting a default for the parameter, when using metadata (like ignore).

Why not?
The general issue is that it’s like trying to fit a square peg in a round hole because there’s some constraints that make it tricky to implement:

Should only be used in function signatures, not function bodies
Needs to be a dagger.Directory during execution
But needs to be slightly different than dagger.Directory during registration

Type checking hurdles
If you have something that’s not a dagger.Directory:
# ContextDirectory provided by the SDK

@function
def build(src: Directory = ContextDirectory("..")) -> Directory:
    ...

You get a type check error:
Pyright: ... "ContextDirectory" is incompatible with "Directory"

But you can trick the type checker by wrapping it in a function that returns Any:
# `from_context` provided by the SDK
def from_context(path: str) -> Any:
    return ContextDirectory("..")

@function
def build(src: Directory = from_context("..")) -> Directory:
    ...

However, now the linter complains:
Ruff: Do not perform function call `from_context` in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable [B008]

The linter is trying to protect us against a known footgun in Python that I won’t go into right now. What’s usually done is to either allow None in the parameter and set the default within the body of the function (won’t work for us), or use a sentinel value like this:
FOO_SRC: Any = ContextDirectory("..")

@function
def build(
    src: Annotated[Directory, Ignore(["**/.venv"])] = FOO_SRC,
) -> Directory:
    ...

That satisfies both the type checker and the linter, but requires users to set the path outside the function, which isn’t great.
API call
What if, instead of an SDK specific ContextDirectory object, we use an API to reduce the burden on SDKs? In this case, you have less freedom to mess with the types. The simplest thing would be for the API call to return an actual dagger.Directory:
@function
def build(
    src: Annotated[Directory, Ignore(["**/.venv"])] = dag.context().directory(".."),
) -> Directory:
    ...

But:

It can’t be quite the same dagger.Directory because the engine needs to know that this should be loaded differently, and at a later time (laziness).
The engine needs to be aware if this is being used during registration or execution, in order to prevent it from being used directly in the function body. It would be the only API call that can only be used in a function’s signature.
If the SDK sees a dagger.Directory (when used in with_directory, for example), it’ll try to serialize it by getting its ID, but the ID from a context directory is different. We need to be careful about caching and other subtleties. Probably having to store some private field in core’s dagger.Directory for just this use case, which may be a leaky abstraction.
If, instead of being lazy, we make a sync call during registration, it would fix a lot issues, but not performance. We only want to load the files from the context when the function is executed and that parameter is omitted. Otherwise you’ll always load the files, even when not needed.

Direct calls
In order to prevent ContextDirectory (or dag.context().directory()) to be used as an actual Directory in body functions, we need to remove it from the function’s signature within the @function decorator. But when you make a direct call from another function, the magic here gets in the way of clarity:
FOO_SRC: Any = ContextDirectory("..")

@function
def build(
    src: Annotated[Directory, Ignore(["**/.venv"])] = FOO_SRC,
) -> Directory:
    ...

@function
def test() -> Container:
    return dag.container().from_("alpine").with_directory("/dist", build())

The user may expect that ContextDirectory is passed into with_directory in the test function. They may even try passing FOO_SRC in directly. So we need to try to detect if it’s used directly, to raise an error, but not if used in the signature.
There’s a bigger issue though. What would you expect the src argument to be when you call build() directly like that? An empty Directory? That would be surprising. Raise an error? That would be limiting. I think the best thing here is to make src a required argument in this case.
There’s an epiphany. It’s optional only for Dagger, but not Python. The best way to express that is with metadata, because you can control both independently.
As metadata
@function
def build(
    self,
    frontend: Annotated[
        dagger.Directory,
        Doc("Frontend source code"),
        Argument(default="./frontend/src"),
    ],
    backend: Annotated[
        dagger.Directory,
        Doc("Backend source code"),
        Argument(
            default="./backend/app/src",
            ignore=["*", "!**/*.go", "!go.mod", "!go.sum"],
        ),
    ],
    build_config: Annotated[
        dagger.File,
        Doc("Build tool configuration, shared across the monorepo"),
        Argument("/tools/builder.conf"),
    ],
) -> dagger.Directory:
    """Build the app"""
    # Execute the build as usual

This solves all problems listed above, and it’s actually fine. Annotated is a part of Python’s standard library. It’s known and used by libraries and tooling, including IDEs.
Prior art
FastAPI
The very popular FastAPI framework uses Annotated for various parameter metadata, but when there’s a default, using the actual parameter’s default is preferred:
# not allowed
q: Annotated[str, Query(default="rick")] = "morty"

# preferred
q: Annotated[str, Query()] = "rick"

# old alternative
q: str = Query(default="rick")

From their docs:

When you don't use Annotated and instead use the (old) default value style, if you call that function without FastAPI in other place, you have to remember to pass the arguments to the function for it to work correctly, otherwise the values will be different from what you expect (e.g. QueryInfo or something similar instead of str). And your editor won't complain, and Python won't complain running that function, only when the operations inside error out.

Pydantic
Pydantic has an equivalent Field that can be used with Annotated to provide extra information about a parameter, but you need to use it as a default value if you want to set a default:
@validate_call
def how_many(num: Annotated[int, Field(gt=10)]):
    return num

@validate_call
def when(dt: datetime = Field(default_factory=datetime.now)):
    return dt

Typer
The Typer library (like Mage) started to adopt Annotated when it became available too. In this case, if the default is static you use the parameter’s natural default value, but if it’s dynamic, you set in as metadata:
def get_name():
    return random.choice(["Deadpool", "Rick", "Morty", "Hiro"])

# static default
def main(name: Annotated[str, typer.Argument()] = "Wade Wilson"):
    print(f"Hello {name}")

# dynamic default
def main(name: Annotated[str, typer.Argument(default_factory=get_name)]):
    print(f"Hello {name}")

That’s the most similar to us. We want a default that Dagger will fill in for us, but where it could be different between different invocations (dynamic).
TypeScript
TypeScript has the same downsides for using the parameter’s default value. The main issue is direct calls. It can implement the metadata as a parameter decorator though:
/**
 * Build the app
 *
 * @param frontend Frontend source code
 * @param backend Backend source code
 * @param buildConfig Build tool configuration, shared across the monorepo
 */
@func()
build(
  @argument({ default: "./frontend/src" })
  frontend: Directory,

  @argument({
    default: "./backend/app/src",
    ignore: ["*", "!**/*.go", "!go.mod", "!go.sum"],
  })
  backend: Directory = from_context("./backend/app/src"),

  @argument({ default: "/tools/builder.conf" })
  buildConfig: File = from_context("/tools/builder.conf"),

): Directory {
  // Execute the build as usual
}

Note: Python doesn’t support decorators in function arguments.
However, I’m not familiar with any prior art here. Decorators don’t seem like widely used (long time in experimental stage), but when it is (and we do), it’s a viable solution.

TL;DR

Using the parameter’s default value seems like the best DX at first glance, but in the end it’s like trying to fit a square peg in a round hole. It takes a lot of effort to make it be something that it isn’t, and that makes it awkward.

Tricking the type checker and linter
Handling direct calls within the same process (it's optional for Dagger, but not for the language)
It’s not a static value at import time like parameter defaults need to be, it’s dynamic and needs to be evaluated during runtime

There’s very popular Python libraries that also use Annotated for similarly adding metadata to function arguments. In some cases actually migrating from the parameter’s default value to Annotated instead[^1].
We already need to support extra metadata in all SDKs for Ignore anyway. It’s much easier to implement in Python and TypeScript, but if a certain language doesn’t have the same issues with parameter default values, then it should use it if that’s what’s most natural in the language.

[^1]: As in using the default value being the “old” way, while Annotated being the new.
Go
// Build the app
func (app *App) Build(
  // Frontend source code
  // +default="./frontend/src"
  frontend *Directory,
  // Backend source code
  // +default="./backend/app/src"
  // +ignore=["*", "!**/*.go", "!go.mod", "!go.sum"]
  backend *Directory,
  // Build tool configuration, shared across the monorepo
  // +default="/tools/builder.conf"
  buildConfig *File,
) *Directory {
  // Execute the build as usual
}

Python
@function
def build(
    self,
    frontend: Annotated[
        dagger.Directory,
        Doc("Frontend source code"),
        Argument(default="./frontend/src"),
    ],
    backend: Annotated[
        dagger.Directory,
        Doc("Backend source code"),
        Argument(
            default="./backend/app/src",
            ignore=["*", "!**/*.go", "!go.mod", "!go.sum"],
        ),
    ],
    build_config: Annotated[
        dagger.File,
        Doc("Build tool configuration, shared across the monorepo"),
        Argument("/tools/builder.conf"),
    ],
) -> dagger.Directory:
    """Build the app"""
    # Execute the build as usual

TypeScript
/**
 * Build the app
 *
 * @param frontend Frontend source code
 * @param backend Backend source code
 * @param buildConfig Build tool configuration, shared across the monorepo
 */
@func()
build(
  @argument({ default: "./frontend/src" })
  frontend: Directory,

  @argument({
    default: "./backend/app/src",
    ignore: ["*", "!**/*.go", "!go.mod", "!go.sum"],
  })
  backend: Directory,

  @argument({ default: "/tools/builder.conf" })
  buildConfig: File,

): Directory {
  // Execute the build as usual
}

FYI @sipsma @vito ⬆

@helderco the tradeoff seems reasonable. A few questions on the details:

This would mean 2 different ways to set default values. Developers will need help navigating that. Which way should they choose when?

Nit. This feels weird:

    backend: Annotated[
        dagger.Directory,
        Doc("Backend source code"),
        Argument(
            default="./backend/app/src",
            ignore=["*", "!**/*.go", "!go.mod", "!go.sum"],
        ),
    ],

The entire annotation applies to an argument, including the type hint and Doc. Feels weird that half the annotations are under Argument and the other half isn't, when by definition they all apply to an argument.
This would feel simpler and more logical:
    backend: Annotated[
        dagger.Directory,
        Doc("Backend source code"),
        Default("./backend/app/src"),
        Ignore("*", "!**/*.go", "!go.mod", "!go.sum"),
    ],

@helderco the tradeoff seems reasonable. A few questions on the details:

This would mean 2 different ways to set default values. Developers will need help navigating that. Which way should they choose when?

The SDK should raise an error if the parameter also has a default value. They should be mutually exclusive. There's a use case of setting the default value, for when you use direct calls for example, but the annotation would take precedence when the call comes from Dagger. I suggest for now to just error to keep things simpler, and revisit that possibility when there's demand for it.

Nit. This feels weird:

    backend: Annotated[
        dagger.Directory,
        Doc("Backend source code"),
        Argument(
            default="./backend/app/src",
            ignore=["*", "!**/*.go", "!go.mod", "!go.sum"],
        ),
    ],

The entire annotation applies to an argument, including the type hint and Doc. Feels weird that half the annotations are under Argument and the other half isn't, when by definition they all apply to an argument.
This would feel simpler and more logical:
    backend: Annotated[
        dagger.Directory,
        Doc("Backend source code"),
        Default("./backend/app/src"),
        Ignore("*", "!**/*.go", "!go.mod", "!go.sum"),
    ],

That's actually how I started, and I agree that Doc may look out of place, but there’s a reason it’s separate. Only today while writing the previous comment and talking to Tom I changed the examples to use a single dagger.Argument.
Right now Python has two of those metadata objects:

Doc: for Dagger descriptions
Arg: for adjusting the name that's exposed to Dagger

I want to deprecate Arg and move the name option to Argument[^1], just keep it as an escape hatch if users need it.
[^1]: The main reason that was created was to support names that are reserved keywords. from is a reserved keyword, so if you want a function with a parameter called "from", you can’t. Recommendation is to append an underscore (from_), and use Arg("from") to override that name. This used to be documented but now it isn’t, so Python users don’t know how to get around it. This can be done automatically by having the SDK remove the last underscore in names, though. I’d just keep it anyway in case there’s other use cases that need it.
So the reason for a single Argument metadata object is threefold:

Allows future expansion while reducing the boilerplate to read the metadata, and avoiding more imports (same with TS).
It’s consistent with other popular Python libraries that do something similar.
Tom’s feedback is that it looks weird with multiple different decorators on top of each other, so if TypeScript implements as a single object it would keep consistency between Py and TS.

I considered moving Doc on over, same as Arg, but there’s a reason Doc exists. It’s because there’s an actual PEP trying to make this a standard way to provide documentation strings for Python symbols: PEP 727 – Documentation in Annotated Metadata.
It’s already available from the Typing Extensions library, which backports latest and future features to older Python versions.
The goal is to make it easier to access these programmatically. For example, I could document a class property with a docstring below it:
class Human:
    name: str
    """The name of this human"""

Depending on the IDE, they can show that description, but it’s not available at runtime to use programmatically like with classes and functions.
If it becomes a standard to use Doc with Annotated, Dagger will benefit from adopting it.
I’m open to implement as multiple objects instead of a single one though.

This would mean 2 different ways to set default values. Developers will need help navigating that. Which way should they choose when?

The SDK should raise an error if the parameter also has a default value. They should be mutually exclusive. There's a use case of setting the default value, for when you use direct calls for example, but the annotation would take precedence when the call comes from Dagger. I suggest for now to just error to keep things simpler, and revisit that possibility when there's demand for it.

I understand and agree with that part, but my question was about all the other times that developers have to think about default values, and decide how to define them.
For example: "my function has a string argument, I want to set a default value. I asked on Discord, and I was told there are two different ways to set them, and it depends. Now I'm confused. It's just my first day learning Dagger and I'm feeling out of my depth already".
What do we answer to that?

I understand and agree with that part, but my question was about all the other times that developers have to think about default values, and decide how to define them.

Oh, If I understand you correctly, this Default() metadata would only be used for the "default path from context", used only with dagger.Directory or dagger.File and erroring otherwise.
I considered a more expressive name instead of just "Default", but it's too verbose and it's just // +default in Go so it requires documenting this use case anyway.
In Python and TS this documentation can be provided by the IDE itself, just by hovering on it.

Oh, If I understand you correctly, this Default() metadata would only be used for the "default path from context", used only with dagger.Directory or dagger.File and erroring otherwise.

But isn't that different from the prior art you linked to, where the annotation is presented as the preferred way to set defaults, not specific to a certain type?

But isn't that different from the prior art you linked to, where the annotation is presented as the preferred way to set defaults, not specific to a certain type?

Not necessarily. Revisit for example Typer's case:
def get_name():
    return random.choice(["Deadpool", "Rick", "Morty", "Hiro"])

# static default
def main(name: Annotated[str, typer.Argument()] = "Wade Wilson"):
    print(f"Hello {name}")

# dynamic default
def main(name: Annotated[str, typer.Argument(default_factory=get_name)]):
    print(f"Hello {name}")

In general terms, the distinguishing factor is the following. The parameter's default value is preferred when it's a static value. If it's dynamic then the use of Annotated is preferred.
In Dagger it means this could be open to all types for more dynamic use cases, by allowing a callable in  Default:
def something_dynamic() -> Directory:
    ...

@function
def build(platform: Annotated[Platform, Default(something_dynamic)]) -> Directory:
    ...

But if, same as with context directory, some other use case comes along with a specifc need for a default for another type that can't be defined as the parameter's default, then using Annotated is also appropriate for that specific case too.

In that case, would it be acceptable to show the Annotated way for all examples everywhere, for consistency? When users ask "can I also use regular default values", then we explain that yes, but only for static values.
The benefit is that it will be one less rule that every Dagger developer has has to know.

In that case, would it be acceptable to show the Annotated way for all examples everywhere, for consistency? When users ask "can I also use regular default values", then we explain that yes, but only for static values.

I don't think so. I understand the appeal but using Annotated should be the exception to the rule, not the other way around. In general, using the parameter's default value should be the preferred approach when the value is static.

The benefit is that it will be one less rule that every Dagger developer has has to know.

We aren't alone in this. Other libraries also need to explain when to use one vs the other.
If you look at Field in Pydantic, they make you use Annotated for that metadata, but if you need to use "default", then you're told to put all the metadata in the default value, instead of just the default:
@validate_call
def how_many(num: Annotated[int, Field(gt=10)]):
    return num

@validate_call
def when(dt: datetime = Field(default_factory=datetime.now)):
    return dt

Notice the first one is in Annotated, while the other one is in the default value.
If I use this very popular library and lint it with Ruff, using Hatch's defaults, it'll show me an error on the second function, for using a callable as a default.
You could argue that that linter error is not a concern because users can silence or ignore it in their configs, and the consistency is more valuable. But you're still left with the other issues I mentioned when using this approach.
I think adapting to Annotated as an exception to the rule is more technically sound, and I think Python users will appreciate that more because it follows the way Python works.
It's better to avoid having more situations where it's needed if we can.

I just realized something about the name dagger.Argument, btw.
In Python, Doc can be used in both function arguments and object attributes:
@object_type
class PythonDev:
    src: Annotated[Directory, Doc("The source code")]

The attributes of an @object_type however, become arguments in the auto-generated constructor:
class PythonDev:
    ...

    # __init__ is automatically generated
    def __init__(self, src: Annotated[Directory, Doc("The source code")]):
        self.src = src

If we use Argument(default=...), then it’s weird as an attribute:
class PythonDev:
    src: Annotated[Directory, Doc("The source code"), Argument(default="..")]

But with separate metadata objects, it works as both an attribute or an argument:
class PythonDev:
    src: Annotated[Directory, Doc("The source code"), Default("..")]

    # __init__ is automatically generated
    def __init__(
        self,
        src: Annotated[
            Directory,
            Doc("The source code"),
            Default(".."),
        ],
    ):
        self.src = src

[...] using Annotated should be the exception to the rule, not the other way around. In general, using the parameter's default value should be the preferred approach when the value is static.
[...sound arguments...]

OK, makes sense.
In that case I think we should change the name of the metadata object, to reduce its scope. Instead of Default I propose we call it DefaultPath. That way it will apply to File and Directory. The name will better align with the behavior, and everything will be more intuitive.

Annotation
Description

Doc()
Documentation string for an argument, function or field

DefaultPath
Only applies to arguments of type File or Directory. If the argument is not set, load it from the given path in the context directory

Ignore
Only applies to arguments of type Directory. The ignore patterns are applied to the input directory, and matching entries are filtered out, in a cache-efficient manner.

This way, there is no confusion as to when to use which. It's very intuitive that DefaultPath isn't a general alternative to regular default values, but a specific tool for a specific job.
We would align it in Go as well:
| Pragma | Description |
| // +default | Set a default value for the argument. Value must be a json-encoded literal value |
| // +defaultPath | Only applies to arguments of type File or Directory. If the argument is not set, load it from the given path in the context directory |
| // +ignore | Only applies to arguments of type Directory. The ignore patterns are applied to the input directory, and matching entries are filtered out, in a cache-efficient manner. |

Preemptively replying to @jedevc's concern from another thread:

I'm not a huge fan of using defaultPath as the pragma here (even though it's distinct at the API level).

What do you mean by "distinct at the API level"?

Could we just use default here?
It doesn't make sense to use defaultPath on a non File/Directory arg, and it doesn't make sense to use default on a File/Directory arg: we should condense this down into one default.

I am also sad that we can't use default, but consistency between SDKs is more important IMO. If there's a "default path" in Python and Typescript, we need one in Go also, or it's weird.


Nuxt document is not defined

I have problem with using vue-scrollTo on Nuxt. In my component I use:
VueScrollTo.scrollTo('#app', 200, { easing: 'linear' });
When page reloads I see: "document is not defined". Before page reloads it works fine. It seems to be problem with SSR. Do you have any ideas why?
I also tried to use: this.$scrollTo('#app', 200, { easing: 'linear' }); but I've got error "this.$scrollTo is not a function".

I'm not familiar with how nuxt handles the modules, and the current module has been kindly implemented by @ricardogobbosouza. He might know the answer to your issue.

Where do you use that code snippet exactly?
In SSR, you don't have document object and neither the scroll concept so make sure that you don't trigger scroll on server side. You can check if you are on server side using if (process.server) or just trigger your code from lifecycle hooks that don't run on server (mounted() for example). Refer to nuxt documentation to understand exactly how things work.

Hi @ProdigyMaster  as @rchl  said.
If you can share the snippet of your code

I solved my problem using process.client in my nuxt component. Thanks. You can close.
import { db, storage } from '~/plugins/firebase.js';
if(process.client) { var VueScrollTo = require('vue-scrollto'); }
import { mapGetters } from 'vuex';


ASP.NET Core 3.0 support

Hi,
Any work to support ASP.NET Core 3.0?
Thanks.

Hi. I think the project is die.

if any new reCaptcha library that supports .Core 3.0 comes up please notify me !

Hi,
seems like there's simply some kind of change in the way that views are rendered in development.
In production - the library is working as intended (though i have "CompileOnPublish" deactivated by default for another reason).
If you want to continue using it - and don't mind missing the feature during development - you can easily create a custom attribute, that ignores captcha-validation for the Development-Environment:
` [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateRecaptchaAttributeIgnoreDevelopment : Attribute, IFilterFactory, IOrderedFilter
{
///
public bool IsReusable => true;
    /// <inheritdoc />
    public int Order { get; set; }

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        var environment = serviceProvider.GetRequiredService<IWebHostEnvironment>();
        if (!environment.IsDevelopment())
            return serviceProvider.GetRequiredService<ValidateRecaptchaFilter>();
        return new DontValidateRecaptchaFilter();
    }
}

public class DontValidateRecaptchaFilter : IFilterMetadata
{

}`


IGNITE-13968 Add snapshot events documentation pages

Thank you for submitting the pull request to the Apache Ignite.
In order to streamline the review of the contribution
we ask you to ensure the following steps have been taken:
The Contribution Checklist

[ ] There is a single JIRA ticket related to the pull request.
[ ] The web-link to the pull request is attached to the JIRA ticket.
[ ] The JIRA ticket has the Patch Available state.
[ ] The pull request body describes changes that have been made.
The description explains WHAT and WHY was made instead of HOW.
[ ] The pull request title is treated as the final commit message.
The following pattern must be used: IGNITE-XXXX Change summary where XXXX - number of JIRA issue.
[ ] A reviewer has been mentioned through the JIRA comments
(see the Maintainers list)
[ ] The pull request has been checked by the Teamcity Bot and
the green visa attached to the JIRA ticket (see TC.Bot: Check PR)

Notes

How to Contribute
Coding abbreviation rules
Coding Guidelines
Apache Ignite Teamcity Bot

If you need any help, please email<EMAIL_ADDRESS>or ask anу advice on http://asf.slack.com #ignite channel.

@Nikita-tech-writer
Thank you for the review. I've applied all your suggestions.

@Nikita-tech-writer
Thank you for the review. I've applied all your suggestions.


Correct typo introduced in 4d122a9

As per your indication, this PR fixes my typo.

:+1:

Yes, the "word diff" is:
because[-not-] they are {+not+} acknowledged

Thanks!


Create model files for each model

Create model files for each model, based on the model list that was created
Dependent Issues
#3

I created a model for emails and news letter, let me know how it looks


Invite you and your Minecraft Modification to introduce to the player/audience in Taiwan.

Hello! We are the Cite Media Holding Group. a publisher in Taiwan. For now we’re devoted to Minecraft promotion, Our company is expected to publish a book entitled <The 100 Most Favorite Minecraft Modification for Game Talents> (tentative title) in December. And we will invite well-known Minecraft Game Talents in Taiwan to carefully select the 100 most recommended Minecraft Modification in the world and introduce them to readers with words and pictures. We think that the Modification of Minecraft you created is quite rich in content and suitable for being selected in the 100 recommended lists. To publish this book, we need to obtain your consent to capture the pictures of your game (less than 10, hereinafter referred to as the authorization target) so that vast readers of Minecraft can understand your work through the introduction of this book. Neither party has to pay the fee for this authorization, but in order to protect the rights and interests of both parties, this email will be used as the basis for authorization that you agree to authorize our company to use the authorization target free of charge in <The 100 Most Favorite Minecraft Modification for Game Talents> (tentative name) by means of, but not limited to, reproducing, distributing and public transferring. If you agree to the authorization, please reply to this email directly and fill in the following information (the following information is only for future reference of the authorizers and sending books, which will not be used for other purposes) to just inform us that you "agree to authorize the pictures free of charge". If you would like to provide your address, we would be very happy to present you a book in return after it is published.
Cite Media Holding Group
Vice General Manager
Daniel Wang
Add：10F, No.141, Sec 2, Misheng E. Rd., Thongshan Distric, Taipei City 10483, Taiwan （R.O.C.）
E-MAIL：
<EMAIL_ADDRESS>TEL：(02)2500-7600  ext.2670   MOB：0916-252-365

Hi there! Thanks for the request, we're currently discussing it internally and will have a response to you as soon as possible.
As most of our art assets are unlicensed (all rights reserved), if we agree to this then I'll get the other artists to comment. Otherwise, the source code is open source and the MIT license explicitly allows for this, so it is only the art authorisation that you would need.

Just an update, I have explicit permission from two out of the three major artists, and I'll get back to you as soon as possible when I hear from the third.


bug: panic nil pointer

Description:
Added following configuration and envoy-gateway is in crashloop and panicing:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: mattermost-test
  namespace: mattermost-test
spec:
  parentRefs:
  - name: internal
    namespace: envoy-gateway-system
    sectionName: https
  hostnames:
  - mattermost.foobar.com
  rules:
  - backendRefs:
    - group: ""
      kind: Service
      name: mattermost-test
      port: 8080
      weight: 1
    matches:
    - path:
        type: PathPrefix
        value: /
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: BackendTLSPolicy
metadata:
  name: mattermost-test
  namespace: mattermost-test
spec:
  targetRef:
    group: ''
    kind: Service
    name: mattermost-test
  tls:
    wellKnownCACerts: System
    hostname: mattermost.foobar.com

If I add sectionName to targetRef it starts to work. However, sectionName is marked as optional in official api spec. https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2Fv1alpha2.PolicyTargetReference https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io%2Fv1alpha2.PolicyTargetReferenceWithSectionName
Environment:
eg 1.0.0
Logs:
2024-03-18T07:13:08.064Z        INFO    provider        status/status.go:122    received a status update        {"runner": "provider", "namespace": "mattermost-test", "name": "mattermost-test"}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0x1eab74d]

goroutine 121 [running]:
github.com/envoyproxy/gateway/internal/xds/translator.findXdsSecret(...)
        /home/runner/work/gateway/gateway/internal/xds/translator/translator.go:546
github.com/envoyproxy/gateway/internal/xds/translator.addXdsSecret(0xc000d9b540, 0xc002d03630)
        /home/runner/work/gateway/gateway/internal/xds/translator/translator.go:556 +0x12d
github.com/envoyproxy/gateway/internal/xds/translator.createOAuth2Secrets(0xc000d9b540?, {0xc002a8ad60, 0x4, 0x0?})
        /home/runner/work/gateway/gateway/internal/xds/translator/oidc.go:277 +0x8f
github.com/envoyproxy/gateway/internal/xds/translator.(*oidc).patchResources(0x0, 0xc000d9b540?, {0xc002a8ad60, 0x4, 0x4})
        /home/runner/work/gateway/gateway/internal/xds/translator/oidc.go:190 +0x51
github.com/envoyproxy/gateway/internal/xds/translator.patchResources(...)
        /home/runner/work/gateway/gateway/internal/xds/translator/httpfilters.go:214
github.com/envoyproxy/gateway/internal/xds/translator.(*Translator).processHTTPListenerXdsTranslation(0xc002613110, 0xc000d9b540, {0xc002b6a2d0, 0x3, 0x2583ede?}, 0x25818a1?, 0x1?, 0x0)
        /home/runner/work/gateway/gateway/internal/xds/translator/translator.go:301 +0x12b2
github.com/envoyproxy/gateway/internal/xds/translator.(*Translator).Translate(0xc002613110, 0xc002a94a00)
        /home/runner/work/gateway/gateway/internal/xds/translator/translator.go:81 +0x7f
github.com/envoyproxy/gateway/internal/xds/translator/runner.(*Runner).subscribeAndTranslate.func1({{0xc0008717a0?, 0x0?}, 0x40?, 0xc002a94a00?}, 0xa?)
        /home/runner/work/gateway/gateway/internal/xds/translator/runner/runner.go:81 +0x24d
github.com/envoyproxy/gateway/internal/message.HandleSubscription[...]({{0x257bef6, 0x28e9be8?}, {0x256efa7?, 0x0?}}, 0xc00007e7e0?, 0xc002613f98)
        /home/runner/work/gateway/gateway/internal/message/watchutil.go:76 +0x112d
github.com/envoyproxy/gateway/internal/xds/translator/runner.(*Runner).subscribeAndTranslate(0xc0002c2680, {0x28e9be8?, 0xc000820280?})
        /home/runner/work/gateway/gateway/internal/xds/translator/runner/runner.go:53 +0x85
created by github.com/envoyproxy/gateway/internal/xds/translator/runner.(*Runner).Start in goroutine 1
        /home/runner/work/gateway/gateway/internal/xds/translator/runner/runner.go:46 +0x26e

Seeing the panic logs, it looks like not really related to targetRef, more related to:
// findXdsSecret finds a xds secret with the same name, and returns nil if there is no match.
func findXdsSecret(tCtx *types.ResourceVersionTable, name string) *tlsv3.Secret {
	if tCtx == nil || tCtx.XdsResources == nil || tCtx.XdsResources[resourcev3.SecretType] == nil {
		return nil
	}

	for _, r := range tCtx.XdsResources[resourcev3.SecretType] {
		secret := r.(*tlsv3.Secret)
		if secret.Name == name {
			return secret
		}
	}

	return nil
}

cc @zhaohuabing

Looks like an nil secret has been added to the XdsResources.


Trouble accessing raspotify config

Due Diligence

[X] I have done my due diligence

What can we help you with?
Hi,
I installed  raspotify as described on this githup page (the easy method). Raspotify is discovered by my mobile device. But I have no sound. I seem to remeber that I have to make changes to the raspotify config file. But when i go to the location /etc/raspotify I get the message that access is denied.
What am i doing wrong?
Tnx in advance,
Sjak

You must have root/admin privileges to see the content of the folder.


Error on whereHasMorph when morph columns are null

Laravel Version: 5.8.35
PHP Version: 7.2.25
Database Driver & Version: MySQL 5.5.5-10.4.10-MariaDB

Description:
Using whereHasMorph and orWhereHasMorph when there are records with null values on morph columns, laravel produces this error:
Error: Class name must be a valid object or a string

/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php:808
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasRelationships.php:183
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php:248
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Relations/Relation.php:90
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php:250
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php:220
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:226
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:250
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php:230
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:226
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php:232
/Users/dignity/codes/query-filter/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/QueriesRelationships.php:324

Steps To Reproduce:
For simplicity's sake, let's use the comments example we have on docs:
https://laravel.com/docs/5.8/eloquent-relationships#one-to-many-polymorphic-relations

Setup a couple comment records in the database. Let couple of records be null on commentable_type and commentable_id columns.
Run the whereHasMorph query against comments like so:

$comments = App\Comment::whereHasMorph('commentable', '*', function (Builder $query) {
    $query->where('title', 'like', 'foo%');
})->get();

Hey there,
Unfortunately we don't support this version anymore. Please check out our support policy on which versions we are currently supporting. Can you please try to upgrade to the latest version and see if your problem persists? We'll help you out and re-open this issue if so.
Thanks!


test_data_structure fails with 1.4.8

This commit https://github.com/crownstone/crownstone-lib-python-cloud/commit/d533d0e6b80663bc5904d0c620c9d77498abe1d1 breaks tests/test_cloud.py::TestCrownstoneCloud::test_data_structure.
___________________ TestCrownstoneCloud.test_data_structure ____________________

self = <tests.test_cloud.TestCrownstoneCloud testMethod=test_data_structure>
mock_request = <CoroutineMock name='get' id='140737317531600'>

    @asynctest.patch.object(RequestHandler, 'get')
    async def test_data_structure(self, mock_request):
        cloud = CrownstoneCloud('email', 'password')
        # create fake instance
        cloud.spheres = Spheres(cloud, 'user_id')
        # add fake sphere data for test
        mock_request.return_value = sphere_data
        await cloud.spheres.async_update_sphere_data()
        mock_request.assert_awaited()

        # test getting a sphere by id and name
        sphere = cloud.spheres.find_by_id('my_awesome_sphere_id_2')
        assert sphere.cloud_id == 'my_awesome_sphere_id_2'
        sphere2 = cloud.spheres.find('my_awesome_sphere')
        assert sphere2.name == 'my_awesome_sphere'

        # test getting keys
        mock_request.return_value = key_data
        keys = await sphere.async_get_keys()
        mock_request.assert_awaited()
        assert keys == expected_key_data

        # add fake crownstone data for test
        mock_request.return_value = crownstone_data
        await sphere.crownstones.async_update_crownstone_data()
        mock_request.assert_awaited()

        # add fake user data for test
        mock_request.return_value = user_data
        await sphere.users.async_update_user_data()
        mock_request.assert_awaited()

        # add fake location data for test
        mock_request.return_value = location_data_init
        await sphere.locations.async_update_location_data()
        mock_request.assert_awaited()
        # presence
        mock_request.return_value = presence_data
        await sphere.locations.async_update_location_presence()
        mock_request.assert_awaited()

        # test getting crownstone, users, locations by id & name
        crownstone = sphere.crownstones.find('my_awesome_crownstone')
        crownstone_by_id = sphere.crownstones.find_by_id('my_awesome_crownstone_id_2')
        assert crownstone.name == 'my_awesome_crownstone'
        assert crownstone_by_id.cloud_id == 'my_awesome_crownstone_id_2'
        # test state
        assert crownstone.state == 0
        # test abilities
        assert crownstone_by_id.abilities.get(DIMMING_ABILITY).is_enabled is False
        assert crownstone_by_id.abilities.get(TAP_TO_TOGGLE_ABILITY).is_enabled is True

        location = sphere.locations.find('my_awesome_location_1')
        location_by_id = sphere.locations.find_by_id('my_awesome_location_id_3')
        assert location.cloud_id == 'my_awesome_location_id_1'
        assert location_by_id.name == 'my_awesome_location_3'
        # test presence
        assert len(location.present_people) == 1
        assert len(location_by_id.present_people) == 1

        mock_request.return_value = location_data_removed
        await sphere.locations.async_update_location_data()
        # test when the location data gets updated
>       assert 'my_awesome_location_id_2' not in sphere.locations.locations
E       AttributeError: 'Locations' object has no attribute 'locations'

tests/test_cloud.py:114: AttributeError

Thanks, fixed in 1.4.9.


React 15.2 Unknown props

With React 15.2+ I'm seeing the unknown props warning in react-stonecutter:
Warning: Unknown props columnWidth, gutterWidth, gutterHeight, minPadding, columns, angleUnit, layout, enter, entered, exit on  tag. Remove these props from the element. For details, see https://fb.me/react-unknown-prop
and
Warning: Unknown prop itemRect on  tag. Remove this prop from the element.
Not sure what FB changed but lots of libs are tossing these warnings now. Stonecutter still works, but just wanted to give you a heads up.

Hi, thanks a lot for the heads up. I've created a new release that should fix this.


Doesn't de-cluster at max zoom, instead cluster vanishes

I tried both MarkerClusterer and MarkerClustererPlus and when I have two markers very close to one another problems arise.   I keep clicking the cluster until I reach the max zoom.  The next click will either do nothing to the cluster icon or remove it and not show the individual markers.  I noticed the Leaflet clusterer de-clusters the markers and has lines that lead to their point of origin, so they decluster cleanly.
Any chance of fixing this to at least de-cluster at max zoom?  I tried implementing it myself, but I don't know how this is achieved since each tile potentially has it's own maxZoom.  The library's maxZoom property doesn't help at all when switching from Maps to Satelite, which has a much lower maxZoom in general.  I found the API's "maxZoomService" but I don't want to query that for every action to see if I need de-cluster.  Ideas would be greatly appreciated.

+1

I have seen this too. Workaround is to just set google maps max zoom on the map object.

+1

+1

This issue, along with many other limitations in the Google Maps API that required workarounds forced our business away from Google Maps.  We're now using Leaflet and Mapbox with great success.

This issue, along with many other limitations in the Google Maps API requiring workarounds forced our business away from Google Maps. We're now using Leaflet and Mapbox with great success.

:+1:

+1

+1

+2


GH - 624: Added dtype arg to read_sql

[ ] xref #624
[ ] Tests added: Please use assert_type() to assert the type of any return value

Can you do 2 things here:

Another change to read_sql() is the introduction of the dtype_backend argument, so can you make that change in this PR?
Can you modify your test to use a different argument for dtype, e.g.,  something likedtype={"int_column": float} should work.  You may need to experiment with that.

Done


Update the game over message

Update the game over message so people know how to play again!

technical onboarding

technical process

nmkl

yuiuuhg


Should the events be firing when using native controls?

Hi,
Great plugin. We are using it in a nativescript-vue project, and it seems that the only event that actually fires (on iOS, we are still getting this up and running on Android) is the playbackReady event. The others only seem to fire if you call their respective api methods directly. We need to detect when the user scrubs, pauses or plays using the native controls. Should we expect these events to be firing, or is that not the intended behaviour?
Thanks!

Think it was just an oversight, lack of time to getting implemented. I'd have to review iOS again to get the events emitting with the media controls. Not something I have planned right now, so a PR would be much appreciated if you have any time/desire to help with that.
On android I think I have most of the events wired up, if you find something lacking, let me know as I'm more familiar with the android API and can help you get that working much quicker.
Thanks 😄

Ok that's interesting to know you are ok with that, we'll have a look! Would you be against us changing the status observer in the PR to be more wide ranging in what it observes to try and catch these events?

Not at all, any improvements are welcome 🎉 just keep me posted if you have something breaking in mind so we can at least discuss it and possibly mitigate any major bumps, unless it's completely unavoidable.

will do, thanks!


Dynamic form handling

How do i handle a dynamically expanded form.
Example:

Each block is an entry form with a remove button (via jquery).
The add button adds a new button (via jquery).
The submit button sends a POST request to my flask application.

How do I handle the resulting data posted to flask? How do I model this as a Form class?
Ref: http://www.sanwebe.com/2013/03/addremove-input-fields-dynamically-with-jquery

This isn't a support forum, this is for bug posts
Ask somewhere like http://stackoverflow.com


par_chunks might use the main thread

I noticed that par_chunks() does not spawn new threads when chunk_size is the same or smaller than the amount of elements. Rayon will just use the main thread.
This is probably a good performance optimization, however it can lead to surprises when expectations are made about the threads like the stack size.
I need a lot of stack size for my application so I set this:
rayon::ThreadPoolBuilder::new()
    .stack_size(16 * 1024 * 1024)
    .build_global()
    .unwrap();

However, the use of the main thread led to a stack overflow because the stack of the main thread is too small.
Maybe this behavior should at least be documented in the API to avoid surprises?

This is not specific to par_chunks, but yes. If a parallel iterator only has one item, it won't have anything to split in parallel, so it ends up running directly. In your case, the one item may in turn be a chunk of many items, but that still can't be split up. We debated about that a little, but IIRC Niko argued for that optimization.
As a workaround, you can use pool.install(|| { ... }) to make sure your code is running within the threadpool to start with.
If you'd like to propose documentation changes, that would be great -- but make sure it's in a general place, not just par_chunks.

I would argue that this single-item use case is really a non use case for rayon. If somebody uses rayon, a single-item is  probably always the exception and therefore not something rayon really needs to optimize for. Especially not if that optimization introduces surprising behavior at the cost of one context switch for a non use case.
Even if documented well, everyone has to do the mental gymnastics to understand what this really means for their application. E.g. If we talk about stack size this is something really difficult to estimate in case of recursive functions. Now people have to really understand what could go wrong and people will fail at this for sure which may crash their applications only in some very specific case. People will also forget to test this edge case. Debugging is also not fun because you have to reproduce the single-item case and then look at the thread ids to see what happens.
Is that optimization to avoid one context switch really worth it?
I would strongly really argue for a more resilient design, since that is also the spirit of rust.
If you want to keep this optimization why not make it optional as configuration on the thread pool or something? So people that obsess with performance can read and hopefully understand this optimization. For all other this optimization is not really needed anyway.

Not that this optimization is not about one item but one task, e.g. 10.000 items with with_min_len(10_000) will still split into only a single task.
I also think one use case that is rather relevant is that an Rayon-using application can be trimmed to avoid thread pool usage and the associated costs by setting the environment variable RAYON_NUM_THREADS=1.

If a user explicitly uses RAYON_NUM_THREADS=1 she hopefully knows what that means for the application. I only argue against making the optimization the default behavior. Having the optimization optional or forced by environment variables is totally fine I think.

I think this may be another problem that this optimization causes. I am trying to spawn an tokio runtime from within a parallel iterator. This works in most cases because it is running on a separate thread, but crashes as soon as it tries to execute on the main thread because there is already a runtime running on the main thread.
here is a simplified example of my code:
let items = Vec<Items>;
items.par_iter().for_each(|items| {
    let events = items.events.clone();
    let event_handles = Vec::new();
    for event in events {
         event_handles.push(thread::spawn(async move || {
              process_event(event).await.unwrap()
         }
    }
    let rt = tokio::runtime::Runtime::new();
    let handles = event_handles.drain(..).map(|h| h.join.unwrap()).collect::<Vec<_>>();
    rt.block_on(futures::future::join_all(handles));
    drop(rt);
});

This code will run for hours with 40,000+ items but will fail with 6 items to go. It took me forever to diagnose the problem, I still haven't found a solution.

I still haven't found a solution.

Couldn't you wrap your top-level par_iter().for_each(..) into spawn to move it into the thread pool while keeping the main thread free?
Alternative, if you do not need a freshly initialized runtime for each item, you might just put them into thread local variables.
I would also suggest using current_thread flavour of the runtime to avoid each runtime spawning a thread pool of its own which I guess will lead to over-subscription.

@adamreichold I'll give those a go. I just found this issue after finally discovering what the issue was (my misunderstanding of the fact that Rayon will fall back to the main thread in certain scenarios). You've at least pointed me in some promising directions, better than what I was thinking about implementing haha.


Error in this file

Error after selecting table at https://www.dmr.nd.gov/ndgs/documents/Publication_List/pdf/geoinv/GI_144.pdf.
Filename: GI_144.pdf
Internal Server Error (500)

        Request Method:
        POST

        Request URL:
        http://<IP_ADDRESS>:8080/pdf/e115b801bf888f36ff70737de9b3f7757945d6ed/data

Comparison method violates its general contract!

Works if I crop the table out into a smaller PDF.


⚠ Everything Hub has degraded performance

In eebb296, Everything Hub (https://hub.everythingbagel.me) experienced degraded performance:

HTTP code: 200
Response time: 3190 ms

Resolved: Everything Hub performance has improved in 6c4d0ba after 14 minutes.


 You can do:
$model->getOriginal()['column']


Customer Addressable Audience instead of Addressable Audience Match Rate

Issue in ./help/using/features/addressable-audiences.md
I think the definition name is changed here. PFA screen shot

Thanks for raising this! We've updated the definition name to reflect the diagram.